Skip to content

Commit 388cccf

Browse files
committed
feat: first implementation
1 parent 9589994 commit 388cccf

16 files changed

+11150
-1
lines changed

Diff for: .editorconfig

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
7+
[*.{js,json,yml}]
8+
charset = utf-8
9+
indent_style = space
10+
indent_size = 2

Diff for: .eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist
3+
coverage

Diff for: .eslintrc.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module.exports = {
2+
root: true,
3+
plugins: [
4+
'@typescript-eslint',
5+
'eslint-comments',
6+
"cypress"
7+
],
8+
env: {
9+
es6: true,
10+
node: true,
11+
'cypress/globals': true
12+
},
13+
extends: [
14+
'eslint:recommended',
15+
'plugin:@typescript-eslint/recommended',
16+
'plugin:eslint-comments/recommended',
17+
'prettier'
18+
],
19+
overrides: [
20+
{
21+
files: ['**.ts'],
22+
parser: '@typescript-eslint/parser',
23+
parserOptions: {
24+
sourceType: 'module',
25+
project: [
26+
'./tsconfig.json',
27+
'./packages/*/tsconfig.json',
28+
],
29+
tsconfigRootDir: __dirname,
30+
warnOnUnsupportedTypeScriptVersion: false,
31+
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
32+
},
33+
rules: {
34+
'@typescript-eslint/explicit-module-boundary-types': 'off'
35+
}
36+
},
37+
],
38+
};

Diff for: .github/dependabot.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "npm"
4+
directory: "/"
5+
schedule:
6+
interval: "monthly"

Diff for: .gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ yarn-debug.log*
66
yarn-error.log*
77
lerna-debug.log*
88

9+
# Yarn
10+
.yarn/*
11+
*/.yarn
12+
!.yarn/patches
13+
!.yarn/plugins
14+
!.yarn/releases
15+
!.yarn/sdks
16+
!.yarn/versions
17+
.pnp.*
18+
919
# Diagnostic reports (https://nodejs.org/api/report.html)
1020
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
1121

Diff for: .yarn/releases/yarn-3.1.0-rc.13.cjs

+768
Large diffs are not rendered by default.

Diff for: .yarnrc.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
nodeLinker: node-modules
2+
yarnPath: .yarn/releases/yarn-3.1.0-rc.13.cjs

Diff for: README.md

