Cypress: Proposal - cy.screenshot enhancements

Created on 6 Mar 2018  路  9Comments  路  Source: cypress-io/cypress

Make cy.screenshot() more useful to go down the path towards screenshot diffing.

Let's create a new set of API's to make it easier to take a picture of the AUT (application under test) without involving the Cypress UI.

To make this happen, we'll also need to give the user the option to remove the artificial "scaling" Cypress applies, and we'll need to black out all of the excess areas.

Bonus points for also going down the route of slicing out the "blacked out" areas of the picture so the resulting screenshot has no excess - and is insulated from resolution differences.

This also leads us towards being able to take pictures of just dom elements instead of the whole window.

Cypress.Screenshot.defaults({
  capture: 'commands | app | both', // default 'commands'
  waitForCommandSynchronization: true, // when taking a picture of the commands, wait until they are synchronized
  scaleAppCaptures: false, // when taking a picture of the app, remove the scaling
  disableTimersAndAnimations: true, // prevent app animations and timers from firing
  screenshotOnRunFailure: true, // config.screenshotOnRunFailure
  blackout: ['selectors'], // whatever matches, we'll blackout their box model
  onScreenshot: ($dom) => {

  },
})

To disable animations - we need to keep references for setTimeout, setInterval, requestAnimationFrame during the initial phase of event binding to the window - and then prevent callbacks from firing until the screenshot has been taken.

onScreenshot would yield you the $elements that are being screenshotted - enabling the user to synchronously perform changes on it (like changing dynamic content).

blackout allows the user to easily pick selectors which we'd use to blackout their box model. This would utilize the logic for layering hitboxes (except layer only a blacked out piece)

Epic

Most helpful comment

Released in 3.0.0.

All 9 comments

To disable CSS animations insert this <style> into the document:

*, *:before, *:after {
  transition-property: none !important;
  transform: none !important;
  animation: none !important;
}

After screenshot perform all the cleanup - remove the CSS, blackouts, and revert timers.

After some discussion, changing capture to be an array:

Cypress.Screenshot.defaults({
  capture: ['app', 'everything']
})

Still need a decent name for 'everything'. Ideas:

  • screen
  • all
  • ui
  • app+ui
  • viewport

Also include 'app-scaled' as an option? Or, another idea is to accept an object instead of a string for each item (maybe called it screenshots instead of capture?):

Cypress.Screenshot.defaults({
  screenshots: [
    {
      capture: 'app',
      scale: false
    },
    {
      capture: 'everything',
      scale: true
    }
  ]
})

Perhaps the user could still use the string 'app' or 'everything' and it defaults to scale: true? Then they only need the object if they're turning off scaling.

@brian-mann, @jennifer-shehane, @bahmutov, thoughts?

Latest API:

Cypress.Screenshot.defaults({
  capture: 'app', // or 'runner'
  waitForCommandSynchronization: true,
  scaleAppCaptures: false,
  disableTimersAndAnimations: true,
  screenshotOnRunFailure: true,
  blackout: ['selectors'],
  beforeScreenshot: (document) => {},
  afterScreenshot: (document) => {},
})

By default, taking 'app' captures with cy.screenshot(). Only take 'runner' captures on failures. Don't black out anything on 'runner' captures.

Some additions:

Allow capturing full page

Add 'fullpage' option for capture.

cy.screenshot({
  capture: 'fullpage'
})

Implement it by scrolling to the top, then taking a screenshot, scrolling, taking a screenshot, and so on, then stitching them together.

When we implement native events, we'll be able to use Chrome's debugger protocol to accomplish this in a better, more succinct way. Its implementation utilizes device emulation overrides. See puppeteer's implementation.

Add element capture support

Make cy.screenshot() a dual command, so you can do:

cy.get('.foo').screenshot()

Implement as follows:

  1. Scroll element into view
  2. Take screenshot, crop out element
  3. Scroll if needed and stitch together

Number 3 will be unnecessary for Chrome once implemented with debugger protocol. See puppeteer's implementation.

Some more changes to this API before it's released:

  • Removing waitForCommandSynchronization option. Will always be true for runner captures and false for non-runner captures
  • Changing capture: 'app' to capture: 'viewport'
  • Changing scaleAppCaptures: true to scale: true

Released in 3.0.0.

Can Screenshot give us DOM which can be stored in JSON format the way snapshot https://github.com/cypress-io/snapshot gives?

@ModiYesha No.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weskor picture weskor  路  3Comments

carloscheddar picture carloscheddar  路  3Comments

rbung picture rbung  路  3Comments

verheyenkoen picture verheyenkoen  路  3Comments

EirikBirkeland picture EirikBirkeland  路  3Comments