Test Runner Visual Regression
Plugin for visual regression testing with Web Test Runner.
This project is experimental. We are testing out different approaches and gathering feedback, let us know what you think and join the discussion!.
Usage
Install the package:
npm i --save-dev @web/test-runner-visual-regression
Add the plugin to you web-test-runner.config.mjs
:
import { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin';
export default {
plugins: [
visualRegressionPlugin({
update: process.argv.includes('--update-visual-baseline'),
}),
],
};
Run a visual diff in your browser test:
import { visualDiff } from '@web/test-runner-visual-regression';
it('can diff an element', async () => {
const element = document.createElement('p');
element.textContent = 'Hello world';
element.style.color = 'blue';
document.body.appendChild(element);
await visualDiff(element, 'my-element');
});
How it works
Element
You call the visualDiff
function with a reference to a DOM element. The element must be connected to the DOM, in the same document the tests are run in. You can also take a screenshot of the whole body for a full-page screenshot by passing in document.body
. The element can also be in shadow DOM.
Diffing
When you run a diff test for the first time, a baseline image is saved to screenshots/baseline/${browser}/${name}.png
. Afterward, every time you do a diff it is compared to this baseline images.
If the difference between the two images is larger than the configured threshold, the test fails. The failed screenshot is saved as screenshots/${browser}/failed/${name}.png
and an image illustrating the differences between the two images is saved as screenshots/${browser}/failed/${name}-diff.png
.
Updating diffs
When tests are run with the update
option set to true
, the new image will be saved as a baseline and the test will always pass.
In the example config, we read the command line args for a --update-visual-baseline
flag, you can use this when running tests:
web-test-runner test/**/*.test.js --update-visual-baseline
Configuration
These are all the possible configuration options. All options are optional:
import pixelmatch from 'pixelmatch';
type PixelMatchParams = Parameters<typeof pixelmatch>;
type PixelMatchOptions = PixelMatchParams[5];
export interface GetNameArgs {
browser: string;
name: string;
testFile: string;
}
export interface ImageArgs {
filePath: string;
baseDir: string;
name: string;
}
export interface SaveImageArgs extends ImageArgs {
content: Buffer;
}
export type OptionalImage = Buffer | undefined | Promise<Buffer | undefined>;
export interface DiffResult {
diffPercentage: number;
diffImage: Buffer;
}
export interface DiffArgs {
name: string;
baselineImage: Buffer;
image: Buffer;
options: PixelMatchOptions;
}
export interface VisualRegressionPluginOptions {
update: boolean;
baseDir: string;
diffOptions: PixelMatchOptions;
getBaselineName: (args: GetNameArgs) => string;
getDiffName: (args: GetNameArgs) => string;
getFailedName: (args: GetNameArgs) => string;
getBaseline: (args: ImageArgs) => OptionalImage;
saveBaseline: (args: SaveImageArgs) => void | Promise<void>;
saveDiff: (args: SaveImageArgs) => void | Promise<void>;
saveFailed: (args: SaveImageArgs) => void | Promise<void>;
getImageDiff: (args: DiffArgs) => DiffResult | Promise<DiffResult>;
failureThreshold: number;
failureThresholdType: 'percent' | 'pixel';
}
Diffing options
We use the pixelmatch library for diffing images. You can configure the diffing options in the config.
import { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin';
import path from 'path';
export default {
plugins: [
visualRegressionPlugin({
diffOptions: {
threshold: 0.2,
includeAA: false,
},
}),
],
};
Names and directories
By default images are saved to disk to the screenshots/${browser}/baseline
and screenshots/${browser}/failed
directories. You can configure different directories or name patterns.
import { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin';
import path from 'path';
export default {
plugins: [
visualRegressionPlugin({
update: process.argv.includes('--update-visual-baseline'),
baseDir: 'screenshots',
getBaselineName: ({ browser, name }) => path.join(browser, 'baseline', name),
getDiffName: ({ browser, name }) => path.join(browser, 'failed', `${name}-diff`),
getFailedName: ({ browser, name }) => path.join(browser, 'failed', name),
}),
],
};
Storing images externally
By default, we write files on disk, but you can configure this behavior from the config. This way you can, for example, upload images to an external service.
import { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin';
import path from 'path';
export default {
plugins: [
visualRegressionPlugin({
update: process.argv.includes('--update-visual-baseline'),
getBaseline({ filePath, baseDir, name }) {
},
saveBaseline({ filePath, content, baseDir, name }) {
},
saveDiff({ filePath, content, baseDir, name }) {
},
saveFailed({ filePath, content, baseDir, name }) {
},
}),
],
};
Custom diffing
You can implement custom image diffing logic in the config. Use this to implement different diffing libraries.
import { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin';
import path from 'path';
export default {
plugins: [
visualRegressionPlugin({
update: process.argv.includes('--update-visual-baseline'),
getImageDiff({
options: VisualRegressionPluginOptions,
image: Buffer,
browser: string,
name: string,
}) {
return {
diffPercentage,
diffImage,
};
},
}),
],
};