@testivai/witness 1.3.1 → 1.4.0
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/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +3 -1
- package/dist/commands/report.js.map +1 -1
- package/dist/config/local-config.d.ts +17 -0
- package/dist/config/local-config.d.ts.map +1 -1
- package/dist/config/local-config.js.map +1 -1
- package/dist/diff/diff.d.ts.map +1 -1
- package/dist/diff/diff.js +13 -2
- package/dist/diff/diff.js.map +1 -1
- package/dist/diff/mask.d.ts +98 -0
- package/dist/diff/mask.d.ts.map +1 -0
- package/dist/diff/mask.js +209 -0
- package/dist/diff/mask.js.map +1 -0
- package/dist/diff/regions.d.ts +17 -4
- package/dist/diff/regions.d.ts.map +1 -1
- package/dist/diff/regions.js +70 -5
- package/dist/diff/regions.js.map +1 -1
- package/dist/diff/types.d.ts +13 -0
- package/dist/diff/types.d.ts.map +1 -1
- package/dist/report/compare.d.ts +9 -0
- package/dist/report/compare.d.ts.map +1 -1
- package/dist/report/compare.js +72 -12
- package/dist/report/compare.js.map +1 -1
- package/dist/report/generator.d.ts +12 -1
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +36 -15
- package/dist/report/generator.js.map +1 -1
- package/dist/report/results.d.ts +12 -0
- package/dist/report/results.d.ts.map +1 -1
- package/dist/report/template.d.ts.map +1 -1
- package/dist/report/template.js +74 -0
- package/dist/report/template.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,SA8CtB,CAAC"}
|
package/dist/commands/report.js
CHANGED
|
@@ -34,7 +34,9 @@ exports.reportCommand = new commander_1.Command('report')
|
|
|
34
34
|
reportDir: config.reportDir ?? 'visual-report',
|
|
35
35
|
threshold: config.threshold,
|
|
36
36
|
autoOpen: options.open ?? config.autoOpen,
|
|
37
|
-
|
|
37
|
+
// NOTE: `version` here is the results.json SCHEMA version (a public
|
|
38
|
+
// contract), not the package version — conflating them shipped
|
|
39
|
+
// results.json files labeled 1.3.1. Omitted → schema default.
|
|
38
40
|
});
|
|
39
41
|
const { summary } = report;
|
|
40
42
|
console.log();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,mDAAqD;AACrD,yDAAyD;AACzD,4CAAyC;AAEzC;;;;;;;;;;GAUG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CACV,oFAAoF;IAClF,8FAA8F,CACjG;KACA,MAAM,CAAC,gBAAgB,EAAE,0EAA0E,CAAC;KACpG,MAAM,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAiD,EAAE,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,WAAW,CAAC,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAA,0BAAc,EAAC;YAC5B,WAAW;YACX,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ;YACzC,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,mDAAqD;AACrD,yDAAyD;AACzD,4CAAyC;AAEzC;;;;;;;;;;GAUG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CACV,oFAAoF;IAClF,8FAA8F,CACjG;KACA,MAAM,CAAC,gBAAgB,EAAE,0EAA0E,CAAC;KACpG,MAAM,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAiD,EAAE,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,WAAW,CAAC,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAA,0BAAc,EAAC;YAC5B,WAAW;YACX,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ;YACzC,oEAAoE;YACpE,+DAA+D;YAC/D,8DAA8D;SAE/D,CAAC,CAAC;QAEH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC3B,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CACT,YAAY,OAAO,CAAC,KAAK,gBAAgB,OAAO,CAAC,MAAM,iBAAiB,OAAO,CAAC,OAAO,aAAa,OAAO,CAAC,YAAY,EAAE,CAC3H,CAAC;QACF,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC,CAAC;QAC9G,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,IAAI,eAAe,yDAAyD,CAAC,CACtH,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;QAC3D,IAAI,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -26,6 +26,23 @@ export interface LocalConfig {
|
|
|
26
26
|
* Example: ["[data-testivai-ignore]", ".version-badge", "#live-chat"]
|
|
27
27
|
*/
|
|
28
28
|
ignoreSelectors?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Masks: page areas excluded from the pixel diff and hatched in the
|
|
31
|
+
* report (auditable — never silent). Each entry is a CSS selector
|
|
32
|
+
* string (geometry captured by the adapter at capture time) or a
|
|
33
|
+
* geometric region: px numbers, 0–1 ratios, "NN%" strings, or a
|
|
34
|
+
* single-edge shorthand like { "top": 24 }.
|
|
35
|
+
*/
|
|
36
|
+
mask?: Array<string | Record<string, number | string>>;
|
|
37
|
+
/**
|
|
38
|
+
* Diff clustering tunables: minSize = noise floor in changed pixels
|
|
39
|
+
* (default 10), mergeDistance = px gap within which nearby regions
|
|
40
|
+
* merge into one (default 12).
|
|
41
|
+
*/
|
|
42
|
+
diffRegions?: {
|
|
43
|
+
minSize?: number;
|
|
44
|
+
mergeDistance?: number;
|
|
45
|
+
};
|
|
29
46
|
/**
|
|
30
47
|
* Pass tolerance: snapshots whose diff percentage (0-100) is at or below
|
|
31
48
|
* this value are reported as passed instead of changed. Default: 0 —
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAgBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAiBhE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC/B,WAAW,CAab;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAYxD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C"}
|
|
1
|
+
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAgBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAiBhE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC/B,WAAW,CAab;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAYxD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyGH,0CAiBC;AAUD,kDAgBC;AAKD,8CAEC;AAMD,kCAYC;AAKD,sCAEC;AAKD,4CAEC;AAzLD,uCAAyB;AACzB,2CAA6B;AAkF7B,MAAM,cAAc,GAAgB;IAClC,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,KAAK;IACjB,cAAc,EAAE,CAAC;IACjB,aAAa,EAAE,KAAK;IACpB,mBAAmB,EAAE,CAAC;IACtB,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,MAAM,eAAe,GAAG,aAAa,CAAC;AACtC,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,WAAmB;IACjD,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO;YACL,GAAG,cAAc;YACjB,GAAG,MAAM;SACV,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;QAC5C,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CACjC,WAAmB,EACnB,SAAgC;IAEhC,MAAM,MAAM,GAAgB;QAC1B,GAAG,cAAc;QACjB,GAAG,SAAS;KACb,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACrD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAErE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,WAAmB;IACnD,OAAO,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,WAAmB;IAC7C,wEAAwE;IACxE,yEAAyE;IACzE,6DAA6D;IAC7D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC1C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,WAAmB;IAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;AAC7D,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB;IAC9B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC"}
|
package/dist/diff/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAsBhE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,UAAU,GAAG,iBAAiB,EACzC,UAAU,EAAE,UAAU,GAAG,iBAAiB,EAC1C,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,UAAU,CA4GZ"}
|
package/dist/diff/diff.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.diff = diff;
|
|
14
14
|
const ignore_1 = require("./ignore");
|
|
15
|
+
const mask_1 = require("./mask");
|
|
15
16
|
const resize_1 = require("./resize");
|
|
16
17
|
const regions_1 = require("./regions");
|
|
17
18
|
const minimap_1 = require("./minimap");
|
|
@@ -89,6 +90,12 @@ function diff(baseline8, candidate8, diff8, width, height, options = {}) {
|
|
|
89
90
|
effectiveCandidate = new Uint8ClampedArray(effectiveCandidate);
|
|
90
91
|
(0, ignore_1.applyIgnoreRegions)(effectiveBaseline, effectiveCandidate, effectiveWidth, effectiveHeight, opts.ignoreRegions);
|
|
91
92
|
}
|
|
93
|
+
// ── Masks: equalize before diffing, hatch the output after ─────────────
|
|
94
|
+
const masks = opts.masks ?? [];
|
|
95
|
+
if (masks.length > 0) {
|
|
96
|
+
effectiveBaseline = new Uint8ClampedArray(effectiveBaseline);
|
|
97
|
+
(0, mask_1.applyMaskRects)(effectiveBaseline, effectiveCandidate, effectiveWidth, effectiveHeight, masks);
|
|
98
|
+
}
|
|
92
99
|
// ── Core Pbd diff loop (basic mode — pixel-level YIQ comparison) ──────
|
|
93
100
|
const raw = pbdDiffCore(effectiveBaseline, effectiveCandidate, diff8, effectiveWidth, effectiveHeight, opts.threshold, opts.cumulatedThreshold, opts.enableMinimap);
|
|
94
101
|
// ── Compute extended result ─────────────────────────────────────────────
|
|
@@ -106,9 +113,13 @@ function diff(baseline8, candidate8, diff8, width, height, options = {}) {
|
|
|
106
113
|
if (sizeMismatch) {
|
|
107
114
|
result.sizeMismatch = sizeMismatch;
|
|
108
115
|
}
|
|
109
|
-
// ── Region detection
|
|
116
|
+
// ── Region detection (before hatching — hatch pixels must not cluster) ──
|
|
110
117
|
if (opts.detectRegions && !isIdentical) {
|
|
111
|
-
result.regions = (0, regions_1.detectRegions)(diff8, effectiveWidth, effectiveHeight);
|
|
118
|
+
result.regions = (0, regions_1.detectRegions)(diff8, effectiveWidth, effectiveHeight, opts.regionOptions ?? {});
|
|
119
|
+
}
|
|
120
|
+
// ── Hatch masked areas in the diff output (auditable, never silent) ─────
|
|
121
|
+
if (masks.length > 0) {
|
|
122
|
+
(0, mask_1.hatchMaskRects)(diff8, effectiveWidth, effectiveHeight, masks);
|
|
112
123
|
}
|
|
113
124
|
return result;
|
|
114
125
|
}
|
package/dist/diff/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestivAI Diff Engine — Masking DSL
|
|
3
|
+
*
|
|
4
|
+
* Masks exclude page areas from the pixel diff while staying auditable:
|
|
5
|
+
* every masked region is hatched in the diff output and listed in the
|
|
6
|
+
* report — silent masking is not acceptable.
|
|
7
|
+
*
|
|
8
|
+
* Two shapes:
|
|
9
|
+
* - CSS selector strings ("#cookie-banner"): geometry is captured by the
|
|
10
|
+
* adapter at capture time (metadata `maskRects`, via
|
|
11
|
+
* getBoundingClientRect) because the DOM snapshot carries no layout.
|
|
12
|
+
* Without captured rects the mask degrades to a warning — never a crash.
|
|
13
|
+
* - Geometric regions: px numbers, 0–1 ratio numbers, "NN%" strings, or a
|
|
14
|
+
* single-edge shorthand like { top: 24 } (full-width/height strip).
|
|
15
|
+
*/
|
|
16
|
+
export type MaskSpec = string | Record<string, number | string>;
|
|
17
|
+
/** A selector's geometry as captured by the adapter (metadata maskRects). */
|
|
18
|
+
export interface CapturedMaskRect {
|
|
19
|
+
selector: string;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
export interface MaskSource {
|
|
26
|
+
type: 'selector' | 'region' | 'edge';
|
|
27
|
+
/** The original spec (selector string or the raw region object). */
|
|
28
|
+
spec: MaskSpec;
|
|
29
|
+
/** Where the mask came from. */
|
|
30
|
+
origin: 'config' | 'call';
|
|
31
|
+
}
|
|
32
|
+
/** A mask resolved to concrete pixels on a specific image. */
|
|
33
|
+
export interface MaskRect {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
source: MaskSource;
|
|
39
|
+
}
|
|
40
|
+
type Parsed = {
|
|
41
|
+
type: 'selector';
|
|
42
|
+
selector: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'region';
|
|
45
|
+
x: Dim;
|
|
46
|
+
y: Dim;
|
|
47
|
+
width: Dim;
|
|
48
|
+
height: Dim;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'edge';
|
|
51
|
+
edge: 'top' | 'right' | 'bottom' | 'left';
|
|
52
|
+
size: Dim;
|
|
53
|
+
};
|
|
54
|
+
/** A dimension before resolution: px count or ratio of the image size. */
|
|
55
|
+
type Dim = {
|
|
56
|
+
kind: 'px';
|
|
57
|
+
value: number;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'ratio';
|
|
60
|
+
value: number;
|
|
61
|
+
};
|
|
62
|
+
export type ParseResult = {
|
|
63
|
+
ok: true;
|
|
64
|
+
mask: Parsed;
|
|
65
|
+
} | {
|
|
66
|
+
ok: false;
|
|
67
|
+
error: string;
|
|
68
|
+
};
|
|
69
|
+
/** Validate a single mask spec. Never throws. */
|
|
70
|
+
export declare function parseMaskSpec(spec: unknown): ParseResult;
|
|
71
|
+
export interface ResolvedMasks {
|
|
72
|
+
rects: MaskRect[];
|
|
73
|
+
/** Human-readable skips (e.g. selector without captured geometry). */
|
|
74
|
+
warnings: string[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve mask specs to concrete pixel rects for one image.
|
|
78
|
+
*
|
|
79
|
+
* @param specs Mask specs from config and/or per-call options
|
|
80
|
+
* @param imgWidth Image width in px
|
|
81
|
+
* @param imgHeight Image height in px
|
|
82
|
+
* @param capturedRects Selector geometry captured by the adapter
|
|
83
|
+
* @param origin Where these specs came from (for the audit trail)
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveMasks(specs: MaskSpec[], imgWidth: number, imgHeight: number, capturedRects?: CapturedMaskRect[], origin?: 'config' | 'call'): ResolvedMasks;
|
|
86
|
+
/**
|
|
87
|
+
* Equalize masked pixels (copy candidate → baseline) so the diff loop sees
|
|
88
|
+
* no difference there. Mutates baseline in place; callers clone first.
|
|
89
|
+
*/
|
|
90
|
+
export declare function applyMaskRects(baseline: Uint8Array | Uint8ClampedArray, candidate: Uint8Array | Uint8ClampedArray, width: number, height: number, rects: MaskRect[]): void;
|
|
91
|
+
/**
|
|
92
|
+
* Draw a diagonal hatch pattern over masked areas of the diff image so
|
|
93
|
+
* masking is always visible in review. Only pixels inside mask rects are
|
|
94
|
+
* touched.
|
|
95
|
+
*/
|
|
96
|
+
export declare function hatchMaskRects(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number, rects: MaskRect[]): void;
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../../src/diff/mask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEhE,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,oEAAoE;IACpE,IAAI,EAAE,QAAQ,CAAC;IACf,gCAAgC;IAChC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC3B;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,KAAK,MAAM,GACP;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,CAAC;AAE3E,0EAA0E;AAC1E,KAAK,GAAG,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBjC,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CA4DxD;AAkBD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,gBAAgB,EAAE,EAClC,MAAM,GAAE,QAAQ,GAAG,MAAiB,GACnC,aAAa,CAqDf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,GAAG,iBAAiB,EACxC,SAAS,EAAE,UAAU,GAAG,iBAAiB,EACzC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EAAE,GAChB,IAAI,CAaN;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EAAE,GAChB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TestivAI Diff Engine — Masking DSL
|
|
4
|
+
*
|
|
5
|
+
* Masks exclude page areas from the pixel diff while staying auditable:
|
|
6
|
+
* every masked region is hatched in the diff output and listed in the
|
|
7
|
+
* report — silent masking is not acceptable.
|
|
8
|
+
*
|
|
9
|
+
* Two shapes:
|
|
10
|
+
* - CSS selector strings ("#cookie-banner"): geometry is captured by the
|
|
11
|
+
* adapter at capture time (metadata `maskRects`, via
|
|
12
|
+
* getBoundingClientRect) because the DOM snapshot carries no layout.
|
|
13
|
+
* Without captured rects the mask degrades to a warning — never a crash.
|
|
14
|
+
* - Geometric regions: px numbers, 0–1 ratio numbers, "NN%" strings, or a
|
|
15
|
+
* single-edge shorthand like { top: 24 } (full-width/height strip).
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.parseMaskSpec = parseMaskSpec;
|
|
19
|
+
exports.resolveMasks = resolveMasks;
|
|
20
|
+
exports.applyMaskRects = applyMaskRects;
|
|
21
|
+
exports.hatchMaskRects = hatchMaskRects;
|
|
22
|
+
const EDGES = ['top', 'right', 'bottom', 'left'];
|
|
23
|
+
const RECT_KEYS = ['x', 'y', 'width', 'height'];
|
|
24
|
+
/**
|
|
25
|
+
* Parse one dimension value: number > =1 → px, 0–1 → ratio, "NN%" → ratio.
|
|
26
|
+
* Returns null for anything else.
|
|
27
|
+
*/
|
|
28
|
+
function parseDim(value, allowNegative = false) {
|
|
29
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
30
|
+
if (!allowNegative && value < 0)
|
|
31
|
+
return null;
|
|
32
|
+
if (value > 0 && value < 1)
|
|
33
|
+
return { kind: 'ratio', value };
|
|
34
|
+
return { kind: 'px', value };
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
const m = /^(\d+(?:\.\d+)?)%$/.exec(value.trim());
|
|
38
|
+
if (!m)
|
|
39
|
+
return null;
|
|
40
|
+
const pct = Number(m[1]);
|
|
41
|
+
return { kind: 'ratio', value: pct / 100 };
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Validate a single mask spec. Never throws. */
|
|
46
|
+
function parseMaskSpec(spec) {
|
|
47
|
+
if (typeof spec === 'string') {
|
|
48
|
+
if (spec.trim().length === 0) {
|
|
49
|
+
return { ok: false, error: 'mask: selector must be a non-empty string' };
|
|
50
|
+
}
|
|
51
|
+
return { ok: true, mask: { type: 'selector', selector: spec.trim() } };
|
|
52
|
+
}
|
|
53
|
+
if (spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: 'mask: expected a CSS selector string or a region object like { x, y, width, height } or { top: 24 }',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const obj = spec;
|
|
60
|
+
const presentEdges = EDGES.filter((e) => obj[e] !== undefined);
|
|
61
|
+
const presentRect = RECT_KEYS.filter((k) => obj[k] !== undefined);
|
|
62
|
+
if (presentEdges.length > 0) {
|
|
63
|
+
if (presentRect.length > 0) {
|
|
64
|
+
return { ok: false, error: 'mask: cannot mix edge shorthands with x/y/width/height' };
|
|
65
|
+
}
|
|
66
|
+
if (presentEdges.length > 1) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
error: `mask: use exactly one edge per shorthand (got ${presentEdges.join(', ')}) — define separate masks instead`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const edge = presentEdges[0];
|
|
73
|
+
const size = parseDim(obj[edge]);
|
|
74
|
+
if (!size || size.value < 0) {
|
|
75
|
+
return { ok: false, error: `mask: { ${edge}: … } must be a positive number of px, a 0–1 ratio, or "NN%"` };
|
|
76
|
+
}
|
|
77
|
+
return { ok: true, mask: { type: 'edge', edge, size } };
|
|
78
|
+
}
|
|
79
|
+
if (presentRect.length !== RECT_KEYS.length) {
|
|
80
|
+
const missing = RECT_KEYS.filter((k) => obj[k] === undefined);
|
|
81
|
+
return { ok: false, error: `mask: region object is missing ${missing.join(', ')}` };
|
|
82
|
+
}
|
|
83
|
+
const dims = {};
|
|
84
|
+
for (const key of RECT_KEYS) {
|
|
85
|
+
// x/y may legitimately be 0; width/height must end up positive after
|
|
86
|
+
// resolution but a 0-width spec is caught by the zero-area drop.
|
|
87
|
+
const dim = parseDim(obj[key]);
|
|
88
|
+
if (!dim) {
|
|
89
|
+
return { ok: false, error: `mask: ${key} must be a number of px, a 0–1 ratio, or "NN%" (got ${JSON.stringify(obj[key])})` };
|
|
90
|
+
}
|
|
91
|
+
if (dim.value < 0) {
|
|
92
|
+
return { ok: false, error: `mask: ${key} cannot be negative` };
|
|
93
|
+
}
|
|
94
|
+
dims[key] = dim;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
mask: { type: 'region', x: dims.x, y: dims.y, width: dims.width, height: dims.height },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function resolveDim(dim, total) {
|
|
102
|
+
return dim.kind === 'ratio' ? Math.round(dim.value * total) : Math.round(dim.value);
|
|
103
|
+
}
|
|
104
|
+
function clampRect(x, y, width, height, imgWidth, imgHeight) {
|
|
105
|
+
const x0 = Math.max(0, Math.min(x, imgWidth));
|
|
106
|
+
const y0 = Math.max(0, Math.min(y, imgHeight));
|
|
107
|
+
const x1 = Math.max(0, Math.min(x + width, imgWidth));
|
|
108
|
+
const y1 = Math.max(0, Math.min(y + height, imgHeight));
|
|
109
|
+
if (x1 <= x0 || y1 <= y0)
|
|
110
|
+
return null; // zero-area after clamping
|
|
111
|
+
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve mask specs to concrete pixel rects for one image.
|
|
115
|
+
*
|
|
116
|
+
* @param specs Mask specs from config and/or per-call options
|
|
117
|
+
* @param imgWidth Image width in px
|
|
118
|
+
* @param imgHeight Image height in px
|
|
119
|
+
* @param capturedRects Selector geometry captured by the adapter
|
|
120
|
+
* @param origin Where these specs came from (for the audit trail)
|
|
121
|
+
*/
|
|
122
|
+
function resolveMasks(specs, imgWidth, imgHeight, capturedRects, origin = 'config') {
|
|
123
|
+
const rects = [];
|
|
124
|
+
const warnings = [];
|
|
125
|
+
for (const spec of specs) {
|
|
126
|
+
const parsed = parseMaskSpec(spec);
|
|
127
|
+
if (!parsed.ok) {
|
|
128
|
+
warnings.push(parsed.error);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const mask = parsed.mask;
|
|
132
|
+
if (mask.type === 'selector') {
|
|
133
|
+
const matches = (capturedRects ?? []).filter((r) => r.selector === mask.selector);
|
|
134
|
+
if (matches.length === 0) {
|
|
135
|
+
warnings.push(`mask: no captured geometry for selector "${mask.selector}" — ` +
|
|
136
|
+
`the adapter records mask rects at capture time; this capture has none, so the mask was skipped`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
for (const m of matches) {
|
|
140
|
+
const rect = clampRect(m.x, m.y, m.width, m.height, imgWidth, imgHeight);
|
|
141
|
+
if (rect) {
|
|
142
|
+
rects.push({ ...rect, source: { type: 'selector', spec: mask.selector, origin } });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (mask.type === 'edge') {
|
|
148
|
+
const size = resolveDim(mask.size, mask.edge === 'top' || mask.edge === 'bottom' ? imgHeight : imgWidth);
|
|
149
|
+
const raw = mask.edge === 'top' ? { x: 0, y: 0, width: imgWidth, height: size } :
|
|
150
|
+
mask.edge === 'bottom' ? { x: 0, y: imgHeight - size, width: imgWidth, height: size } :
|
|
151
|
+
mask.edge === 'left' ? { x: 0, y: 0, width: size, height: imgHeight } :
|
|
152
|
+
{ x: imgWidth - size, y: 0, width: size, height: imgHeight };
|
|
153
|
+
const rect = clampRect(raw.x, raw.y, raw.width, raw.height, imgWidth, imgHeight);
|
|
154
|
+
if (rect)
|
|
155
|
+
rects.push({ ...rect, source: { type: 'edge', spec: spec, origin } });
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const rect = clampRect(resolveDim(mask.x, imgWidth), resolveDim(mask.y, imgHeight), resolveDim(mask.width, imgWidth), resolveDim(mask.height, imgHeight), imgWidth, imgHeight);
|
|
159
|
+
if (rect)
|
|
160
|
+
rects.push({ ...rect, source: { type: 'region', spec: spec, origin } });
|
|
161
|
+
}
|
|
162
|
+
return { rects, warnings };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Equalize masked pixels (copy candidate → baseline) so the diff loop sees
|
|
166
|
+
* no difference there. Mutates baseline in place; callers clone first.
|
|
167
|
+
*/
|
|
168
|
+
function applyMaskRects(baseline, candidate, width, height, rects) {
|
|
169
|
+
for (const r of rects) {
|
|
170
|
+
for (let y = r.y; y < r.y + r.height; y++) {
|
|
171
|
+
const rowOffset = y * width * 4;
|
|
172
|
+
for (let x = r.x; x < r.x + r.width; x++) {
|
|
173
|
+
const i = rowOffset + x * 4;
|
|
174
|
+
baseline[i] = candidate[i];
|
|
175
|
+
baseline[i + 1] = candidate[i + 1];
|
|
176
|
+
baseline[i + 2] = candidate[i + 2];
|
|
177
|
+
baseline[i + 3] = candidate[i + 3];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Hatch stripe period in px. */
|
|
183
|
+
const HATCH_PERIOD = 8;
|
|
184
|
+
/** Hatch color: mid gray, ~55% alpha — visible on light and dark diffs. */
|
|
185
|
+
const HATCH_RGBA = [128, 128, 128, 140];
|
|
186
|
+
/**
|
|
187
|
+
* Draw a diagonal hatch pattern over masked areas of the diff image so
|
|
188
|
+
* masking is always visible in review. Only pixels inside mask rects are
|
|
189
|
+
* touched.
|
|
190
|
+
*/
|
|
191
|
+
function hatchMaskRects(diff8, width, height, rects) {
|
|
192
|
+
for (const r of rects) {
|
|
193
|
+
for (let y = r.y; y < r.y + r.height; y++) {
|
|
194
|
+
const rowOffset = y * width * 4;
|
|
195
|
+
for (let x = r.x; x < r.x + r.width; x++) {
|
|
196
|
+
const onStripe = (x + y) % HATCH_PERIOD === 0;
|
|
197
|
+
const onBorder = x === r.x || x === r.x + r.width - 1 || y === r.y || y === r.y + r.height - 1;
|
|
198
|
+
if (!onStripe && !onBorder)
|
|
199
|
+
continue;
|
|
200
|
+
const i = rowOffset + x * 4;
|
|
201
|
+
diff8[i] = HATCH_RGBA[0];
|
|
202
|
+
diff8[i + 1] = HATCH_RGBA[1];
|
|
203
|
+
diff8[i + 2] = HATCH_RGBA[2];
|
|
204
|
+
diff8[i + 3] = HATCH_RGBA[3];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.js","sourceRoot":"","sources":["../../src/diff/mask.ts"],"names":[],"mappings":"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"}
|
package/dist/diff/regions.d.ts
CHANGED
|
@@ -5,19 +5,32 @@
|
|
|
5
5
|
* and groups them into rectangular bounding boxes using a simple
|
|
6
6
|
* connected-component labeling approach.
|
|
7
7
|
*/
|
|
8
|
-
import { DiffRegion } from './types';
|
|
8
|
+
import { DiffRegion, RegionOptions } from './types';
|
|
9
|
+
/** Defaults for region clustering (config-overridable). */
|
|
10
|
+
export declare const DEFAULT_REGION_OPTIONS: Required<RegionOptions>;
|
|
9
11
|
/**
|
|
10
12
|
* Detect regions of change in the diff buffer.
|
|
11
13
|
*
|
|
12
14
|
* Scans for non-transparent pixels (any pixel with alpha > 0 in
|
|
13
15
|
* the diff output), groups adjacent changed pixels into rectangular
|
|
14
|
-
* bounding boxes,
|
|
16
|
+
* bounding boxes, filters out regions smaller than the noise floor,
|
|
17
|
+
* and merges regions whose bounding boxes are within mergeDistance px
|
|
18
|
+
* of each other (an anti-fragmentation pass — one logical change often
|
|
19
|
+
* rasterizes as several nearby specks).
|
|
15
20
|
*
|
|
16
21
|
* @param diff8 - Diff image pixel buffer (RGBA)
|
|
17
22
|
* @param width - Image width
|
|
18
23
|
* @param height - Image height
|
|
19
|
-
* @param
|
|
24
|
+
* @param options - { minSize, mergeDistance }, or a bare number meaning
|
|
25
|
+
* minSize (legacy signature, mergeDistance 0)
|
|
20
26
|
* @returns Array of DiffRegion bounding boxes
|
|
21
27
|
*/
|
|
22
|
-
export declare function detectRegions(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number,
|
|
28
|
+
export declare function detectRegions(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number, options?: number | RegionOptions): DiffRegion[];
|
|
29
|
+
/**
|
|
30
|
+
* Merge regions whose bounding boxes are within `distance` px of each
|
|
31
|
+
* other (Chebyshev gap: both axis gaps must be within the distance).
|
|
32
|
+
* Merging cascades until stable; pixel counts sum, boxes union.
|
|
33
|
+
* Region counts are small, so the O(n²) pass is fine.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergeRegions(regions: DiffRegion[], distance: number): DiffRegion[];
|
|
23
36
|
//# sourceMappingURL=regions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpD,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,aAAa,CAG1D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,aAAkB,GACnC,UAAU,EAAE,CAyHd;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CA4ClF"}
|
package/dist/diff/regions.js
CHANGED
|
@@ -7,21 +7,36 @@
|
|
|
7
7
|
* connected-component labeling approach.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DEFAULT_REGION_OPTIONS = void 0;
|
|
10
11
|
exports.detectRegions = detectRegions;
|
|
12
|
+
exports.mergeRegions = mergeRegions;
|
|
13
|
+
/** Defaults for region clustering (config-overridable). */
|
|
14
|
+
exports.DEFAULT_REGION_OPTIONS = {
|
|
15
|
+
minSize: 10,
|
|
16
|
+
mergeDistance: 12,
|
|
17
|
+
};
|
|
11
18
|
/**
|
|
12
19
|
* Detect regions of change in the diff buffer.
|
|
13
20
|
*
|
|
14
21
|
* Scans for non-transparent pixels (any pixel with alpha > 0 in
|
|
15
22
|
* the diff output), groups adjacent changed pixels into rectangular
|
|
16
|
-
* bounding boxes,
|
|
23
|
+
* bounding boxes, filters out regions smaller than the noise floor,
|
|
24
|
+
* and merges regions whose bounding boxes are within mergeDistance px
|
|
25
|
+
* of each other (an anti-fragmentation pass — one logical change often
|
|
26
|
+
* rasterizes as several nearby specks).
|
|
17
27
|
*
|
|
18
28
|
* @param diff8 - Diff image pixel buffer (RGBA)
|
|
19
29
|
* @param width - Image width
|
|
20
30
|
* @param height - Image height
|
|
21
|
-
* @param
|
|
31
|
+
* @param options - { minSize, mergeDistance }, or a bare number meaning
|
|
32
|
+
* minSize (legacy signature, mergeDistance 0)
|
|
22
33
|
* @returns Array of DiffRegion bounding boxes
|
|
23
34
|
*/
|
|
24
|
-
function detectRegions(diff8, width, height,
|
|
35
|
+
function detectRegions(diff8, width, height, options = {}) {
|
|
36
|
+
const opts = typeof options === 'number'
|
|
37
|
+
? { minSize: options, mergeDistance: 0 }
|
|
38
|
+
: { ...exports.DEFAULT_REGION_OPTIONS, ...options };
|
|
39
|
+
const minSize = opts.minSize;
|
|
25
40
|
// Build a binary grid: 1 = changed pixel, 0 = unchanged
|
|
26
41
|
const changed = new Uint8Array(width * height);
|
|
27
42
|
for (let y = 0; y < height; y++) {
|
|
@@ -122,8 +137,58 @@ function detectRegions(diff8, width, height, minSize = 10) {
|
|
|
122
137
|
diffPercent: regionArea > 0 ? (box.count / regionArea) * 100 : 0,
|
|
123
138
|
});
|
|
124
139
|
}
|
|
140
|
+
const merged = mergeRegions(regions, opts.mergeDistance);
|
|
125
141
|
// Sort by position (top-left first)
|
|
126
|
-
|
|
127
|
-
return
|
|
142
|
+
merged.sort((a, b) => a.y - b.y || a.x - b.x);
|
|
143
|
+
return merged;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Merge regions whose bounding boxes are within `distance` px of each
|
|
147
|
+
* other (Chebyshev gap: both axis gaps must be within the distance).
|
|
148
|
+
* Merging cascades until stable; pixel counts sum, boxes union.
|
|
149
|
+
* Region counts are small, so the O(n²) pass is fine.
|
|
150
|
+
*/
|
|
151
|
+
function mergeRegions(regions, distance) {
|
|
152
|
+
if (distance <= 0 || regions.length < 2)
|
|
153
|
+
return regions;
|
|
154
|
+
const gap = (a, b) => {
|
|
155
|
+
const dx = Math.max(0, Math.max(a.x, b.x) - Math.min(a.x + a.width, b.x + b.width));
|
|
156
|
+
const dy = Math.max(0, Math.max(a.y, b.y) - Math.min(a.y + a.height, b.y + b.height));
|
|
157
|
+
return Math.max(dx, dy);
|
|
158
|
+
};
|
|
159
|
+
const out = [...regions];
|
|
160
|
+
let mergedAny = true;
|
|
161
|
+
while (mergedAny) {
|
|
162
|
+
mergedAny = false;
|
|
163
|
+
outer: for (let i = 0; i < out.length; i++) {
|
|
164
|
+
for (let j = i + 1; j < out.length; j++) {
|
|
165
|
+
if (gap(out[i], out[j]) <= distance) {
|
|
166
|
+
const a = out[i];
|
|
167
|
+
const b = out[j];
|
|
168
|
+
const x0 = Math.min(a.x, b.x);
|
|
169
|
+
const y0 = Math.min(a.y, b.y);
|
|
170
|
+
const x1 = Math.max(a.x + a.width, b.x + b.width);
|
|
171
|
+
const y1 = Math.max(a.y + a.height, b.y + b.height);
|
|
172
|
+
const union = {
|
|
173
|
+
x: x0,
|
|
174
|
+
y: y0,
|
|
175
|
+
width: x1 - x0,
|
|
176
|
+
height: y1 - y0,
|
|
177
|
+
diffPixels: a.diffPixels + b.diffPixels,
|
|
178
|
+
diffPercent: 0, // recomputed below
|
|
179
|
+
};
|
|
180
|
+
out.splice(j, 1);
|
|
181
|
+
out[i] = union;
|
|
182
|
+
mergedAny = true;
|
|
183
|
+
break outer;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const r of out) {
|
|
189
|
+
const area = r.width * r.height;
|
|
190
|
+
r.diffPercent = area > 0 ? (r.diffPixels / area) * 100 : 0;
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
128
193
|
}
|
|
129
194
|
//# sourceMappingURL=regions.js.map
|
package/dist/diff/regions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regions.js","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG
|
|
1
|
+
{"version":3,"file":"regions.js","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":"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"}
|
package/dist/diff/types.d.ts
CHANGED
|
@@ -16,9 +16,22 @@ export interface DiffOptions {
|
|
|
16
16
|
ignoreRegions?: IgnoreRegion[];
|
|
17
17
|
/** Detect and return bounding boxes of changed regions. Default: false */
|
|
18
18
|
detectRegions?: boolean;
|
|
19
|
+
/** Clustering tunables used when detectRegions is enabled. */
|
|
20
|
+
regionOptions?: RegionOptions;
|
|
21
|
+
/**
|
|
22
|
+
* Resolved mask rects: excluded from diffing (candidate copied over
|
|
23
|
+
* baseline) AND hatched in the diff output for auditability.
|
|
24
|
+
*/
|
|
25
|
+
masks?: import('./mask').MaskRect[];
|
|
19
26
|
/** How to handle size mismatches between baseline and candidate */
|
|
20
27
|
handleSizeMismatch?: 'pad' | 'crop' | 'error';
|
|
21
28
|
}
|
|
29
|
+
export interface RegionOptions {
|
|
30
|
+
/** Minimum changed pixels for a region to count (noise floor). Default 10. */
|
|
31
|
+
minSize?: number;
|
|
32
|
+
/** Merge regions whose boxes are within this many px. Default 12. */
|
|
33
|
+
mergeDistance?: number;
|
|
34
|
+
}
|
|
22
35
|
export interface IgnoreRegion {
|
|
23
36
|
x: number;
|
|
24
37
|
y: number;
|
package/dist/diff/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/diff/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/diff/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACpC,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/report/compare.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Diffs all temp captures against baselines using the Phase 1 diff engine.
|
|
5
5
|
*/
|
|
6
6
|
import { SnapshotResult } from './results';
|
|
7
|
+
import { MaskSpec } from '../diff/mask';
|
|
8
|
+
import { RegionOptions } from '../diff/types';
|
|
7
9
|
export interface CompareOptions {
|
|
8
10
|
threshold?: number;
|
|
9
11
|
projectRoot: string;
|
|
@@ -14,6 +16,13 @@ export interface CompareOptions {
|
|
|
14
16
|
* marks the snapshot changed.
|
|
15
17
|
*/
|
|
16
18
|
passCriteria?: PassCriteria;
|
|
19
|
+
/**
|
|
20
|
+
* Global masks (from .testivai/config.json `mask`). Merged with any
|
|
21
|
+
* per-call masks recorded in the capture's metadata.
|
|
22
|
+
*/
|
|
23
|
+
mask?: MaskSpec[];
|
|
24
|
+
/** Region clustering tunables (config `diffRegions`). */
|
|
25
|
+
diffRegions?: RegionOptions;
|
|
17
26
|
}
|
|
18
27
|
export interface PassCriteria {
|
|
19
28
|
/** Pass when diff percentage (0-100) is at or below this. Default 0. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,EAAqB,cAAc,EAAkB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,EAAqB,cAAc,EAAkB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAEL,QAAQ,EAET,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;IAClB,yDAAyD;IACzD,WAAW,CAAC,EAAE,aAAa,CAAC;CAC7B;AAsBD,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,EAAE,CA8EpE"}
|
package/dist/report/compare.js
CHANGED
|
@@ -45,13 +45,24 @@ const pngjs_1 = require("pngjs");
|
|
|
45
45
|
const diff_1 = require("../diff");
|
|
46
46
|
const dom_diff_1 = require("../diff/dom-diff");
|
|
47
47
|
const store_1 = require("../baselines/store");
|
|
48
|
+
const mask_1 = require("../diff/mask");
|
|
49
|
+
function readTempMaskMetadata(projectRoot, name) {
|
|
50
|
+
const metaPath = path.join(projectRoot, '.testivai', 'temp', name, 'metadata.json');
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
53
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return {}; // absent or malformed metadata never breaks the compare
|
|
57
|
+
}
|
|
58
|
+
}
|
|
48
59
|
/**
|
|
49
60
|
* Compare all temp screenshots against their baselines.
|
|
50
61
|
*
|
|
51
62
|
* @returns Array of SnapshotResult for each snapshot
|
|
52
63
|
*/
|
|
53
64
|
function compareAll(options) {
|
|
54
|
-
const { projectRoot, reportDir, threshold = 0.1, passCriteria = {} } = options;
|
|
65
|
+
const { projectRoot, reportDir, threshold = 0.1, passCriteria = {}, diffRegions } = options;
|
|
55
66
|
const store = new store_1.BaselineStore(projectRoot);
|
|
56
67
|
const tempNames = store.listTemp();
|
|
57
68
|
const results = [];
|
|
@@ -87,7 +98,16 @@ function compareAll(options) {
|
|
|
87
98
|
// screenshots from the browser, we need to handle the PNG decoding.
|
|
88
99
|
// For simplicity and zero-dep, we do a byte-level comparison first,
|
|
89
100
|
// then fall back to diff engine for actual pixel data.
|
|
90
|
-
const
|
|
101
|
+
const captureMeta = readTempMaskMetadata(projectRoot, name);
|
|
102
|
+
const result = compareBuffers(baselineBuffer, tempBuffer, name, snapshotImagesDir, threshold, {
|
|
103
|
+
configMasks: options.mask ?? [],
|
|
104
|
+
callMasks: [
|
|
105
|
+
...(captureMeta.masks ?? []),
|
|
106
|
+
...(captureMeta.maskSelectors ?? []),
|
|
107
|
+
],
|
|
108
|
+
capturedRects: captureMeta.maskRects,
|
|
109
|
+
regionOptions: diffRegions,
|
|
110
|
+
});
|
|
91
111
|
// DOM-level noise hint. Only meaningful when both sides captured DOM.
|
|
92
112
|
// For 'passed' (pixel-identical) we skip the work — it's already the
|
|
93
113
|
// strongest possible signal.
|
|
@@ -151,6 +171,17 @@ function computeDomSignal(baselineDom, candidateDom) {
|
|
|
151
171
|
noiseHint: !result.domChanged,
|
|
152
172
|
};
|
|
153
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Read a PNG's dimensions from its IHDR header without a full decode.
|
|
176
|
+
* Returns null for anything that doesn't look like a PNG.
|
|
177
|
+
*/
|
|
178
|
+
function readPngDims(buffer) {
|
|
179
|
+
// PNG signature (8 bytes) + IHDR length/type (8) → width at 16, height at 20
|
|
180
|
+
if (buffer.length < 24 || buffer.readUInt32BE(12) !== 0x49484452 /* 'IHDR' */) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
|
|
184
|
+
}
|
|
154
185
|
/**
|
|
155
186
|
* Decode a PNG buffer to raw RGBA pixel data using pngjs.
|
|
156
187
|
*/
|
|
@@ -166,16 +197,10 @@ function encodePng(data, width, height) {
|
|
|
166
197
|
png.data = data;
|
|
167
198
|
return pngjs_1.PNG.sync.write(png);
|
|
168
199
|
}
|
|
169
|
-
|
|
170
|
-
* Compare two PNG buffers.
|
|
171
|
-
*
|
|
172
|
-
* Decodes both PNGs to raw RGBA, runs the pixel-level diff engine,
|
|
173
|
-
* writes a valid diff PNG, and returns a SnapshotResult.
|
|
174
|
-
*/
|
|
175
|
-
function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir, threshold) {
|
|
200
|
+
function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir, threshold, maskContext = { configMasks: [], callMasks: [] }) {
|
|
176
201
|
// Quick byte-level comparison — identical files need no decoding
|
|
177
202
|
if (baselineBuffer.equals(candidateBuffer)) {
|
|
178
|
-
|
|
203
|
+
const result = {
|
|
179
204
|
name,
|
|
180
205
|
status: 'passed',
|
|
181
206
|
diffPercent: 0,
|
|
@@ -184,6 +209,23 @@ function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir
|
|
|
184
209
|
baselinePath: `images/${name}/baseline.png`,
|
|
185
210
|
currentPath: `images/${name}/current.png`,
|
|
186
211
|
};
|
|
212
|
+
// Masks are moot for identical bytes, but the audit trail (and any
|
|
213
|
+
// could-not-apply warnings) must still surface. Header-only dims read.
|
|
214
|
+
const specCount = maskContext.configMasks.length + maskContext.callMasks.length;
|
|
215
|
+
if (specCount > 0) {
|
|
216
|
+
const dims = readPngDims(baselineBuffer);
|
|
217
|
+
if (dims) {
|
|
218
|
+
const fromConfig = (0, mask_1.resolveMasks)(maskContext.configMasks, dims.width, dims.height, maskContext.capturedRects, 'config');
|
|
219
|
+
const fromCall = (0, mask_1.resolveMasks)(maskContext.callMasks, dims.width, dims.height, maskContext.capturedRects, 'call');
|
|
220
|
+
const rects = [...fromConfig.rects, ...fromCall.rects];
|
|
221
|
+
const warnings = [...fromConfig.warnings, ...fromCall.warnings];
|
|
222
|
+
if (rects.length > 0)
|
|
223
|
+
result.masks = rects;
|
|
224
|
+
if (warnings.length > 0)
|
|
225
|
+
result.maskWarnings = warnings;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
187
229
|
}
|
|
188
230
|
try {
|
|
189
231
|
// Decode PNG → raw RGBA so the diff engine gets real pixel data
|
|
@@ -198,7 +240,18 @@ function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir
|
|
|
198
240
|
const candidate8 = new Uint8ClampedArray(expectedLen);
|
|
199
241
|
candidate8.set(new Uint8ClampedArray(candidate.data.buffer, candidate.data.byteOffset, Math.min(candidate.data.length, expectedLen)));
|
|
200
242
|
const diffOutput = new Uint8ClampedArray(expectedLen);
|
|
201
|
-
|
|
243
|
+
// Resolve masks against this image's dimensions. Config and per-call
|
|
244
|
+
// specs are resolved separately so the audit trail records the origin.
|
|
245
|
+
const fromConfig = (0, mask_1.resolveMasks)(maskContext.configMasks, width, height, maskContext.capturedRects, 'config');
|
|
246
|
+
const fromCall = (0, mask_1.resolveMasks)(maskContext.callMasks, width, height, maskContext.capturedRects, 'call');
|
|
247
|
+
const maskRects = [...fromConfig.rects, ...fromCall.rects];
|
|
248
|
+
const maskWarnings = [...fromConfig.warnings, ...fromCall.warnings];
|
|
249
|
+
const diffResult = (0, diff_1.diff)(baseline8, candidate8, diffOutput, width, height, {
|
|
250
|
+
threshold,
|
|
251
|
+
masks: maskRects,
|
|
252
|
+
detectRegions: true,
|
|
253
|
+
regionOptions: maskContext.regionOptions,
|
|
254
|
+
});
|
|
202
255
|
// Encode the diff RGBA back to a valid PNG and write it
|
|
203
256
|
const diffPngBuffer = encodePng(Buffer.from(diffOutput.buffer), width, height);
|
|
204
257
|
const diffPath = path.join(snapshotImagesDir, 'diff.png');
|
|
@@ -206,7 +259,7 @@ function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir
|
|
|
206
259
|
// Status starts as 'changed'; applyPassCriteria() in compareAll may
|
|
207
260
|
// flip it to 'passed' based on the configured tolerances.
|
|
208
261
|
const status = 'changed';
|
|
209
|
-
|
|
262
|
+
const result = {
|
|
210
263
|
name,
|
|
211
264
|
status,
|
|
212
265
|
diffPercent: diffResult.diffPercent,
|
|
@@ -216,6 +269,13 @@ function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir
|
|
|
216
269
|
currentPath: `images/${name}/current.png`,
|
|
217
270
|
diffPath: `images/${name}/diff.png`,
|
|
218
271
|
};
|
|
272
|
+
if (diffResult.regions)
|
|
273
|
+
result.regions = diffResult.regions;
|
|
274
|
+
if (maskRects.length > 0)
|
|
275
|
+
result.masks = maskRects;
|
|
276
|
+
if (maskWarnings.length > 0)
|
|
277
|
+
result.maskWarnings = maskWarnings;
|
|
278
|
+
return result;
|
|
219
279
|
}
|
|
220
280
|
catch (err) {
|
|
221
281
|
// PNG decode failed (corrupt file, unexpected format) — report as changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* → render HTML → optionally auto-open browser.
|
|
6
6
|
*/
|
|
7
7
|
import { PassCriteria } from './compare';
|
|
8
|
-
import { ReportData } from './results';
|
|
8
|
+
import { ReportData, SnapshotResult } from './results';
|
|
9
9
|
export interface GenerateReportOptions {
|
|
10
10
|
projectRoot: string;
|
|
11
11
|
/** Directory for report output. Default: 'visual-report' */
|
|
@@ -35,5 +35,16 @@ export interface GenerateReportOptions {
|
|
|
35
35
|
*
|
|
36
36
|
* @returns The report data
|
|
37
37
|
*/
|
|
38
|
+
/**
|
|
39
|
+
* results.json schema version. 2.2.0 adds per-snapshot `regions[]`
|
|
40
|
+
* (clustered diff bounding boxes), `masks[]` (applied masks with their
|
|
41
|
+
* source — the audit trail), and `maskWarnings[]`. Additive over 2.1.0.
|
|
42
|
+
*/
|
|
43
|
+
export declare const RESULTS_SCHEMA_VERSION = "2.2.0";
|
|
44
|
+
/**
|
|
45
|
+
* Write results.json for an already-computed set of snapshot results.
|
|
46
|
+
* Exposed for callers that manage comparison and rendering separately.
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateResults(results: SnapshotResult[], reportDir: string, version?: string): void;
|
|
38
49
|
export declare function generateReport(options: GenerateReportOptions): ReportData;
|
|
39
50
|
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA8B,YAAY,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA8B,YAAY,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,UAAU,EAAiB,cAAc,EAAC,MAAM,WAAW,CAAC;AAIrE,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UAAU,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EAAE,EACzB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAA+B,GACvC,IAAI,CAGN;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAqDzE"}
|
package/dist/report/generator.js
CHANGED
|
@@ -39,6 +39,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.RESULTS_SCHEMA_VERSION = void 0;
|
|
43
|
+
exports.generateResults = generateResults;
|
|
42
44
|
exports.generateReport = generateReport;
|
|
43
45
|
const fs = __importStar(require("fs"));
|
|
44
46
|
const path = __importStar(require("path"));
|
|
@@ -57,8 +59,22 @@ const local_config_1 = require("../config/local-config");
|
|
|
57
59
|
*
|
|
58
60
|
* @returns The report data
|
|
59
61
|
*/
|
|
62
|
+
/**
|
|
63
|
+
* results.json schema version. 2.2.0 adds per-snapshot `regions[]`
|
|
64
|
+
* (clustered diff bounding boxes), `masks[]` (applied masks with their
|
|
65
|
+
* source — the audit trail), and `maskWarnings[]`. Additive over 2.1.0.
|
|
66
|
+
*/
|
|
67
|
+
exports.RESULTS_SCHEMA_VERSION = '2.2.0';
|
|
68
|
+
/**
|
|
69
|
+
* Write results.json for an already-computed set of snapshot results.
|
|
70
|
+
* Exposed for callers that manage comparison and rendering separately.
|
|
71
|
+
*/
|
|
72
|
+
function generateResults(results, reportDir, version = exports.RESULTS_SCHEMA_VERSION) {
|
|
73
|
+
const data = buildReportData(results, version);
|
|
74
|
+
fs.writeFileSync(path.join(reportDir, 'results.json'), JSON.stringify(data, null, 2));
|
|
75
|
+
}
|
|
60
76
|
function generateReport(options) {
|
|
61
|
-
const { projectRoot, reportDir: reportDirRelative = 'visual-report', threshold = 0.1, autoOpen = true, version =
|
|
77
|
+
const { projectRoot, reportDir: reportDirRelative = 'visual-report', threshold = 0.1, autoOpen = true, version = exports.RESULTS_SCHEMA_VERSION, } = options;
|
|
62
78
|
const reportDir = path.isAbsolute(reportDirRelative)
|
|
63
79
|
? reportDirRelative
|
|
64
80
|
: path.join(projectRoot, reportDirRelative);
|
|
@@ -78,22 +94,12 @@ function generateReport(options) {
|
|
|
78
94
|
reportDir,
|
|
79
95
|
threshold,
|
|
80
96
|
passCriteria,
|
|
97
|
+
mask: localConfig.mask,
|
|
98
|
+
diffRegions: localConfig.diffRegions,
|
|
81
99
|
};
|
|
82
100
|
const snapshots = (0, compare_1.compareAll)(compareOptions);
|
|
83
|
-
// 2. Build summary
|
|
84
|
-
const
|
|
85
|
-
total: snapshots.length,
|
|
86
|
-
passed: snapshots.filter((s) => s.status === 'passed').length,
|
|
87
|
-
changed: snapshots.filter((s) => s.status === 'changed').length,
|
|
88
|
-
newSnapshots: snapshots.filter((s) => s.status === 'new').length,
|
|
89
|
-
};
|
|
90
|
-
// 3. Build report data
|
|
91
|
-
const reportData = {
|
|
92
|
-
version,
|
|
93
|
-
timestamp: new Date().toISOString(),
|
|
94
|
-
summary,
|
|
95
|
-
snapshots,
|
|
96
|
-
};
|
|
101
|
+
// 2-3. Build summary + report data
|
|
102
|
+
const reportData = buildReportData(snapshots, version);
|
|
97
103
|
// 4. Write results.json
|
|
98
104
|
const resultsPath = path.join(reportDir, 'results.json');
|
|
99
105
|
fs.writeFileSync(resultsPath, JSON.stringify(reportData, null, 2));
|
|
@@ -107,6 +113,21 @@ function generateReport(options) {
|
|
|
107
113
|
}
|
|
108
114
|
return reportData;
|
|
109
115
|
}
|
|
116
|
+
/** Assemble the ReportData envelope for a set of snapshot results. */
|
|
117
|
+
function buildReportData(snapshots, version) {
|
|
118
|
+
const summary = {
|
|
119
|
+
total: snapshots.length,
|
|
120
|
+
passed: snapshots.filter((s) => s.status === 'passed').length,
|
|
121
|
+
changed: snapshots.filter((s) => s.status === 'changed').length,
|
|
122
|
+
newSnapshots: snapshots.filter((s) => s.status === 'new').length,
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
version,
|
|
126
|
+
timestamp: new Date().toISOString(),
|
|
127
|
+
summary,
|
|
128
|
+
snapshots,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
110
131
|
/**
|
|
111
132
|
* Open a file in the default browser.
|
|
112
133
|
* Best-effort — does not throw on failure.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":";AAAA;;;;;GAKG
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"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"}
|
package/dist/report/results.d.ts
CHANGED
|
@@ -41,6 +41,18 @@ export interface SnapshotResult {
|
|
|
41
41
|
* captured DOM HTML alongside the screenshot.
|
|
42
42
|
*/
|
|
43
43
|
dom?: SnapshotDomSignal;
|
|
44
|
+
/**
|
|
45
|
+
* Changed-pixel clusters as bounding boxes (top-left sorted). Present
|
|
46
|
+
* for 'changed' results (and auto-passed ones — the boxes stay useful).
|
|
47
|
+
*/
|
|
48
|
+
regions?: import('../diff/types').DiffRegion[];
|
|
49
|
+
/**
|
|
50
|
+
* Masks applied to this comparison (resolved to pixels, with their
|
|
51
|
+
* source) — the audit trail for excluded areas. Present when non-empty.
|
|
52
|
+
*/
|
|
53
|
+
masks?: import('../diff/mask').MaskRect[];
|
|
54
|
+
/** Mask specs that could not be applied (e.g. selector without captured geometry). */
|
|
55
|
+
maskWarnings?: string[];
|
|
44
56
|
/**
|
|
45
57
|
* Present when the snapshot's pixels differed but a pass criterion
|
|
46
58
|
* reported it as passed: 'threshold' (within maxDiffPercent /
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnG;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B"}
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnG;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,eAAe,EAAE,UAAU,EAAE,CAAC;IAC/C;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,cAAc,EAAE,QAAQ,EAAE,CAAC;IAC1C,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/report/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AAEvD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/report/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AAEvD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA+NnD"}
|
package/dist/report/template.js
CHANGED
|
@@ -97,6 +97,19 @@ function renderHtml(data) {
|
|
|
97
97
|
.approve-cmd button:hover { background: #30363d; }
|
|
98
98
|
.approve-cmd button.copied { background: #3fb950; color: #0d1117; }
|
|
99
99
|
|
|
100
|
+
/* Regions */
|
|
101
|
+
.regions { margin-top: 8px; padding: 10px 12px; background: #0d1117; border: 1px solid #21262d; border-radius: 8px; font-size: 12px; }
|
|
102
|
+
.regions .regions-title { color: #8b949e; font-weight: 600; margin-bottom: 6px; }
|
|
103
|
+
.regions ul { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
104
|
+
.regions li a { display: inline-block; padding: 3px 10px; border-radius: 10px; background: #f8514922; color: #f85149; text-decoration: none; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 11px; }
|
|
105
|
+
.regions li a:hover { background: #f8514944; }
|
|
106
|
+
|
|
107
|
+
/* Masks (audit trail) */
|
|
108
|
+
.masks { margin-top: 8px; padding: 10px 12px; background: repeating-linear-gradient(45deg, #161b22, #161b22 6px, #1c2129 6px, #1c2129 8px); border: 1px solid #30363d; border-radius: 8px; font-size: 12px; color: #8b949e; }
|
|
109
|
+
.masks .label { font-weight: 600; color: #c9d1d9; }
|
|
110
|
+
.masks code { color: #79c0ff; font-size: 11px; }
|
|
111
|
+
.mask-warning { margin-top: 8px; padding: 10px 12px; background: #f0883e1a; border: 1px solid #f0883e44; border-radius: 8px; font-size: 12px; color: #f0883e; }
|
|
112
|
+
|
|
100
113
|
/* Zoom overlay */
|
|
101
114
|
.zoom-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 100; justify-content: center; align-items: center; cursor: zoom-out; }
|
|
102
115
|
.zoom-overlay.active { display: flex; }
|
|
@@ -192,6 +205,17 @@ function renderHtml(data) {
|
|
|
192
205
|
document.getElementById('zoomOverlay').classList.remove('active');
|
|
193
206
|
});
|
|
194
207
|
|
|
208
|
+
// Region chips zoom the diff image for inspection
|
|
209
|
+
document.querySelectorAll('.region-chip').forEach(chip => {
|
|
210
|
+
chip.addEventListener('click', (e) => {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
const diff = chip.dataset.diff;
|
|
213
|
+
if (!diff) return;
|
|
214
|
+
document.getElementById('zoomImage').src = diff;
|
|
215
|
+
document.getElementById('zoomOverlay').classList.add('active');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
195
219
|
// Copy approve command
|
|
196
220
|
document.querySelectorAll('.copy-btn').forEach(btn => {
|
|
197
221
|
btn.addEventListener('click', () => {
|
|
@@ -230,6 +254,8 @@ function renderSnapshot(snapshot) {
|
|
|
230
254
|
</div>
|
|
231
255
|
${snapshot.status === 'changed' || snapshot.autoPassed ? `<div class="snapshot-stats">Diff: ${snapshot.diffPercent.toFixed(2)}% (${snapshot.diffCount} pixels)${snapshot.autoPassed ? ` — auto-passed: ${snapshot.autoPassed === 'noise' ? 'DOM unchanged, within noise tolerance' : 'within configured diff tolerance'}` : ''}</div>` : ''}
|
|
232
256
|
${renderDomHint(snapshot)}
|
|
257
|
+
${renderRegions(snapshot)}
|
|
258
|
+
${renderMasks(snapshot)}
|
|
233
259
|
<div class="${gridClass}">
|
|
234
260
|
${hasBaseline ? `<div class="diff-col"><label>Baseline</label><img src="${snapshot.baselinePath}" alt="Baseline"></div>` : ''}
|
|
235
261
|
${hasDiff ? `<div class="diff-col"><label>Diff</label><img src="${snapshot.diffPath}" alt="Diff"></div>` : ''}
|
|
@@ -282,6 +308,54 @@ function renderDomHint(snapshot) {
|
|
|
282
308
|
<span class="summary">— ${escapeHtml(parts.join(', ') || 'structural difference')}.</span>
|
|
283
309
|
</div>`;
|
|
284
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Region chip list: "N changed regions" with coordinates. Clicking a chip
|
|
313
|
+
* zooms the diff image so the reviewer can inspect that area.
|
|
314
|
+
*/
|
|
315
|
+
function renderRegions(snapshot) {
|
|
316
|
+
const regions = snapshot.regions ?? [];
|
|
317
|
+
if (regions.length === 0 || snapshot.status === 'new')
|
|
318
|
+
return '';
|
|
319
|
+
const chips = regions
|
|
320
|
+
.map((r, i) => `<li><a href="#" class="region-chip" data-diff="${snapshot.diffPath ?? ''}" title="${r.diffPixels} changed pixels">#${i + 1} · ${r.width}×${r.height} @ (${r.x}, ${r.y})</a></li>`)
|
|
321
|
+
.join('');
|
|
322
|
+
return `
|
|
323
|
+
<div class="regions">
|
|
324
|
+
<div class="regions-title">${regions.length} changed region${regions.length === 1 ? '' : 's'}</div>
|
|
325
|
+
<ul>${chips}</ul>
|
|
326
|
+
</div>`;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Mask audit trail: every mask applied to this comparison is listed with
|
|
330
|
+
* its source — masking is visible in review, never silent. Warnings cover
|
|
331
|
+
* masks that could not be applied (e.g. selector without captured rects).
|
|
332
|
+
*/
|
|
333
|
+
function renderMasks(snapshot) {
|
|
334
|
+
const masks = snapshot.masks ?? [];
|
|
335
|
+
const warnings = snapshot.maskWarnings ?? [];
|
|
336
|
+
if (masks.length === 0 && warnings.length === 0)
|
|
337
|
+
return '';
|
|
338
|
+
const describe = (m) => {
|
|
339
|
+
const src = m.source.type === 'selector'
|
|
340
|
+
? `selector <code>${escapeHtml(String(m.source.spec))}</code>`
|
|
341
|
+
: m.source.type === 'edge'
|
|
342
|
+
? `edge <code>${escapeHtml(JSON.stringify(m.source.spec))}</code>`
|
|
343
|
+
: `region <code>${escapeHtml(JSON.stringify(m.source.spec))}</code>`;
|
|
344
|
+
return `${src} → ${m.width}×${m.height} @ (${m.x}, ${m.y}) <em>(${m.source.origin})</em>`;
|
|
345
|
+
};
|
|
346
|
+
const maskBlock = masks.length > 0
|
|
347
|
+
? `
|
|
348
|
+
<div class="masks" title="Masked areas are excluded from the pixel diff and hatched in the diff image.">
|
|
349
|
+
<span class="label">🙈 ${masks.length} mask${masks.length === 1 ? '' : 's'} applied</span>
|
|
350
|
+
— ${masks.map(describe).join('; ')}
|
|
351
|
+
</div>`
|
|
352
|
+
: '';
|
|
353
|
+
const warningBlock = warnings.length > 0
|
|
354
|
+
? `
|
|
355
|
+
<div class="mask-warning">⚠️ ${warnings.map((w) => escapeHtml(w)).join('<br>')}</div>`
|
|
356
|
+
: '';
|
|
357
|
+
return maskBlock + warningBlock;
|
|
358
|
+
}
|
|
285
359
|
function escapeHtml(str) {
|
|
286
360
|
return str
|
|
287
361
|
.replace(/&/g, '&')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/report/template.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAOH,
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/report/template.ts"],"names":[],"mappings":"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"}
|