+70-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,71 @@
11
# cypress-plugin-visual-regression-diff
2-
Perform visual regression test with a nice GUI as help. 💅 Only for Cypress!
2+
3+
<p align="center">
4+
<a href="https://www.npmjs.com/package/@frsource/cypress-plugin-visual-regression-diff">
5+
<img src="https://img.shields.io/npm/v/@frsource/cypress-plugin-visual-regression-diff" alt="NPM version badge">
6+
</a>
7+
<a href="https://github.com/semantic-release/semantic-release">
8+
<img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-relase badge">
9+
</a>
10+
<a href="https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/blob/master/LICENSE">
11+
<img src="https://img.shields.io/github/license/FRSOURCE/cypress-plugin-visual-regression-diff" alt="license MIT badge">
12+
</a>
13+
</p>
14+
15+
<h1 align="center">Cypress Plugin Visual Regresion Diff - perform visual regression test with a nice GUI as help. 💅 Only for Cypress!</h1>
16+
17+
<p align="center">
18+
<a href="#getting-started">Getting Started</a>
19+
·
20+
<a href="#documentation">Documentation</a>
21+
·
22+
<a href="https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/issues">File an Issue</a>
23+
·
24+
<a href="https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/discussions">Have a question or an idea?</a>
25+
<br>
26+
</p>
27+
28+
<p align="center">
29+
<br>
30+
<i>Plugin for visual regression testing that provides smooth experience:
31+
<br>Specify threshold below which the test will fail.
32+
<br>Quickly preview old/new screenshots and a visual diff between the two directly in the Cypress UI.
33+
<br>Published as treeshakeable bundles, separate for JS ES5 or modern browsers thanks to <a href="https://www.npmjs.com/package/microbundle">microbundle</a>.
34+
<br>Provides proper typings as is written completely in <a href="https://www.typescriptlang.org">typescript</a>.</i>
35+
<br>
36+
<br>
37+
</p>
38+
39+
<p align="center">
40+
<img src="https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/blob/master/src/assets/logo.svg" alt="Cypress Plugin Visual Regression Diff logo" height="120px"/>
41+
<br>
42+
<a href="https://www.npmjs.com/package/@frsource/cypress-plugin-visual-regression-diff"><strong>@frsource/cypress-plugin-visual-regression-diff</strong></a>
43+
<br>
44+
<br>
45+
</p>
46+
47+
## Getting started
48+
49+
50+
## Documentation
51+
52+
53+
## Questions
54+
55+
Don’t hesitate to ask a question directly on the [discussions board](https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/discussions)!
56+
57+
## Changelog
58+
59+
Changes for every release are documented in the [release notes](https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/releases) and [CHANGELOG files of every package](https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/tree/master/packages).
60+
61+
## License
62+
63+
[MIT](https://opensource.org/licenses/MIT)
64+
65+
Copyright (c) 2021-present, Jakub FRS Freisler, [FRSOURCE](https://www.frsource.org/)
66+
67+
<p align="center">
68+
<a href="https://www.frsource.org/" title="Click to visit FRSOURCE page!">
69+
<img src="https://www.frsource.org/logo.jpg" alt="FRSOURCE logo" height="60px"/>
70+
</a>
71+
</p>

Diff for: package.json

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"name": "@frsource/cypress-plugin-visual-regression-diff",
3+
"description": "Perform visual regression test with a nice GUI as help. 💅 Only for Cypress!",
4+
"version": "0.0.1",
5+
"author": "Jakub Freisler <[email protected]>",
6+
"repository": "https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff.git",
7+
"sideEffects": false,
8+
"license": "MIT",
9+
"scripts": {
10+
"build": "del-cli dist && microbundle src/*.ts -f cjs,esm",
11+
"lint": "eslint '**/*.ts' --ignore-pattern '**/*.d.ts'",
12+
"lint.fix": "yarn lint --fix",
13+
"lint.ci": "yarn lint --max-warnings 0",
14+
"prettify": "prettier --write src",
15+
"prepack": "yarn build",
16+
"release": "semantic-release",
17+
"release:ci": "yarn release --yes",
18+
"release:test": "yarn release --no-git-tag-version --no-push --skip-npm"
19+
},
20+
"engines": {
21+
"node": ">=10"
22+
},
23+
"packageManager": "[email protected]",
24+
"peerDependencies": {
25+
"cypress": ">=4.5.0"
26+
},
27+
"devDependencies": {
28+
"@semantic-release/changelog": "^6.0.0",
29+
"@semantic-release/commit-analyzer": "^9.0.1",
30+
"@semantic-release/git": "^10.0.0",
31+
"@semantic-release/npm": "^8.0.2",
32+
"@semantic-release/release-notes-generator": "^10.0.2",
33+
"@types/pixelmatch": "^5.2.4",
34+
"@types/pngjs": "^6.0.1",
35+
"@typescript-eslint/eslint-plugin": "^5.1.0",
36+
"@typescript-eslint/parser": "^5.1.0",
37+
"cypress": "^8.6.0",
38+
"del-cli": "^4.0.1",
39+
"eslint": "^8.1.0",
40+
"eslint-config-prettier": "^8.3.0",
41+
"eslint-plugin-cypress": "^2.12.1",
42+
"eslint-plugin-eslint-comments": "^3.2.0",
43+
"microbundle": "^0.14.1",
44+
"prettier": "^2.4.1",
45+
"semantic-release": "^18.0.0",
46+
"typescript": "^4.4.4"
47+
},
48+
"keywords": [
49+
"Cypress",
50+
"Cypress plugin",
51+
"visual regression testing",
52+
"visual diff",
53+
"image diff",
54+
"visual comparison",
55+
"image comparison",
56+
"Cypress visual regression",
57+
"regression testing",
58+
"visual snapshot",
59+
"image snapshot",
60+
"Cypress image snapshot"
61+
],
62+
"dependencies": {
63+
"pixelmatch": "^5.2.1",
64+
"pngjs": "^6.0.0"
65+
}
66+
}

Diff for: src/assets/logo.svg

+8
Loading

Diff for: src/commands.ts

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { FILE_SUFFIX, LINK_PREFIX, TASK } from "./constants";
2+
import type pixelmatch from "pixelmatch";
3+
4+
const nameCacheCounter: Record<string, number> = {};
5+
6+
Cypress.Commands.add(
7+
"matchImage",
8+
{ prevSubject: "optional" },
9+
(
10+
subject?: JQuery<HTMLElement>,
11+
options: {
12+
suffix?: string;
13+
screenshotConfig?: Partial<Cypress.ScreenshotDefaultsOptions>;
14+
diffConfig?: Parameters<typeof pixelmatch>[5];
15+
} = {}
16+
) => {
17+
let title = Cypress.currentTest.titlePath.join(" ");
18+
if (typeof nameCacheCounter[title] === "undefined")
19+
nameCacheCounter[title] = -1;
20+
title += ` #${++nameCacheCounter[title]}`;
21+
22+
return cy
23+
.log("visual regression diff")
24+
.then(() =>
25+
cy.task(TASK.getScreenshotPath, {
26+
title,
27+
specPath: Cypress.spec.relative,
28+
})
29+
)
30+
.then((screenshotPath) => {
31+
let imgPath: string;
32+
return (subject ? cy.wrap(subject) : cy)
33+
.screenshot(screenshotPath as string, {
34+
onAfterScreenshot(_el, props) {
35+
imgPath = props.path;
36+
},
37+
})
38+
.then(() => imgPath);
39+
})
40+
.then((imgPath) =>
41+
cy
42+
.task(TASK.compareImages, {
43+
imgNew: imgPath,
44+
imgOld: imgPath.replace(FILE_SUFFIX.actual, ""),
45+
...(options.diffConfig || {}),
46+
})
47+
.then((res) => ({
48+
res: res as null | {
49+
error?: boolean;
50+
message?: string;
51+
imgDiff?: number;
52+
maxDiffThreshold?: number;
53+
},
54+
imgPath,
55+
}))
56+
)
57+
.then(({ res, imgPath }) => {
58+
const log = Cypress.log({
59+
name: "log",
60+
displayName: "Match image",
61+
$el: subject,
62+
});
63+
64+
if (!res) {
65+
log.set("message", "Unexpected error!");
66+
const err = new Error("Unexpected error!");
67+
// only way to throw & log the message properly in Cypress
68+
// https://github.com/cypress-io/cypress/blob/5f94cad3cb4126e0567290b957050c33e3a78e3c/packages/driver/src/cypress/error_utils.ts#L214-L216
69+
(err as unknown as { onFail: (e: Error) => void }).onFail = (
70+
err: Error
71+
) => log.error(err);
72+
throw err;
73+
}
74+
75+
if (res.error) {
76+
log.set(
77+
"message",
78+
`${res.message}\n[See comparison](${LINK_PREFIX}${btoa(
79+
JSON.stringify({ title, imgPath })
80+
)})`
81+
);
82+
log.set("consoleProps", () => res);
83+
const err = new Error(res.message);
84+
// only way to throw & log the message properly in Cypress
85+
// https://github.com/cypress-io/cypress/blob/5f94cad3cb4126e0567290b957050c33e3a78e3c/packages/driver/src/cypress/error_utils.ts#L214-L216
86+
(err as unknown as { onFail: (e: Error) => void }).onFail = (
87+
err: Error
88+
) => log.error(err);
89+
throw err;
90+
} else {
91+
log.set("message", res.message);
92+
}
93+
});
94+
}
95+
);

Diff for: src/constants.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const PLUGIN_NAME = "cp-visual-regression-diff";
2+
export const LINK_PREFIX = `#${PLUGIN_NAME}-`;
3+
export const OVERLAY_CLASS = `${PLUGIN_NAME}-overlay`;
4+
export const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;
5+
6+
export enum FILE_SUFFIX {
7+
diff = ".diff",
8+
actual = ".actual",
9+
}
10+
11+
export const TASK = {
12+
getScreenshotPath: `${PLUGIN_NAME}-getScreenshotPath`,
13+
compareImages: `${PLUGIN_NAME}-compareImages`,
14+
approveImage: `${PLUGIN_NAME}-approveImage`,
15+
};

0 commit comments

Comments
 (0)