Skip to content

Commit e98d97f

Browse files
authoredOct 13, 2022
feat: add support for configuring failure threshold (#1879)

File tree

4 files changed

+58
-11
lines changed

4 files changed

+58
-11
lines changed
 

Diff for: ‎.changeset/unlucky-turtles-fold.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@web/test-runner-visual-regression': minor
3+
---
4+
5+
Add visual regression config option to set a failure threshold in pixels or percent

Diff for: ‎packages/test-runner-visual-regression/src/config.ts

+16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type OptionalImage = Buffer | undefined | Promise<Buffer | undefined>;
2828

2929
export interface DiffResult {
3030
diffPercentage: number;
31+
diffPixels: number;
3132
diffImage: Buffer;
3233
error: string;
3334
}
@@ -60,6 +61,18 @@ export interface VisualRegressionPluginOptions {
6061
*/
6162
diffOptions: PixelMatchOptions;
6263

64+
/**
65+
* The threshold after which a diff is considered a failure, depending on the failureThresholdType.
66+
* For `failureThresholdType` of "percentage", this should be a number between 0-100.
67+
* For `failureThresholdType` of "pixels", this should be a positive integer.
68+
*/
69+
failureThreshold: number;
70+
71+
/**
72+
* The type of threshold that would trigger a failure.
73+
*/
74+
failureThresholdType: 'percent' | 'pixel';
75+
6376
/**
6477
* Returns the name of the baseline image file. The name
6578
* is a path relative to the baseDir
@@ -119,6 +132,9 @@ export const defaultOptions: VisualRegressionPluginOptions = {
119132
baseDir: 'screenshots',
120133
diffOptions: {},
121134

135+
failureThreshold: 0,
136+
failureThresholdType: 'percent',
137+
122138
getBaselineName: ({ browser, name }) => path.join(browser, 'baseline', name),
123139
getDiffName: ({ browser, name }) => path.join(browser, 'failed', `${name}-diff`),
124140
getFailedName: ({ browser, name }) => path.join(browser, 'failed', name),

Diff for: ‎packages/test-runner-visual-regression/src/pixelMatchDiff.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function pixelMatchDiff({ baselineImage, image, options }: DiffArgs): Dif
1212
if (basePng.width !== png.width || basePng.height !== png.height) {
1313
error =
1414
`Screenshot is not the same width and height as the baseline. ` +
15-
`Baseline: { width: ${basePng.width}, height: ${basePng.height} }` +
15+
`Baseline: { width: ${basePng.width}, height: ${basePng.height} } ` +
1616
`Screenshot: { width: ${png.width}, height: ${png.height} }`;
1717
width = Math.max(basePng.width, png.width);
1818
height = Math.max(basePng.height, png.height);
@@ -33,5 +33,6 @@ export function pixelMatchDiff({ baselineImage, image, options }: DiffArgs): Dif
3333
error,
3434
diffImage: PNG.sync.write(diff),
3535
diffPercentage,
36+
diffPixels: numDiffPixels,
3637
};
3738
}

Diff for: ‎packages/test-runner-visual-regression/src/visualDiffCommand.ts

+35-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import path from 'path';
22

3-
import { VisualRegressionPluginOptions } from './config';
3+
import { VisualRegressionPluginOptions, DiffResult } from './config';
44
import { VisualRegressionError } from './VisualRegressionError';
55

66
function resolveImagePath(baseDir: string, name: string) {
@@ -22,6 +22,30 @@ export interface VisualDiffCommandContext {
2222
testFile: string;
2323
}
2424

25+
function passesFailureThreshold(
26+
{ diffPercentage, diffPixels }: DiffResult,
27+
{ failureThresholdType, failureThreshold }: VisualRegressionPluginOptions,
28+
): { passed: boolean; message?: string } {
29+
if (failureThresholdType === 'percent') {
30+
return diffPercentage <= failureThreshold
31+
? { passed: true }
32+
: {
33+
passed: false,
34+
// if diff is suitably small, output raw value, otherwise to two decimal points.
35+
// this avoids outputting a failure value of "0.00%"
36+
message: `${diffPercentage < 0.005 ? diffPercentage : diffPercentage.toFixed(2)}%`,
37+
};
38+
}
39+
40+
if (failureThresholdType === 'pixel') {
41+
return diffPixels <= failureThreshold
42+
? { passed: true }
43+
: { passed: false, message: `${diffPixels} pixels` };
44+
}
45+
46+
throw new VisualRegressionError(`Unrecognized failureThresholdType: ${failureThresholdType}`);
47+
}
48+
2549
export async function visualDiffCommand(
2650
options: VisualRegressionPluginOptions,
2751
image: Buffer,
@@ -79,22 +103,28 @@ export async function visualDiffCommand(
79103
};
80104
}
81105

82-
const { diffImage, diffPercentage, error } = await options.getImageDiff({
106+
const result = await options.getImageDiff({
83107
name,
84108
baselineImage,
85109
image,
86110
options: options.diffOptions,
87111
});
88112

113+
const { error, diffImage } = result;
114+
89115
if (error) {
90116
// The diff has failed, be sure to save the new image.
91117
await saveFailed();
92118
await saveDiff();
93119

94-
throw new VisualRegressionError(error);
120+
return {
121+
passed: false,
122+
errorMessage: error,
123+
diffPercentage: -1,
124+
};
95125
}
96126

97-
const passed = diffPercentage === 0;
127+
const { passed, message } = passesFailureThreshold(result, options);
98128

99129
if (!passed) {
100130
await saveDiff();
@@ -104,14 +134,9 @@ export async function visualDiffCommand(
104134
await saveFailed();
105135
}
106136

107-
// if diff is suitably small, output raw value, otherwise to two decimal points.
108-
// this avoids outputting a message like "New screenshot is 0.00% different"
109-
const diffPercentageToDisplay =
110-
diffPercentage < 0.005 ? diffPercentage : diffPercentage.toFixed(2);
111-
112137
return {
113138
errorMessage: !passed
114-
? `Visual diff failed. New screenshot is ${diffPercentageToDisplay}% different.\nSee diff for details: ${diffFilePath}`
139+
? `Visual diff failed. New screenshot is ${message} different.\nSee diff for details: ${diffFilePath}`
115140
: undefined,
116141
diffPercentage: -1,
117142
passed,

0 commit comments

Comments
 (0)
Please sign in to comment.