Perform visual regression test with a nice GUI as help. π OnlyΒ forΒ Cypress! Both e2e and component-testing compatible πͺ
Getting Started
Β·
Usage
Β·
FAQ
Β·
File an Issue
Β·
Have a question or an idea?
Plugin for visual regression testing that provides smooth experience:
Specify threshold below which the test will fail.
Quickly preview old & new screenshot directly in the Cypress UI.
Find visual changes using images diff.
Published as treeshakeable bundles, separate for JS ES5 or modern bundlers thanks to microbundle.
Working with every bundler (tested on webpack, vite, rollup),
Provides proper typings as is written completely in typescript.
You can install this library using your favorite package manager:
# npm
npm install --save-dev @frsource/cypress-plugin-visual-regression-diff
# yarn
yarn add -D @frsource/cypress-plugin-visual-regression-diff
# pnpm
pnpm add -D @frsource/cypress-plugin-visual-regression-diffNext, you need to import the library:
- first, in your support file (located by default in
cypress/support/index.js):
// typescript / ES6
import '@frsource/cypress-plugin-visual-regression-diff';
// javascript
require('@frsource/cypress-plugin-visual-regression-diff');- secondly:
- (for Cypress 10.0+) in
cypress.config.js(orcypress.config.ts):
- (for Cypress 10.0+) in
// typescript / ES6
import { defineConfig } from 'cypress';
import { initPlugin as initVisualRegressionPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
export default defineConfig({
// initPlugin must be called in the section where it is used: e2e or component
e2e: {
setupNodeEvents(on, config) {
initVisualRegressionPlugin(on, config);
},
},
component: {
setupNodeEvents(on, config) {
initVisualRegressionPlugin(on, config);
},
},
});- (for Cypress <10.0) in your plugins file (located by default in
cypress/plugins/index.js):
// typescript / ES6
import { initPlugin as initVisualRegressionPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
export default function (
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions,
) {
initVisualRegressionPlugin(on, config);
return config;
}
// javascript
const {
initPlugin: initVisualRegressionPlugin,
} = require('@frsource/cypress-plugin-visual-regression-diff/plugins');
module.exports = function (on, config) {
initVisualRegressionPlugin(on, config);
return config;
};That's it - now let's see how to use the library in usage section.
Once installed, the library might be used by writing in your test:
cy.get('.an-element-of-your-choice').matchImage();Or, if you would like to make a screenshot of whole document:
cy.matchImage();matchImage command will do a screenshot and compare it with image from a previous run. In case of regression the test will fail and you'll get a "See comparison" button to see what's a root of a problem.
Still got troubles with installation? Have a look at examples directory of this repo to see how this plugin can be used in e2e or component-testing Cypress environment within your project.
It's useful to remove screenshots generated by the visual regression plugin that are not used by any test anymore. Enable this feature via env variable and enjoy freed up storage space π:
npx cypress run --env "pluginVisualRegressionCleanupUnusedImages=true"Configure the plugin:
- by passing in configuration as an argument to
matchImagecommand:
cy.matchImage({
// screenshot configuration, passed directly to the the Cypress screenshot method: https://docs.cypress.io/api/cypress-api/screenshot-api#Arguments
// default: { }
screenshotConfig: {
blackout: ['.element-to-be-blackouted'],
},
// pixelmatch options, see: https://www.npmjs.com/package/pixelmatch#pixelmatchimg1-img2-output-width-height-options
// default: { includeAA: true }
diffConfig: {
threshold: 0.01,
},
// whether to create missing baseline images automatically
// default: true
createMissingImages: false,
// whether to update images automatically - useful for CI
// - true: overwrite all baseline images without comparing (fastest)
// - 'failures-only': compare first, update baseline only when diff exceeds threshold
// - false: never update baseline images automatically
// default: false
updateImages: true,
// directory path in which screenshot images will be stored
// relative path are resolved against project root
// absolute paths (both on unix and windows OS) supported
// path separators will be normalised by the plugin depending on OS, you should always use / as path separator, e.g.: C:/my-directory/nested for windows-like drive notation
// There are one special variable available to be used in the path:
// - {spec_path} - relative path leading from project root to the current spec file directory (e.g. `/src/components/my-tested-component`)
// default: '{spec_path}/__image_snapshots__'
imagesPath: 'this-might-be-your-custom/maybe-nested-directory',
// maximum threshold above which the test should fail
// default: 0.01
maxDiffThreshold: 0.1,
// forces scale factor to be set as value "1"
// helps with screenshots being scaled 2x on high-density screens like Mac Retina
// default: true
forceDeviceScaleFactor: false,
// title used for naming the image file
// default: Cypress.currentTest.titlePath (your test title)
title: `${Cypress.currentTest.titlePath.join(' ')} (${Cypress.browser.displayName})`,
// pass a path to custom image that should be used for comparison
// instead of checking against the image from previous run
// default: undefined
matchAgainstPath: '/path/to/reference-image.png',
});- via global env configuration. Environment variable names are the same as keys of the configuration object above, but with added
pluginVisualRegressionprefix, e.g.:
npx cypress run --env "pluginVisualRegressionUpdateImages=true,pluginVisualRegressionDiffConfig={\"threshold\":0.01}"// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
env: {
pluginVisualRegressionUpdateImages: true,
pluginVisualRegressionDiffConfig: { threshold: 0.01 },
},
});// cypress.env.json (https://docs.cypress.io/guides/guides/environment-variables#Option-2-cypress-env-json)
{
"pluginVisualRegressionUpdateImages": true,
"pluginVisualRegressionDiffConfig": { "threshold": 0.01 }
}For more ways of setting environment variables take a look here.
Why screenshots doesn't conform to the `viewport` set in my Cypress configuration?
Screenshots in Cypress do not scale to the viewport size by default. You can change this behavior:
- globally, by changing default screenshot configuration:
Cypress.Screenshot.defaults({ capture: 'viewport' }); - locally, by passing screenshot configuration directly to the
.matchImagecommand:cy.matchImage({ screenshotConfig: { capture: 'viewport' } });
I've upgraded version of this plugin and all on my baseline images has been automatically updated. Why?
Sometimes we need to do a breaking change in image comparison or image generation algorithms. To provide you with the easiest upgrade path - the plugin updates your baseline images automatically. Just commit them to your repository after the plugin upgrade and you are good to go!
Screenshots look different between cypress run and cypress open. How do I fix it?
This is typically caused by device pixel ratio differences between headless and headed modes. Use the forceDeviceScaleFactor option to normalize the scale factor to 1:
cy.matchImage({ forceDeviceScaleFactor: true });Or set it globally via environment variable:
npx cypress run --env "pluginVisualRegressionForceDeviceScaleFactor=true"For persistent viewport size differences, consider setting the browser window size explicitly in setupNodeEvents:
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome' && browser.isHeadless) {
launchOptions.args.push('--window-size=1280,720');
}
return launchOptions;
});How do I use this plugin alongside other Cypress plugins that also register setupNodeEvents events?
Cypress only supports a single handler per event. If multiple plugins register the same event (e.g. after:screenshot), only the last one will be called. To compose multiple plugins safely, use cypress-on-fix:
import { defineConfig } from 'cypress';
import { initPlugin as initVisualRegressionPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
import fix from 'cypress-on-fix';
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
const fixedOn = fix(on);
initVisualRegressionPlugin(fixedOn, config);
// register other plugins using fixedOn here
},
},
});How do I integrate with mochawesome or other reporters that embed screenshot images?
The plugin stores baseline images outside the default cypress/screenshots/ directory. Some reporters (like cypress-mochawesome-reporter) expect screenshots to be in that folder.
Use the processImgPath task hook to control where the screenshot file is placed. Override it in setupNodeEvents after calling initPlugin:
setupNodeEvents(on, config) {
initVisualRegressionPlugin(on, config);
// Override the path processor to keep screenshots accessible to the reporter
on('task', {
'cp-visual-regression-diff-processImgPath': ({ path }: { path: string }) => {
// return a new path, or the same path to keep default behavior
return path;
},
});
}How to fail on any visual difference, no matter how small?
Set maxDiffThreshold to 0:
cy.matchImage({ maxDiffThreshold: 0 });Or globally via an environment variable:
npx cypress run --env "pluginVisualRegressionMaxDiffThreshold=0"Why is the actual screenshot deleted after a successful image comparison?
When images match, the .actual.png file is temporary and gets cleaned up after the comparison. Only the baseline image (no suffix) is kept. This avoids storing redundant files.
If you need actual screenshots to stay accessible for a reporter (e.g. cypress-mochawesome-reporter), use the processImgPath task hook to control where screenshots are stored. See How do I integrate with mochawesome or other reporters that embed screenshot images?
How to remove spaces from screenshot filenames?
Screenshot filenames are derived from Cypress test titles, which may contain spaces. To use a filename without spaces, pass a custom title option to each matchImage call:
cy.matchImage({ title: 'my-screenshot-without-spaces' });To apply this globally for all matchImage calls, override the command in your support file (cypress/support/commands.ts):
Cypress.Commands.overwrite('matchImage', (originalFn, subject, options = {}) =>
originalFn(subject, {
title: Cypress.currentTest.titlePath.join(' ').replace(/\s+/g, '-'),
...options,
}),
);How to include the browser name in image filenames (for cross-browser testing)?
Different browsers may render fonts and elements slightly differently. To keep separate baseline images per browser, override matchImage in your support file (cypress/support/commands.ts) to set a browser-specific imagesPath:
Cypress.Commands.overwrite('matchImage', (originalFn, subject, options = {}) =>
originalFn(subject, {
imagesPath: `{spec_path}/__image_snapshots__/${Cypress.browser.name}`,
...options,
}),
);This creates separate image directories per browser (e.g. __image_snapshots__/chrome/, __image_snapshots__/firefox/).
How to generate an HTML report showing baseline, diff, and actual images?
The plugin provides a built-in visual comparison overlay in the Cypress UI β clicking "See comparison" on a failed test shows the baseline, diff, and actual images side by side.
For CI or sharable HTML reports, integrate with a reporter such as cypress-mochawesome-reporter. See How do I integrate with mochawesome or other reporters that embed screenshot images? for setup details.
Donβt hesitate to ask a question directly on the discussions board!
Changes for every release are documented in the release notes and CHANGELOG files of every package.
Copyright (c) 2021-present, Jakub FRS Freisler, FRSOURCE
