@pixsame/cypress-plugin-visual-regression-diff 0.0.0 → 1.0.0-canary-20261011-4a88kxci
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +563 -1
- package/dist/commands.d.ts +60 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +43 -0
- package/dist/constants.js.map +1 -0
- package/dist/constants.mjs +34 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/plugins.d.ts +1 -0
- package/dist/plugins.js +575 -0
- package/dist/plugins.js.map +1 -0
- package/dist/plugins.mjs +567 -0
- package/dist/plugins.mjs.map +1 -0
- package/dist/support.d.ts +12 -0
- package/dist/support.js +647 -0
- package/dist/support.js.map +1 -0
- package/dist/support.mjs +633 -0
- package/dist/support.mjs.map +1 -0
- package/dist/types.d.ts +20 -0
- package/package.json +117 -4
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const PLUGIN_NAME = 'cp-visual-regression-diff';
|
|
2
|
+
const LINK_PREFIX = `#${PLUGIN_NAME}-`;
|
|
3
|
+
const OVERLAY_CLASS = `${PLUGIN_NAME}-overlay`;
|
|
4
|
+
const FAB_CLASS = `${PLUGIN_NAME}-fab`;
|
|
5
|
+
const FAB_BADGE_CLASS = `${PLUGIN_NAME}-fab-badge`;
|
|
6
|
+
const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;
|
|
7
|
+
exports.FILE_SUFFIX = void 0;
|
|
8
|
+
(function (FILE_SUFFIX) {
|
|
9
|
+
FILE_SUFFIX["diff"] = ".diff";
|
|
10
|
+
FILE_SUFFIX["actual"] = ".actual";
|
|
11
|
+
})(exports.FILE_SUFFIX || (exports.FILE_SUFFIX = {}));
|
|
12
|
+
const TASK = {
|
|
13
|
+
getScreenshotPathInfo: `${PLUGIN_NAME}-getScreenshotPathInfo`,
|
|
14
|
+
compareImages: `${PLUGIN_NAME}-compareImages`,
|
|
15
|
+
approveImage: `${PLUGIN_NAME}-approveImage`,
|
|
16
|
+
cleanupImages: `${PLUGIN_NAME}-cleanupImages`,
|
|
17
|
+
doesFileExist: `${PLUGIN_NAME}-doesFileExist`,
|
|
18
|
+
processImgPath: `${PLUGIN_NAME}-processImgPath`,
|
|
19
|
+
recordPendingDiff: `${PLUGIN_NAME}-recordPendingDiff`,
|
|
20
|
+
getPendingDiffs: `${PLUGIN_NAME}-getPendingDiffs`,
|
|
21
|
+
clearPendingDiffs: `${PLUGIN_NAME}-clearPendingDiffs`
|
|
22
|
+
/* c8 ignore next */
|
|
23
|
+
};
|
|
24
|
+
const PATH_VARIABLES = {
|
|
25
|
+
specPath: '{spec_path}',
|
|
26
|
+
unixSystemRootPath: '{unix_system_root_path}',
|
|
27
|
+
winSystemRootPath: '{win_system_root_path}'
|
|
28
|
+
};
|
|
29
|
+
const WINDOWS_LIKE_DRIVE_REGEX = /^[A-Z]:$/;
|
|
30
|
+
const METADATA_KEY = 'FRSOURCE_CPVRD_V';
|
|
31
|
+
const LS_SHOW_NON_FAILING_DIFFS = 'cp-vrd-show-non-failing-diffs';
|
|
32
|
+
|
|
33
|
+
exports.FAB_BADGE_CLASS = FAB_BADGE_CLASS;
|
|
34
|
+
exports.FAB_CLASS = FAB_CLASS;
|
|
35
|
+
exports.IMAGE_SNAPSHOT_PREFIX = IMAGE_SNAPSHOT_PREFIX;
|
|
36
|
+
exports.LINK_PREFIX = LINK_PREFIX;
|
|
37
|
+
exports.LS_SHOW_NON_FAILING_DIFFS = LS_SHOW_NON_FAILING_DIFFS;
|
|
38
|
+
exports.METADATA_KEY = METADATA_KEY;
|
|
39
|
+
exports.OVERLAY_CLASS = OVERLAY_CLASS;
|
|
40
|
+
exports.PATH_VARIABLES = PATH_VARIABLES;
|
|
41
|
+
exports.TASK = TASK;
|
|
42
|
+
exports.WINDOWS_LIKE_DRIVE_REGEX = WINDOWS_LIKE_DRIVE_REGEX;
|
|
43
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["const PLUGIN_NAME = 'cp-visual-regression-diff';\nexport const LINK_PREFIX = `#${PLUGIN_NAME}-`;\nexport const OVERLAY_CLASS = `${PLUGIN_NAME}-overlay`;\nexport const FAB_CLASS = `${PLUGIN_NAME}-fab`;\nexport const FAB_BADGE_CLASS = `${PLUGIN_NAME}-fab-badge`;\nexport const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;\n\nexport enum FILE_SUFFIX {\n diff = '.diff',\n actual = '.actual',\n}\n\nexport const TASK = {\n getScreenshotPathInfo: `${PLUGIN_NAME}-getScreenshotPathInfo`,\n compareImages: `${PLUGIN_NAME}-compareImages`,\n approveImage: `${PLUGIN_NAME}-approveImage`,\n cleanupImages: `${PLUGIN_NAME}-cleanupImages`,\n doesFileExist: `${PLUGIN_NAME}-doesFileExist`,\n processImgPath: `${PLUGIN_NAME}-processImgPath`,\n recordPendingDiff: `${PLUGIN_NAME}-recordPendingDiff`,\n getPendingDiffs: `${PLUGIN_NAME}-getPendingDiffs`,\n clearPendingDiffs: `${PLUGIN_NAME}-clearPendingDiffs`,\n /* c8 ignore next */\n};\n\nexport const PATH_VARIABLES = {\n specPath: '{spec_path}',\n unixSystemRootPath: '{unix_system_root_path}',\n winSystemRootPath: '{win_system_root_path}',\n} as const;\n\nexport const WINDOWS_LIKE_DRIVE_REGEX = /^[A-Z]:$/;\n\nexport const METADATA_KEY = 'FRSOURCE_CPVRD_V';\n\nexport const LS_SHOW_NON_FAILING_DIFFS = 'cp-vrd-show-non-failing-diffs';\n"],"names":["PLUGIN_NAME","LINK_PREFIX","OVERLAY_CLASS","FAB_CLASS","FAB_BADGE_CLASS","IMAGE_SNAPSHOT_PREFIX","FILE_SUFFIX","TASK","getScreenshotPathInfo","compareImages","approveImage","cleanupImages","doesFileExist","processImgPath","recordPendingDiff","getPendingDiffs","clearPendingDiffs","PATH_VARIABLES","specPath","unixSystemRootPath","winSystemRootPath","WINDOWS_LIKE_DRIVE_REGEX","METADATA_KEY","LS_SHOW_NON_FAILING_DIFFS"],"mappings":"AAAA,MAAMA,WAAW,GAAG,2BAA2B,CAAA;AAClCC,MAAAA,WAAW,GAAG,CAAA,CAAA,EAAID,WAAW,CAAG,CAAA,EAAA;AAChCE,MAAAA,aAAa,GAAG,CAAA,EAAGF,WAAW,CAAU,QAAA,EAAA;AACxCG,MAAAA,SAAS,GAAG,CAAA,EAAGH,WAAW,CAAM,IAAA,EAAA;AAChCI,MAAAA,eAAe,GAAG,CAAA,EAAGJ,WAAW,CAAY,UAAA,EAAA;AAC5CK,MAAAA,qBAAqB,GAAG,CAAA,EAAA,EAAKL,WAAW,CAAc,YAAA,EAAA;AAEvDM,6BAGX;AAHD,CAAA,UAAYA,WAAW,EAAA;AACrBA,EAAAA,WAAA,CAAA,MAAA,CAAA,GAAA,OAAc,CAAA;AACdA,EAAAA,WAAA,CAAA,QAAA,CAAA,GAAA,SAAkB,CAAA;AACpB,CAAC,EAHWA,mBAAW,KAAXA,mBAAW,GAGtB,EAAA,CAAA,CAAA,CAAA;AAEM,MAAMC,IAAI,GAAG;EAClBC,qBAAqB,EAAE,CAAGR,EAAAA,WAAW,CAAwB,sBAAA,CAAA;EAC7DS,aAAa,EAAE,CAAGT,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7CU,YAAY,EAAE,CAAGV,EAAAA,WAAW,CAAe,aAAA,CAAA;EAC3CW,aAAa,EAAE,CAAGX,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7CY,aAAa,EAAE,CAAGZ,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7Ca,cAAc,EAAE,CAAGb,EAAAA,WAAW,CAAiB,eAAA,CAAA;EAC/Cc,iBAAiB,EAAE,CAAGd,EAAAA,WAAW,CAAoB,kBAAA,CAAA;EACrDe,eAAe,EAAE,CAAGf,EAAAA,WAAW,CAAkB,gBAAA,CAAA;EACjDgB,iBAAiB,EAAE,GAAGhB,WAAW,CAAA,kBAAA,CAAA;AACjC;EACD;AAEM,MAAMiB,cAAc,GAAG;AAC5BC,EAAAA,QAAQ,EAAE,aAAa;AACvBC,EAAAA,kBAAkB,EAAE,yBAAyB;AAC7CC,EAAAA,iBAAiB,EAAE,wBAAA;EACX;AAEH,MAAMC,wBAAwB,GAAG,WAAU;AAE3C,MAAMC,YAAY,GAAG,mBAAkB;AAEvC,MAAMC,yBAAyB,GAAG;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const PLUGIN_NAME = 'cp-visual-regression-diff';
|
|
2
|
+
const LINK_PREFIX = `#${PLUGIN_NAME}-`;
|
|
3
|
+
const OVERLAY_CLASS = `${PLUGIN_NAME}-overlay`;
|
|
4
|
+
const FAB_CLASS = `${PLUGIN_NAME}-fab`;
|
|
5
|
+
const FAB_BADGE_CLASS = `${PLUGIN_NAME}-fab-badge`;
|
|
6
|
+
const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;
|
|
7
|
+
var FILE_SUFFIX;
|
|
8
|
+
(function (FILE_SUFFIX) {
|
|
9
|
+
FILE_SUFFIX["diff"] = ".diff";
|
|
10
|
+
FILE_SUFFIX["actual"] = ".actual";
|
|
11
|
+
})(FILE_SUFFIX || (FILE_SUFFIX = {}));
|
|
12
|
+
const TASK = {
|
|
13
|
+
getScreenshotPathInfo: `${PLUGIN_NAME}-getScreenshotPathInfo`,
|
|
14
|
+
compareImages: `${PLUGIN_NAME}-compareImages`,
|
|
15
|
+
approveImage: `${PLUGIN_NAME}-approveImage`,
|
|
16
|
+
cleanupImages: `${PLUGIN_NAME}-cleanupImages`,
|
|
17
|
+
doesFileExist: `${PLUGIN_NAME}-doesFileExist`,
|
|
18
|
+
processImgPath: `${PLUGIN_NAME}-processImgPath`,
|
|
19
|
+
recordPendingDiff: `${PLUGIN_NAME}-recordPendingDiff`,
|
|
20
|
+
getPendingDiffs: `${PLUGIN_NAME}-getPendingDiffs`,
|
|
21
|
+
clearPendingDiffs: `${PLUGIN_NAME}-clearPendingDiffs`
|
|
22
|
+
/* c8 ignore next */
|
|
23
|
+
};
|
|
24
|
+
const PATH_VARIABLES = {
|
|
25
|
+
specPath: '{spec_path}',
|
|
26
|
+
unixSystemRootPath: '{unix_system_root_path}',
|
|
27
|
+
winSystemRootPath: '{win_system_root_path}'
|
|
28
|
+
};
|
|
29
|
+
const WINDOWS_LIKE_DRIVE_REGEX = /^[A-Z]:$/;
|
|
30
|
+
const METADATA_KEY = 'FRSOURCE_CPVRD_V';
|
|
31
|
+
const LS_SHOW_NON_FAILING_DIFFS = 'cp-vrd-show-non-failing-diffs';
|
|
32
|
+
|
|
33
|
+
export { FAB_BADGE_CLASS, FAB_CLASS, FILE_SUFFIX, IMAGE_SNAPSHOT_PREFIX, LINK_PREFIX, LS_SHOW_NON_FAILING_DIFFS, METADATA_KEY, OVERLAY_CLASS, PATH_VARIABLES, TASK, WINDOWS_LIKE_DRIVE_REGEX };
|
|
34
|
+
//# sourceMappingURL=constants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.mjs","sources":["../src/constants.ts"],"sourcesContent":["const PLUGIN_NAME = 'cp-visual-regression-diff';\nexport const LINK_PREFIX = `#${PLUGIN_NAME}-`;\nexport const OVERLAY_CLASS = `${PLUGIN_NAME}-overlay`;\nexport const FAB_CLASS = `${PLUGIN_NAME}-fab`;\nexport const FAB_BADGE_CLASS = `${PLUGIN_NAME}-fab-badge`;\nexport const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;\n\nexport enum FILE_SUFFIX {\n diff = '.diff',\n actual = '.actual',\n}\n\nexport const TASK = {\n getScreenshotPathInfo: `${PLUGIN_NAME}-getScreenshotPathInfo`,\n compareImages: `${PLUGIN_NAME}-compareImages`,\n approveImage: `${PLUGIN_NAME}-approveImage`,\n cleanupImages: `${PLUGIN_NAME}-cleanupImages`,\n doesFileExist: `${PLUGIN_NAME}-doesFileExist`,\n processImgPath: `${PLUGIN_NAME}-processImgPath`,\n recordPendingDiff: `${PLUGIN_NAME}-recordPendingDiff`,\n getPendingDiffs: `${PLUGIN_NAME}-getPendingDiffs`,\n clearPendingDiffs: `${PLUGIN_NAME}-clearPendingDiffs`,\n /* c8 ignore next */\n};\n\nexport const PATH_VARIABLES = {\n specPath: '{spec_path}',\n unixSystemRootPath: '{unix_system_root_path}',\n winSystemRootPath: '{win_system_root_path}',\n} as const;\n\nexport const WINDOWS_LIKE_DRIVE_REGEX = /^[A-Z]:$/;\n\nexport const METADATA_KEY = 'FRSOURCE_CPVRD_V';\n\nexport const LS_SHOW_NON_FAILING_DIFFS = 'cp-vrd-show-non-failing-diffs';\n"],"names":["PLUGIN_NAME","LINK_PREFIX","OVERLAY_CLASS","FAB_CLASS","FAB_BADGE_CLASS","IMAGE_SNAPSHOT_PREFIX","FILE_SUFFIX","TASK","getScreenshotPathInfo","compareImages","approveImage","cleanupImages","doesFileExist","processImgPath","recordPendingDiff","getPendingDiffs","clearPendingDiffs","PATH_VARIABLES","specPath","unixSystemRootPath","winSystemRootPath","WINDOWS_LIKE_DRIVE_REGEX","METADATA_KEY","LS_SHOW_NON_FAILING_DIFFS"],"mappings":"AAAA,MAAMA,WAAW,GAAG,2BAA2B,CAAA;AAClCC,MAAAA,WAAW,GAAG,CAAA,CAAA,EAAID,WAAW,CAAG,CAAA,EAAA;AAChCE,MAAAA,aAAa,GAAG,CAAA,EAAGF,WAAW,CAAU,QAAA,EAAA;AACxCG,MAAAA,SAAS,GAAG,CAAA,EAAGH,WAAW,CAAM,IAAA,EAAA;AAChCI,MAAAA,eAAe,GAAG,CAAA,EAAGJ,WAAW,CAAY,UAAA,EAAA;AAC5CK,MAAAA,qBAAqB,GAAG,CAAA,EAAA,EAAKL,WAAW,CAAc,YAAA,EAAA;IAEvDM,YAGX;AAHD,CAAA,UAAYA,WAAW,EAAA;AACrBA,EAAAA,WAAA,CAAA,MAAA,CAAA,GAAA,OAAc,CAAA;AACdA,EAAAA,WAAA,CAAA,QAAA,CAAA,GAAA,SAAkB,CAAA;AACpB,CAAC,EAHWA,WAAW,KAAXA,WAAW,GAGtB,EAAA,CAAA,CAAA,CAAA;AAEM,MAAMC,IAAI,GAAG;EAClBC,qBAAqB,EAAE,CAAGR,EAAAA,WAAW,CAAwB,sBAAA,CAAA;EAC7DS,aAAa,EAAE,CAAGT,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7CU,YAAY,EAAE,CAAGV,EAAAA,WAAW,CAAe,aAAA,CAAA;EAC3CW,aAAa,EAAE,CAAGX,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7CY,aAAa,EAAE,CAAGZ,EAAAA,WAAW,CAAgB,cAAA,CAAA;EAC7Ca,cAAc,EAAE,CAAGb,EAAAA,WAAW,CAAiB,eAAA,CAAA;EAC/Cc,iBAAiB,EAAE,CAAGd,EAAAA,WAAW,CAAoB,kBAAA,CAAA;EACrDe,eAAe,EAAE,CAAGf,EAAAA,WAAW,CAAkB,gBAAA,CAAA;EACjDgB,iBAAiB,EAAE,GAAGhB,WAAW,CAAA,kBAAA,CAAA;AACjC;EACD;AAEM,MAAMiB,cAAc,GAAG;AAC5BC,EAAAA,QAAQ,EAAE,aAAa;AACvBC,EAAAA,kBAAkB,EAAE,yBAAyB;AAC7CC,EAAAA,iBAAiB,EAAE,wBAAA;EACX;AAEH,MAAMC,wBAAwB,GAAG,WAAU;AAE3C,MAAMC,YAAY,GAAG,mBAAkB;AAEvC,MAAMC,yBAAyB,GAAG;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const initPlugin: (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => void;
|
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var pngjs = require('pngjs');
|
|
3
|
+
var pixelmatch = require('pixelmatch');
|
|
4
|
+
var moveFile = require('move-file');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var sharp = require('sharp');
|
|
7
|
+
var metaPngPkg = require('meta-png');
|
|
8
|
+
var glob = require('glob');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
+
|
|
12
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
13
|
+
var pixelmatch__default = /*#__PURE__*/_interopDefaultLegacy(pixelmatch);
|
|
14
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
15
|
+
var sharp__default = /*#__PURE__*/_interopDefaultLegacy(sharp);
|
|
16
|
+
var metaPngPkg__default = /*#__PURE__*/_interopDefaultLegacy(metaPngPkg);
|
|
17
|
+
|
|
18
|
+
const PLUGIN_NAME = 'cp-visual-regression-diff';
|
|
19
|
+
const IMAGE_SNAPSHOT_PREFIX = `__${PLUGIN_NAME}_snapshots__`;
|
|
20
|
+
var FILE_SUFFIX;
|
|
21
|
+
(function (FILE_SUFFIX) {
|
|
22
|
+
FILE_SUFFIX["diff"] = ".diff";
|
|
23
|
+
FILE_SUFFIX["actual"] = ".actual";
|
|
24
|
+
})(FILE_SUFFIX || (FILE_SUFFIX = {}));
|
|
25
|
+
const TASK = {
|
|
26
|
+
getScreenshotPathInfo: `${PLUGIN_NAME}-getScreenshotPathInfo`,
|
|
27
|
+
compareImages: `${PLUGIN_NAME}-compareImages`,
|
|
28
|
+
approveImage: `${PLUGIN_NAME}-approveImage`,
|
|
29
|
+
cleanupImages: `${PLUGIN_NAME}-cleanupImages`,
|
|
30
|
+
doesFileExist: `${PLUGIN_NAME}-doesFileExist`,
|
|
31
|
+
processImgPath: `${PLUGIN_NAME}-processImgPath`,
|
|
32
|
+
recordPendingDiff: `${PLUGIN_NAME}-recordPendingDiff`,
|
|
33
|
+
getPendingDiffs: `${PLUGIN_NAME}-getPendingDiffs`,
|
|
34
|
+
clearPendingDiffs: `${PLUGIN_NAME}-clearPendingDiffs`
|
|
35
|
+
/* c8 ignore next */
|
|
36
|
+
};
|
|
37
|
+
const PATH_VARIABLES = {
|
|
38
|
+
specPath: '{spec_path}',
|
|
39
|
+
unixSystemRootPath: '{unix_system_root_path}',
|
|
40
|
+
winSystemRootPath: '{win_system_root_path}'
|
|
41
|
+
};
|
|
42
|
+
const WINDOWS_LIKE_DRIVE_REGEX = /^[A-Z]:$/;
|
|
43
|
+
const METADATA_KEY = 'FRSOURCE_CPVRD_V';
|
|
44
|
+
|
|
45
|
+
const supportsExpose = version => {
|
|
46
|
+
const [major, minor] = version.split('.').map(Number);
|
|
47
|
+
return major > 15 || major === 15 && minor >= 10;
|
|
48
|
+
};
|
|
49
|
+
const getPluginConfig = (config, key) => {
|
|
50
|
+
var _config$version, _config$env;
|
|
51
|
+
if (supportsExpose((_config$version = config.version) != null ? _config$version : '')) {
|
|
52
|
+
var _config$expose;
|
|
53
|
+
return (_config$expose = config.expose) == null ? void 0 : _config$expose[key];
|
|
54
|
+
}
|
|
55
|
+
return (_config$env = config.env) == null ? void 0 : _config$env[key];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function isHighSurrogate(codePoint) {
|
|
59
|
+
return codePoint >= 0xd800 && codePoint <= 0xdbff;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isLowSurrogate(codePoint) {
|
|
63
|
+
return codePoint >= 0xdc00 && codePoint <= 0xdfff;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Truncate string by size in bytes
|
|
67
|
+
var truncate = function truncate(getLength, string, byteLength) {
|
|
68
|
+
if (typeof string !== "string") {
|
|
69
|
+
throw new Error("Input must be string");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var charLength = string.length;
|
|
73
|
+
var curByteLength = 0;
|
|
74
|
+
var codePoint;
|
|
75
|
+
var segment;
|
|
76
|
+
|
|
77
|
+
for (var i = 0; i < charLength; i += 1) {
|
|
78
|
+
codePoint = string.charCodeAt(i);
|
|
79
|
+
segment = string[i];
|
|
80
|
+
|
|
81
|
+
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
|
82
|
+
i += 1;
|
|
83
|
+
segment += string[i];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
curByteLength += getLength(segment);
|
|
87
|
+
|
|
88
|
+
if (curByteLength === byteLength) {
|
|
89
|
+
return string.slice(0, i + 1);
|
|
90
|
+
}
|
|
91
|
+
else if (curByteLength > byteLength) {
|
|
92
|
+
return string.slice(0, i - segment.length + 1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return string;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
var getLength = Buffer.byteLength.bind(Buffer);
|
|
100
|
+
var truncateUtf8Bytes = truncate.bind(null, getLength);
|
|
101
|
+
|
|
102
|
+
/*jshint node:true*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Replaces characters in strings that are illegal/unsafe for filenames.
|
|
106
|
+
* Unsafe characters are either removed or replaced by a substitute set
|
|
107
|
+
* in the optional `options` object.
|
|
108
|
+
*
|
|
109
|
+
* Illegal Characters on Various Operating Systems
|
|
110
|
+
* / ? < > \ : * | "
|
|
111
|
+
* https://kb.acronis.com/content/39790
|
|
112
|
+
*
|
|
113
|
+
* Unicode Control codes
|
|
114
|
+
* C0 0x00-0x1f & C1 (0x80-0x9f)
|
|
115
|
+
* http://en.wikipedia.org/wiki/C0_and_C1_control_codes
|
|
116
|
+
*
|
|
117
|
+
* Reserved filenames on Unix-based systems (".", "..")
|
|
118
|
+
* Reserved filenames in Windows ("CON", "PRN", "AUX", "NUL", "COM1",
|
|
119
|
+
* "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
|
120
|
+
* "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", and
|
|
121
|
+
* "LPT9") case-insesitively and with or without filename extensions.
|
|
122
|
+
*
|
|
123
|
+
* Capped at 255 characters in length.
|
|
124
|
+
* http://unix.stackexchange.com/questions/32795/what-is-the-maximum-allowed-filename-and-folder-size-with-ecryptfs
|
|
125
|
+
*
|
|
126
|
+
* @param {String} input Original filename
|
|
127
|
+
* @param {Object} options {replacement: String | Function }
|
|
128
|
+
* @return {String} Sanitized filename
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
var illegalRe = /[\/\?<>\\:\*\|"]/g;
|
|
134
|
+
var controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
|
135
|
+
var reservedRe = /^\.+$/;
|
|
136
|
+
var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Strip trailing spaces and dots, which are not allowed on some Windows file
|
|
140
|
+
* systems. Does not use a regex to avoid a quadratic ReDoS vulnerability
|
|
141
|
+
* (CWE-1333).
|
|
142
|
+
*/
|
|
143
|
+
function replaceTrailingDotsAndSpaces(str, replacement) {
|
|
144
|
+
var end = str.length;
|
|
145
|
+
while (end > 0 && (str[end - 1] === '.' || str[end - 1] === ' ')) end--;
|
|
146
|
+
return end < str.length ? str.slice(0, end) + replacement : str;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function sanitize(input, replacement) {
|
|
150
|
+
if (typeof input !== 'string') {
|
|
151
|
+
throw new Error('Input must be string');
|
|
152
|
+
}
|
|
153
|
+
var sanitized = input
|
|
154
|
+
.replace(illegalRe, replacement)
|
|
155
|
+
.replace(controlRe, replacement)
|
|
156
|
+
.replace(reservedRe, replacement)
|
|
157
|
+
.replace(windowsReservedRe, replacement);
|
|
158
|
+
sanitized = replaceTrailingDotsAndSpaces(sanitized, replacement);
|
|
159
|
+
return truncateUtf8Bytes(sanitized, 255);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
var sanitizeFilename = function (input, options) {
|
|
163
|
+
var replacement = (options && options.replacement) || '';
|
|
164
|
+
var output = sanitize(input, replacement);
|
|
165
|
+
if (replacement === '') {
|
|
166
|
+
return output;
|
|
167
|
+
}
|
|
168
|
+
return sanitize(output, '');
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// highest `_#n` index handed out per screenshot path during the current spec
|
|
172
|
+
const nameCacheCounter = {};
|
|
173
|
+
// counters as they were before the current test attempt started, for every
|
|
174
|
+
// path the attempt touched - a retry restores them so the regenerated
|
|
175
|
+
// screenshots get the same names again
|
|
176
|
+
const countersBeforeAttempt = {};
|
|
177
|
+
let currentAttempt;
|
|
178
|
+
const resetMap = map => {
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
180
|
+
for (const key in map) delete map[key];
|
|
181
|
+
};
|
|
182
|
+
const parsePathPartVariables = (specPath, pathPart, i) => {
|
|
183
|
+
if (pathPart === PATH_VARIABLES.specPath) {
|
|
184
|
+
return path__default["default"].dirname(specPath);
|
|
185
|
+
} else if (i === 0 && !pathPart) {
|
|
186
|
+
// when unix-like absolute path
|
|
187
|
+
return PATH_VARIABLES.unixSystemRootPath;
|
|
188
|
+
} else if (i === 0 && WINDOWS_LIKE_DRIVE_REGEX.test(pathPart)) {
|
|
189
|
+
// when win-like absolute path
|
|
190
|
+
return path__default["default"].join(PATH_VARIABLES.winSystemRootPath, pathPart[0]);
|
|
191
|
+
}
|
|
192
|
+
return pathPart;
|
|
193
|
+
};
|
|
194
|
+
const startAttempt = (testId, retry) => {
|
|
195
|
+
var _currentAttempt;
|
|
196
|
+
const isSameTest = ((_currentAttempt = currentAttempt) == null ? void 0 : _currentAttempt.testId) === testId;
|
|
197
|
+
if (isSameTest && currentAttempt && retry > currentAttempt.retry) {
|
|
198
|
+
for (const screenshotPath in countersBeforeAttempt) {
|
|
199
|
+
nameCacheCounter[screenshotPath] = countersBeforeAttempt[screenshotPath];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (!isSameTest || currentAttempt && retry !== currentAttempt.retry) {
|
|
203
|
+
resetMap(countersBeforeAttempt);
|
|
204
|
+
}
|
|
205
|
+
currentAttempt = {
|
|
206
|
+
testId,
|
|
207
|
+
retry
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
const generateScreenshotPath = ({
|
|
211
|
+
titleFromOptions,
|
|
212
|
+
imagesPath,
|
|
213
|
+
specPath,
|
|
214
|
+
currentRetryNumber,
|
|
215
|
+
testId
|
|
216
|
+
}) => {
|
|
217
|
+
const screenshotPath = path__default["default"].join(...imagesPath.split('/').map(parsePathPartVariables.bind(void 0, specPath)), sanitizeFilename(titleFromOptions));
|
|
218
|
+
startAttempt(testId, currentRetryNumber);
|
|
219
|
+
if (typeof nameCacheCounter[screenshotPath] === 'undefined') {
|
|
220
|
+
nameCacheCounter[screenshotPath] = -1;
|
|
221
|
+
}
|
|
222
|
+
if (!(screenshotPath in countersBeforeAttempt)) {
|
|
223
|
+
countersBeforeAttempt[screenshotPath] = nameCacheCounter[screenshotPath];
|
|
224
|
+
}
|
|
225
|
+
return path__default["default"].join(IMAGE_SNAPSHOT_PREFIX, `${screenshotPath}_#${++nameCacheCounter[screenshotPath]}${FILE_SUFFIX.actual}.png`);
|
|
226
|
+
};
|
|
227
|
+
const screenshotPathRegex = new RegExp(`^([\\s\\S]+?)_#([0-9]+)(?:(?:\\${FILE_SUFFIX.diff})|(?:\\${FILE_SUFFIX.actual}))?\\.(?:png|PNG)$`);
|
|
228
|
+
const wasScreenshotUsed = imagePath => {
|
|
229
|
+
const matched = imagePath.match(screenshotPathRegex);
|
|
230
|
+
/* c8 ignore next */
|
|
231
|
+
if (!matched) return false;
|
|
232
|
+
const [, screenshotPath, numString] = matched;
|
|
233
|
+
const num = parseInt(numString);
|
|
234
|
+
/* c8 ignore next */
|
|
235
|
+
if (!screenshotPath || isNaN(num)) return false;
|
|
236
|
+
return screenshotPath in nameCacheCounter && num <= nameCacheCounter[screenshotPath];
|
|
237
|
+
};
|
|
238
|
+
const resetScreenshotNameCache = () => {
|
|
239
|
+
currentAttempt = undefined;
|
|
240
|
+
resetMap(nameCacheCounter);
|
|
241
|
+
resetMap(countersBeforeAttempt);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const {
|
|
245
|
+
addMetadata,
|
|
246
|
+
getMetadata
|
|
247
|
+
} = metaPngPkg__default["default"];
|
|
248
|
+
// version of images generated by this plugin
|
|
249
|
+
// bump only when there is a breaking change to:
|
|
250
|
+
// image comparison or image generation algorithms
|
|
251
|
+
const DIFF_IMAGES_VERSION = '1';
|
|
252
|
+
const addPNGMetadata = (config, png) => addMetadata(new Uint8Array(png), METADATA_KEY, JSON.stringify({
|
|
253
|
+
version: DIFF_IMAGES_VERSION,
|
|
254
|
+
testingType: config.testingType || 'e2e'
|
|
255
|
+
}) /* c8 ignore next */);
|
|
256
|
+
const getPNGMetadata = png => {
|
|
257
|
+
const metadataString = getMetadata(new Uint8Array(png), METADATA_KEY /* c8 ignore next */);
|
|
258
|
+
if (metadataString === undefined) return;
|
|
259
|
+
try {
|
|
260
|
+
return JSON.parse(metadataString);
|
|
261
|
+
} catch /* v8 ignore next 2 */{
|
|
262
|
+
return {
|
|
263
|
+
version: metadataString
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const isImageCurrentVersion = png => {
|
|
268
|
+
var _getPNGMetadata;
|
|
269
|
+
return ((_getPNGMetadata = getPNGMetadata(png)) == null ? void 0 : _getPNGMetadata.version) === DIFF_IMAGES_VERSION;
|
|
270
|
+
};
|
|
271
|
+
const isImageGeneratedByPlugin = png => !!getPNGMetadata(png /* c8 ignore next */);
|
|
272
|
+
const isImageOfTestType = (png, testingType) => {
|
|
273
|
+
var _getPNGMetadata2;
|
|
274
|
+
if (!isImageGeneratedByPlugin(png)) return false;
|
|
275
|
+
const imageTestingType = (_getPNGMetadata2 = getPNGMetadata(png /* c8 ignore next */)) == null ? void 0 : _getPNGMetadata2.testingType;
|
|
276
|
+
return imageTestingType === testingType || testingType === undefined;
|
|
277
|
+
};
|
|
278
|
+
const writePNG = (config, name, png) => fs__default["default"].writeFileSync(name, addPNGMetadata(config, png instanceof pngjs.PNG ? pngjs.PNG.sync.write(png) : png));
|
|
279
|
+
const inArea = (x, y, height, width) => y > height || x > width;
|
|
280
|
+
const fillSizeDifference = (image, width, height) => {
|
|
281
|
+
for (let y = 0; y < image.height; y++) {
|
|
282
|
+
for (let x = 0; x < image.width; x++) {
|
|
283
|
+
if (inArea(x, y, height, width)) {
|
|
284
|
+
const idx = image.width * y + x << 2;
|
|
285
|
+
image.data[idx] = 0;
|
|
286
|
+
image.data[idx + 1] = 0;
|
|
287
|
+
image.data[idx + 2] = 0;
|
|
288
|
+
image.data[idx + 3] = 64;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return image;
|
|
293
|
+
/* c8 ignore next */
|
|
294
|
+
};
|
|
295
|
+
const createImageResizer = (width, height) => source => {
|
|
296
|
+
const resized = new pngjs.PNG({
|
|
297
|
+
width,
|
|
298
|
+
height,
|
|
299
|
+
fill: true
|
|
300
|
+
});
|
|
301
|
+
pngjs.PNG.bitblt(source, resized, 0, 0, source.width, source.height, 0, 0);
|
|
302
|
+
return resized;
|
|
303
|
+
/* c8 ignore next */
|
|
304
|
+
};
|
|
305
|
+
const scaleImageAndWrite = async ({
|
|
306
|
+
scaleFactor,
|
|
307
|
+
path
|
|
308
|
+
}) => {
|
|
309
|
+
const imgBuffer = fs__default["default"].readFileSync(path);
|
|
310
|
+
if (scaleFactor === 1) return imgBuffer;
|
|
311
|
+
const rawImgNew = pngjs.PNG.sync.read(imgBuffer);
|
|
312
|
+
const newImageWidth = Math.ceil(rawImgNew.width * scaleFactor);
|
|
313
|
+
const newImageHeight = Math.ceil(rawImgNew.height * scaleFactor);
|
|
314
|
+
await sharp__default["default"](imgBuffer).resize(newImageWidth, newImageHeight).toFile(path);
|
|
315
|
+
return fs__default["default"].readFileSync(path);
|
|
316
|
+
};
|
|
317
|
+
const alignImagesToSameSize = (firstImage, secondImage) => {
|
|
318
|
+
const firstImageWidth = firstImage.width;
|
|
319
|
+
const firstImageHeight = firstImage.height;
|
|
320
|
+
const secondImageWidth = secondImage.width;
|
|
321
|
+
const secondImageHeight = secondImage.height;
|
|
322
|
+
const resizeToSameSize = createImageResizer(Math.max(firstImageWidth, secondImageWidth), Math.max(firstImageHeight, secondImageHeight));
|
|
323
|
+
const resizedFirst = resizeToSameSize(firstImage);
|
|
324
|
+
const resizedSecond = resizeToSameSize(secondImage);
|
|
325
|
+
return [fillSizeDifference(resizedFirst, firstImageWidth, firstImageHeight), fillSizeDifference(resizedSecond, secondImageWidth, secondImageHeight)];
|
|
326
|
+
};
|
|
327
|
+
const cleanupUnused = config => {
|
|
328
|
+
glob.globSync('**/*.png', {
|
|
329
|
+
cwd: config.projectRoot,
|
|
330
|
+
ignore: 'node_modules/**/*'
|
|
331
|
+
}).forEach(pngPath => {
|
|
332
|
+
const absolutePath = path__default["default"].join(config.projectRoot, pngPath);
|
|
333
|
+
if (!wasScreenshotUsed(pngPath) && isImageOfTestType(fs__default["default"].readFileSync(absolutePath), config.testingType)) {
|
|
334
|
+
fs__default["default"].unlinkSync(absolutePath);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
let pendingDiffs = [];
|
|
340
|
+
const round = n => Math.ceil(n * 1000) / 1000;
|
|
341
|
+
const unlinkSyncSafe = path => fs__default["default"].existsSync(path) && fs__default["default"].unlinkSync(path);
|
|
342
|
+
// `.diff.png` sibling of an `.actual.png`; null when the name has no `.actual`
|
|
343
|
+
// suffix (only happens with hand-picked paths), so nothing else gets deleted
|
|
344
|
+
const diffPathFor = actualPath => {
|
|
345
|
+
const diffPath = actualPath.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff);
|
|
346
|
+
return diffPath === actualPath ? null : diffPath;
|
|
347
|
+
};
|
|
348
|
+
const removeStaleDiff = actualPath => {
|
|
349
|
+
const diffPath = diffPathFor(actualPath);
|
|
350
|
+
if (diffPath) unlinkSyncSafe(diffPath);
|
|
351
|
+
};
|
|
352
|
+
const getScreenshotPathInfoTask = cfg => {
|
|
353
|
+
const screenshotPath = generateScreenshotPath(cfg);
|
|
354
|
+
return {
|
|
355
|
+
screenshotPath,
|
|
356
|
+
title: path__default["default"].basename(screenshotPath, '.png')
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
const cleanupImagesTask = config => {
|
|
360
|
+
if (getPluginConfig(config, 'pluginVisualRegressionCleanupUnusedImages')) {
|
|
361
|
+
cleanupUnused(config);
|
|
362
|
+
}
|
|
363
|
+
resetScreenshotNameCache();
|
|
364
|
+
return null;
|
|
365
|
+
};
|
|
366
|
+
const approveImageTask = async ({
|
|
367
|
+
img,
|
|
368
|
+
imgOld
|
|
369
|
+
}) => {
|
|
370
|
+
const oldImg = imgOld != null ? imgOld : img.replace(FILE_SUFFIX.actual, '');
|
|
371
|
+
unlinkSyncSafe(oldImg);
|
|
372
|
+
const diffImg = img.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff);
|
|
373
|
+
unlinkSyncSafe(diffImg);
|
|
374
|
+
await moveFile.moveFile(img, oldImg);
|
|
375
|
+
return null;
|
|
376
|
+
};
|
|
377
|
+
const compareImagesTask = async (cypressConfig, cfg) => {
|
|
378
|
+
const messages = [];
|
|
379
|
+
// Stamp the screenshot with plugin metadata exactly once, so that every file
|
|
380
|
+
// derived from it (baseline via moveFile, kept .actual.png, manual rename)
|
|
381
|
+
// carries FRSOURCE_CPVRD_V and won't be silently rewritten on the next run.
|
|
382
|
+
const stampedImgNew = addPNGMetadata(cypressConfig, await scaleImageAndWrite({
|
|
383
|
+
scaleFactor: cfg.scaleFactor,
|
|
384
|
+
path: cfg.imgNew
|
|
385
|
+
}));
|
|
386
|
+
fs__default["default"].writeFileSync(cfg.imgNew, stampedImgNew);
|
|
387
|
+
const rawImgNewBuffer = Buffer.from(stampedImgNew);
|
|
388
|
+
let imgDiff;
|
|
389
|
+
let imgNewBase64, imgOldBase64, imgDiffBase64;
|
|
390
|
+
let error = false;
|
|
391
|
+
if (fs__default["default"].existsSync(cfg.imgOld) && cfg.updateImages !== true) {
|
|
392
|
+
const rawImgNew = pngjs.PNG.sync.read(rawImgNewBuffer);
|
|
393
|
+
const rawImgOldBuffer = fs__default["default"].readFileSync(cfg.imgOld);
|
|
394
|
+
const rawImgOld = pngjs.PNG.sync.read(rawImgOldBuffer);
|
|
395
|
+
const isImgSizeDifferent = rawImgNew.height !== rawImgOld.height || rawImgNew.width !== rawImgOld.width;
|
|
396
|
+
const [imgNew, imgOld] = isImgSizeDifferent ? alignImagesToSameSize(rawImgNew, rawImgOld) : [rawImgNew, rawImgOld];
|
|
397
|
+
const {
|
|
398
|
+
width,
|
|
399
|
+
height
|
|
400
|
+
} = imgNew;
|
|
401
|
+
const diff = new pngjs.PNG({
|
|
402
|
+
width,
|
|
403
|
+
height
|
|
404
|
+
});
|
|
405
|
+
const diffConfig = Object.assign({
|
|
406
|
+
includeAA: true
|
|
407
|
+
}, cfg.diffConfig);
|
|
408
|
+
const diffPixels = pixelmatch__default["default"](new Uint8Array(imgNew.data), new Uint8Array(imgOld.data), diff.data, width, height, diffConfig);
|
|
409
|
+
imgDiff = diffPixels / (width * height);
|
|
410
|
+
if (isImgSizeDifferent) {
|
|
411
|
+
messages.push(`Warning: Images size mismatch - new screenshot is ${rawImgNew.width}px by ${rawImgNew.height}px while old one is ${rawImgOld.width}px by ${rawImgOld.height} (width x height).`);
|
|
412
|
+
}
|
|
413
|
+
if (imgDiff > cfg.maxDiffThreshold) {
|
|
414
|
+
messages.unshift(`Image diff factor (${round(imgDiff * 100)}%) is bigger than maximum threshold option ${round(cfg.maxDiffThreshold * 100)}%.`);
|
|
415
|
+
error = true;
|
|
416
|
+
}
|
|
417
|
+
const diffBuffer = pngjs.PNG.sync.write(diff);
|
|
418
|
+
imgNewBase64 = pngjs.PNG.sync.write(imgNew).toString('base64');
|
|
419
|
+
imgDiffBase64 = diffBuffer.toString('base64');
|
|
420
|
+
imgOldBase64 = pngjs.PNG.sync.write(imgOld).toString('base64');
|
|
421
|
+
if (error && cfg.updateImages === 'failures-only') {
|
|
422
|
+
await moveFile.moveFile(cfg.imgNew, cfg.imgOld);
|
|
423
|
+
// a diff image left by an earlier failed attempt is stale now
|
|
424
|
+
removeStaleDiff(cfg.imgNew);
|
|
425
|
+
error = false;
|
|
426
|
+
messages[0] = messages[0].replace('is bigger than maximum threshold option', 'was bigger than maximum threshold option (baseline updated):');
|
|
427
|
+
} else if (error) {
|
|
428
|
+
writePNG(cypressConfig, cfg.imgNew.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff), diffBuffer);
|
|
429
|
+
} else {
|
|
430
|
+
removeStaleDiff(cfg.imgNew);
|
|
431
|
+
if (rawImgOld && !isImageCurrentVersion(rawImgOldBuffer)) {
|
|
432
|
+
await moveFile.moveFile(cfg.imgNew, cfg.imgOld);
|
|
433
|
+
} else {
|
|
434
|
+
// don't overwrite file if it's the same (imgDiff < cfg.maxDiffThreshold && !isImgSizeDifferent)
|
|
435
|
+
fs__default["default"].unlinkSync(cfg.imgNew);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
// there is no "old screenshot" or screenshots should be immediately updated
|
|
440
|
+
imgDiff = 0;
|
|
441
|
+
imgNewBase64 = '';
|
|
442
|
+
imgDiffBase64 = '';
|
|
443
|
+
imgOldBase64 = '';
|
|
444
|
+
if (cfg.createMissingImages) {
|
|
445
|
+
await moveFile.moveFile(cfg.imgNew, cfg.imgOld);
|
|
446
|
+
removeStaleDiff(cfg.imgNew);
|
|
447
|
+
} else {
|
|
448
|
+
error = true;
|
|
449
|
+
messages.unshift(`Baseline image is missing at path: "${cfg.imgOld}". Provide a baseline image or enable "createMissingImages" option in plugin configuration.`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (typeof imgDiff !== 'undefined') {
|
|
453
|
+
if (!error) {
|
|
454
|
+
messages.unshift(`Image diff factor (${round(imgDiff * 100)}%) is within boundaries of maximum threshold option ${round(cfg.maxDiffThreshold * 100)}%.`);
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
error,
|
|
458
|
+
message: messages.join('\n'),
|
|
459
|
+
imgDiff,
|
|
460
|
+
imgNewBase64,
|
|
461
|
+
imgDiffBase64,
|
|
462
|
+
imgOldBase64,
|
|
463
|
+
maxDiffThreshold: cfg.maxDiffThreshold
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/* c8 ignore next */
|
|
467
|
+
return null;
|
|
468
|
+
};
|
|
469
|
+
const doesFileExistTask = ({
|
|
470
|
+
path
|
|
471
|
+
}) => fs__default["default"].existsSync(path);
|
|
472
|
+
const processImgPathTask = ({
|
|
473
|
+
path
|
|
474
|
+
}) => path;
|
|
475
|
+
const recordPendingDiffTask = record => {
|
|
476
|
+
pendingDiffs.push(record);
|
|
477
|
+
return pendingDiffs.filter(d => !d.passed).length;
|
|
478
|
+
};
|
|
479
|
+
const getPendingDiffsTask = () => [...pendingDiffs];
|
|
480
|
+
const clearPendingDiffsTask = () => {
|
|
481
|
+
pendingDiffs = [];
|
|
482
|
+
return null;
|
|
483
|
+
};
|
|
484
|
+
/* c8 ignore start */
|
|
485
|
+
const initTaskHook = config => ({
|
|
486
|
+
[TASK.getScreenshotPathInfo]: getScreenshotPathInfoTask,
|
|
487
|
+
[TASK.cleanupImages]: cleanupImagesTask.bind(undefined, config),
|
|
488
|
+
[TASK.doesFileExist]: doesFileExistTask,
|
|
489
|
+
[TASK.approveImage]: approveImageTask,
|
|
490
|
+
[TASK.compareImages]: compareImagesTask.bind(undefined, config),
|
|
491
|
+
[TASK.processImgPath]: processImgPathTask,
|
|
492
|
+
[TASK.recordPendingDiff]: recordPendingDiffTask,
|
|
493
|
+
[TASK.getPendingDiffs]: getPendingDiffsTask,
|
|
494
|
+
[TASK.clearPendingDiffs]: clearPendingDiffsTask
|
|
495
|
+
});
|
|
496
|
+
/* c8 ignore stop */
|
|
497
|
+
|
|
498
|
+
const MIMIC_ROOT_WIN_REGEX = new RegExp(`^${PATH_VARIABLES.winSystemRootPath}\\${path__default["default"].sep}([A-Z])\\${path__default["default"].sep}`);
|
|
499
|
+
const MIMIC_ROOT_UNIX_REGEX = new RegExp(`^${PATH_VARIABLES.unixSystemRootPath}\\${path__default["default"].sep}`);
|
|
500
|
+
const getConfigVariableOrThrow = (config, name) => {
|
|
501
|
+
if (config[name]) {
|
|
502
|
+
return config[name];
|
|
503
|
+
}
|
|
504
|
+
/* c8 ignore start */
|
|
505
|
+
throw `[@pixsame/cypress-plugin-visual-regression-diff] CypressConfig.${name} cannot be missing or \`false\`!`;
|
|
506
|
+
};
|
|
507
|
+
/* c8 ignore stop */
|
|
508
|
+
const parseAbsolutePath = ({
|
|
509
|
+
screenshotPath,
|
|
510
|
+
projectRoot
|
|
511
|
+
}) => {
|
|
512
|
+
let newAbsolutePath;
|
|
513
|
+
const matchedMimicingWinRoot = screenshotPath.match(MIMIC_ROOT_WIN_REGEX);
|
|
514
|
+
const matchedMimicingUnixRoot = screenshotPath.match(MIMIC_ROOT_UNIX_REGEX);
|
|
515
|
+
if (matchedMimicingWinRoot && matchedMimicingWinRoot[1]) {
|
|
516
|
+
const driveLetter = matchedMimicingWinRoot[1];
|
|
517
|
+
newAbsolutePath = path__default["default"].join(`${driveLetter}:\\`, screenshotPath.substring(matchedMimicingWinRoot[0].length));
|
|
518
|
+
} else if (matchedMimicingUnixRoot) {
|
|
519
|
+
newAbsolutePath = path__default["default"].sep + screenshotPath.substring(matchedMimicingUnixRoot[0].length);
|
|
520
|
+
} else {
|
|
521
|
+
newAbsolutePath = path__default["default"].join(projectRoot, screenshotPath);
|
|
522
|
+
}
|
|
523
|
+
return path__default["default"].normalize(newAbsolutePath);
|
|
524
|
+
};
|
|
525
|
+
const initAfterScreenshotHook = config => details => {
|
|
526
|
+
var _details$name;
|
|
527
|
+
// it's not a screenshot generated by FRSOURCE Cypress Plugin Visual Regression Diff
|
|
528
|
+
/* c8 ignore start */
|
|
529
|
+
if (((_details$name = details.name) == null ? void 0 : _details$name.indexOf(IMAGE_SNAPSHOT_PREFIX)) !== 0) return;
|
|
530
|
+
/* c8 ignore stop */
|
|
531
|
+
const screenshotsFolder = getConfigVariableOrThrow(config, 'screenshotsFolder');
|
|
532
|
+
const screenshotPath = details.name.substring(IMAGE_SNAPSHOT_PREFIX.length + path__default["default"].sep.length);
|
|
533
|
+
const newAbsolutePath = parseAbsolutePath({
|
|
534
|
+
screenshotPath,
|
|
535
|
+
projectRoot: config.projectRoot
|
|
536
|
+
});
|
|
537
|
+
return (async () => {
|
|
538
|
+
await moveFile.moveFile(details.path, newAbsolutePath);
|
|
539
|
+
await fs.promises.rm(path__default["default"].join(screenshotsFolder, IMAGE_SNAPSHOT_PREFIX), {
|
|
540
|
+
recursive: true,
|
|
541
|
+
force: true
|
|
542
|
+
});
|
|
543
|
+
return {
|
|
544
|
+
path: newAbsolutePath
|
|
545
|
+
};
|
|
546
|
+
})();
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
/* c8 ignore start */
|
|
550
|
+
const initForceDeviceScaleFactor = on => {
|
|
551
|
+
// based on https://github.com/cypress-io/cypress/issues/2102#issuecomment-521299946
|
|
552
|
+
on('before:browser:launch', (browser, launchOptions) => {
|
|
553
|
+
if (browser.name === 'chrome' || browser.name === 'chromium') {
|
|
554
|
+
launchOptions.args.push('--force-device-scale-factor=1');
|
|
555
|
+
launchOptions.args.push('--high-dpi-support=1');
|
|
556
|
+
return launchOptions;
|
|
557
|
+
} else if (browser.name === 'electron' && browser.isHeaded) {
|
|
558
|
+
// eslint-disable-next-line no-console
|
|
559
|
+
console.log("There isn't currently a way of setting the device scale factor in Cypress when running headed electron so we disable the image regression commands.");
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
/* c8 ignore stop */
|
|
564
|
+
const initPlugin = (on, config) => {
|
|
565
|
+
/* c8 ignore start */
|
|
566
|
+
if (getPluginConfig(config, 'pluginVisualRegressionForceDeviceScaleFactor') !== false) {
|
|
567
|
+
initForceDeviceScaleFactor(on);
|
|
568
|
+
}
|
|
569
|
+
/* c8 ignore stop */
|
|
570
|
+
on('task', initTaskHook(config));
|
|
571
|
+
on('after:screenshot', initAfterScreenshotHook(config));
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
exports.initPlugin = initPlugin;
|
|
575
|
+
//# sourceMappingURL=plugins.js.map
|