Cypress Image Snapshot binds jest-image-snapshot's image diffing logic to Cypress.io commands.
- Installation
- Usage
- How it works
- Requirements
- Contributing
- Forked from
jaredpalmer/cypress-image-snapshot
Install with your chosen package manager
# yarn
yarn add --dev @simonsmith/cypress-image-snapshot
# npm
npm install --save-dev @simonsmith/cypress-image-snapshot
Next, import the plugin function and add it to the setupNodeEvents
function:
// cypress.config.ts
import {defineConfig} from 'cypress'
import {addMatchImageSnapshotPlugin} from '@simonsmith/cypress-image-snapshot/plugin'
export default defineConfig({
e2e: {
setupNodeEvents(on) {
addMatchImageSnapshotPlugin(on)
},
},
})
Add the command to your relevant support file:
// cypress/support/e2e.ts
import {addMatchImageSnapshotCommand} from '@simonsmith/cypress-image-snapshot/command'
addMatchImageSnapshotCommand()
// can also add any default options to be used
// by all instances of `matchImageSnapshot`
addMatchImageSnapshotCommand({
failureThreshold: 0.2,
})
TypeScript is supported so any reference to @types/cypress-image-snapshot
can be removed from your project
Ensure that the types are included in your tsconfig.json
{
"compilerOptions": {
// ...
},
"types": ["@simonsmith/cypress-image-snapshot/types"]
}
describe('Login', () => {
it('should be publicly accessible', () => {
cy.visit('/login');
// snapshot name will be the test title
cy.matchImageSnapshot();
// snapshot name will be the name passed in
cy.matchImageSnapshot('login');
// snapshot will be created inside `some/dir`
cy.matchImageSnapshot('some/dir/image')
// options object passed in
cy.matchImageSnapshot({
failureThreshold: 0.4
blur: 10
});
// match element snapshot
cy.get('#login').matchImageSnapshot();
});
});
The options object combines jest-image-snapshot and Cypress screenshot configuration.
cy.matchImageSnapshot({
// options for jest-image-snapshot
failureThreshold: 0.2,
comparisonMethod: 'ssim',
// options for Cypress.screenshot()
capture: 'viewport',
blackout: ['.some-element'],
})
It is also possible to configure the extensions given to snap and diff files generated by the plugin. The default options are:
{
snapFilenameExtension: '.snap',
diffFilenameExtension: '.diff',
}
// will create a snap called `some-name.custom-snap-name.png`
cy.matchImageSnapshot('some-name', {
snapFilenameExtension: '.custom-snap-name',
})
// will create a snap called `some-name.png`
cy.matchImageSnapshot('some-name', {
snapFilenameExtension: '',
})
// will create a diff called `some-name.wrong.png` when a test fails
cy.matchImageSnapshot('some-name', {
diffFilenameExtension: '.wrong',
})
As of Cypress 10.0.0 a change was made to remove common ancestor paths of
generated screenshots. This means that it is difficult to mimic the folder
structure found in the cypress/e2e/
directory when creating the snapshots
directory.
To workaround this, cypress-image-snapshot makes use of a e2eSpecDir
option. Here's an example:
addMatchImageSnapshotCommand({
e2eSpecDir: 'cypress/e2e/', // the default value
})
Example output in a project:
cypress
├── e2e
│ ├── matchImageSnapshot.cy.ts
│ ├── nested
│ │ └── test
│ └── someOtherTest.cy.ts
├── snapshots
│ ├── matchImageSnapshot.cy.ts
│ │ ├── matches with just options.snap.png
│ │ ├── name and options.snap.png
│ │ ├── no arguments.snap.png
│ │ └── with custom name.snap.png
│ ├── nested
│ │ └── test
│ └── someOtherTest.cy.ts
│ └── some other test taking a snapshot.snap.png
Without the e2eSpecDir
option the cypress/e2e/
directories would be
repeated inside the snapshots
directory. Set this option to whatever
directory structure is inside the specPattern
configuration value.
See more:
Run Cypress with --env updateSnapshots=true
in order to update the base image files for all of your tests.
By default tests will fail when a snapshot fails to match. Run Cypress with
--env failOnSnapshotDiff=false
in order to prevent test failures when an image
diff does not pass.
Run Cypress with --env requireSnapshots=true
in order to fail if snapshots are missing. This is useful in continuous integration where snapshots should be present in advance.
The workflow of cy.matchImageSnapshot()
when running Cypress is:
- Take a screenshot with
cy.screenshot()
named according to the current test. - Check if a saved snapshot exists in
<rootDir>/cypress/snapshots
and if so diff against that snapshot. - If there is a resulting diff, save it to
<rootDir>/cypress/snapshots/__diff_output__
.
Tested on Cypress 10.x, 11.x and 12.x
Cypress must be installed as a peer dependency
- Clone the repository and install the yarn dependencies with
yarn install
- Ensure that Docker is setup. This is necessary for generating/updating snapshots
- Using Volta is recommended for managing Node and Yarn versions. These are
automatically picked up from the
package.json
- Commits should be based on conventional-changelog
To make it easier to test whilst developing there are a few simple Cypress tests that validate the plugin. There are two ways to run these tests:
yarn test:open
In open mode the tests run in Electron and ignore any snapshot failures. This is due to the rendering differences on developer machines vs CI. Here there is also verbose output sent to the test runner console to aid debugging.
Note here that the yarn script above will re-build the plugin each time. This is
necessary because the tests are run against the output in the dist
directory
to ensure parity between the built package on NPM.
Ensure that the command is run each time changes need to be tested in Cypress
yarn docker:build
yarn docker:run
The commands here ensure that the tests are run inside a Docker container that matches the CI machine. This allows images to be generated and matched correctly when running the tests in Github Actions.
It is necessary to have two environment variables defined by default before running the tests in Docker:
CYPRESS_updateSnapshots=false
CYPRESS_debugSnapshots=false
It's recommended that these are loaded into the shell with something like direnv
Then they can be overridden as needed:
CYPRESS_updateSnapshots=true yarn docker:run
This is a rewrite of the original plugin as active development has ceased. Full credit goes to Jared Palmer.