@testivai/witness 2.4.0 → 2.6.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.
Files changed (106) hide show
  1. package/README.md +9 -2
  2. package/dist/baselines/store.d.ts +31 -0
  3. package/dist/baselines/store.d.ts.map +1 -1
  4. package/dist/baselines/store.js +38 -1
  5. package/dist/baselines/store.js.map +1 -1
  6. package/dist/bin/testivai.js +2 -0
  7. package/dist/bin/testivai.js.map +1 -1
  8. package/dist/capture/element-map.d.ts +23 -2
  9. package/dist/capture/element-map.d.ts.map +1 -1
  10. package/dist/capture/element-map.js +102 -4
  11. package/dist/capture/element-map.js.map +1 -1
  12. package/dist/capture/environment.d.ts +243 -0
  13. package/dist/capture/environment.d.ts.map +1 -0
  14. package/dist/capture/environment.js +403 -0
  15. package/dist/capture/environment.js.map +1 -0
  16. package/dist/capture/health-limits.d.ts +14 -0
  17. package/dist/capture/health-limits.d.ts.map +1 -0
  18. package/dist/capture/health-limits.js +17 -0
  19. package/dist/capture/health-limits.js.map +1 -0
  20. package/dist/capture/health.d.ts +124 -0
  21. package/dist/capture/health.d.ts.map +1 -0
  22. package/dist/capture/health.js +327 -0
  23. package/dist/capture/health.js.map +1 -0
  24. package/dist/capture/native-app.d.ts +77 -0
  25. package/dist/capture/native-app.d.ts.map +1 -0
  26. package/dist/capture/native-app.js +172 -0
  27. package/dist/capture/native-app.js.map +1 -0
  28. package/dist/capture/shard.d.ts +17 -0
  29. package/dist/capture/shard.d.ts.map +1 -1
  30. package/dist/capture/shard.js +8 -1
  31. package/dist/capture/shard.js.map +1 -1
  32. package/dist/commands/approve.d.ts.map +1 -1
  33. package/dist/commands/approve.js +20 -0
  34. package/dist/commands/approve.js.map +1 -1
  35. package/dist/commands/env.d.ts +37 -0
  36. package/dist/commands/env.d.ts.map +1 -0
  37. package/dist/commands/env.js +113 -0
  38. package/dist/commands/env.js.map +1 -0
  39. package/dist/commands/merge-captures.d.ts.map +1 -1
  40. package/dist/commands/merge-captures.js +10 -0
  41. package/dist/commands/merge-captures.js.map +1 -1
  42. package/dist/config/local-config.d.ts +27 -0
  43. package/dist/config/local-config.d.ts.map +1 -1
  44. package/dist/config/local-config.js +1 -0
  45. package/dist/config/local-config.js.map +1 -1
  46. package/dist/config/validate-config.d.ts.map +1 -1
  47. package/dist/config/validate-config.js +22 -0
  48. package/dist/config/validate-config.js.map +1 -1
  49. package/dist/diff/attribution.d.ts +16 -2
  50. package/dist/diff/attribution.d.ts.map +1 -1
  51. package/dist/diff/attribution.js +5 -4
  52. package/dist/diff/attribution.js.map +1 -1
  53. package/dist/diff/dom-diff.d.ts.map +1 -1
  54. package/dist/diff/dom-diff.js +7 -1
  55. package/dist/diff/dom-diff.js.map +1 -1
  56. package/dist/index.d.ts +10 -4
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +42 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/report/anchor.d.ts +18 -0
  61. package/dist/report/anchor.d.ts.map +1 -0
  62. package/dist/report/anchor.js +51 -0
  63. package/dist/report/anchor.js.map +1 -0
  64. package/dist/report/compare.d.ts +18 -1
  65. package/dist/report/compare.d.ts.map +1 -1
  66. package/dist/report/compare.js +107 -16
  67. package/dist/report/compare.js.map +1 -1
  68. package/dist/report/environment.d.ts +101 -0
  69. package/dist/report/environment.d.ts.map +1 -0
  70. package/dist/report/environment.js +354 -0
  71. package/dist/report/environment.js.map +1 -0
  72. package/dist/report/generator.d.ts +20 -2
  73. package/dist/report/generator.d.ts.map +1 -1
  74. package/dist/report/generator.js +46 -6
  75. package/dist/report/generator.js.map +1 -1
  76. package/dist/report/health.d.ts +115 -0
  77. package/dist/report/health.d.ts.map +1 -0
  78. package/dist/report/health.js +279 -0
  79. package/dist/report/health.js.map +1 -0
  80. package/dist/report/index.d.ts +9 -2
  81. package/dist/report/index.d.ts.map +1 -1
  82. package/dist/report/index.js +25 -1
  83. package/dist/report/index.js.map +1 -1
  84. package/dist/report/results.d.ts +73 -1
  85. package/dist/report/results.d.ts.map +1 -1
  86. package/dist/report/shift-check.d.ts +40 -0
  87. package/dist/report/shift-check.d.ts.map +1 -0
  88. package/dist/report/shift-check.js +109 -0
  89. package/dist/report/shift-check.js.map +1 -0
  90. package/dist/report/template.d.ts +28 -1
  91. package/dist/report/template.d.ts.map +1 -1
  92. package/dist/report/template.js +179 -16
  93. package/dist/report/template.js.map +1 -1
  94. package/dist/report/thumbnail.d.ts +76 -0
  95. package/dist/report/thumbnail.d.ts.map +1 -0
  96. package/dist/report/thumbnail.js +166 -0
  97. package/dist/report/thumbnail.js.map +1 -0
  98. package/dist/report/validate.d.ts +53 -0
  99. package/dist/report/validate.d.ts.map +1 -0
  100. package/dist/report/validate.js +383 -0
  101. package/dist/report/validate.js.map +1 -0
  102. package/dist/standalone/run.d.ts +39 -1
  103. package/dist/standalone/run.d.ts.map +1 -1
  104. package/dist/standalone/run.js +142 -3
  105. package/dist/standalone/run.js.map +1 -1
  106. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ /**
2
+ * TestivAI Report — capture health: when a capture is unhealthy, and the words
3
+ *
4
+ * A capture is UNHEALTHY when what it shows is not the page as built: web
5
+ * fonts were still loading, a web font failed to load (the page rendered with
6
+ * a fallback font), a stylesheet failed to load, or a stylesheet request
7
+ * answered 400 or more. Such a diff is a glitch of the capture or a broken
8
+ * asset, never render noise: the DOM can be identical and the computed styles
9
+ * match (the style digest hashes the declared font-family, not the font that
10
+ * rendered), so without this the diff read "likely render noise".
11
+ *
12
+ * A font request that answered 400 or more makes the capture unhealthy only
13
+ * with a web font that failed, or with no Font Loading API to say none did: a
14
+ * face whose first `src` 404s and whose next one loads rendered as built.
15
+ *
16
+ * Broken images, failed scripts and other requests, and a failed font request
17
+ * while no web font failed, are WARNINGS: recorded and shown, but they do not
18
+ * make a capture unhealthy on their own. A broken image shows as a changed
19
+ * region where it sits, which the pixel and DOM layers already read; telling a
20
+ * broken image that matters from a tracking pixel needs its position compared
21
+ * with the diff's regions (a follow-up).
22
+ *
23
+ * An unhealthy capture is never render noise, and no pass criterion passes
24
+ * one whose pixels differ, with one exception: when both sides failed in
25
+ * exactly the same way (a permanently broken asset, `sameOnBothSides`), the
26
+ * pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance) still
27
+ * apply, because both images miss the same assets. Noise auto-pass never does.
28
+ *
29
+ * Deterministic and read from results.json fields only (`captureHealth`), so
30
+ * the report, @testivai/mcp and the GitHub Action agree. No Node dependencies.
31
+ */
32
+ import type { CaptureHealth } from '../capture/health';
33
+ import type { SnapshotCaptureHealth } from './results';
34
+ export interface HealthAssessment {
35
+ /** True when the capture does not show the page as built (see the module comment). */
36
+ unhealthy: boolean;
37
+ /** What made it unhealthy, short: "a failed web font", "a failed stylesheet", "web fonts still loading". */
38
+ problems: string[];
39
+ /** Why it is unhealthy, in plain words, most important first. */
40
+ reasons: string[];
41
+ /** What else failed to load (images, scripts, other requests, a font file a face fell back from): shown, not unhealthy. */
42
+ warnings: string[];
43
+ }
44
+ /** Judge one capture's health: unhealthy or not, why, and what else failed. */
45
+ export declare function assessHealth(health: CaptureHealth): HealthAssessment;
46
+ /**
47
+ * Whether two captures failed in exactly the same way, on everything that can
48
+ * make a capture unhealthy: the same web font faces (family, weight, style),
49
+ * the same stylesheets, and the same stylesheet and font requests (host and
50
+ * path, and status). A permanently broken asset (a font that 404s on every
51
+ * run) then shows in both images alike, so the diff compares like with like.
52
+ *
53
+ * Never the same when web fonts were still loading on either side (what
54
+ * rendered depends on timing), when the two sides could not see the same
55
+ * things (Font Loading API or request statuses available on one side only),
56
+ * or when a list may have been cut at HEALTH_LIMITS.list: what is not
57
+ * recorded cannot be proven the same. Broken images and failed scripts and
58
+ * other requests are warnings and do not count.
59
+ */
60
+ export declare function sameHealthFailures(a: CaptureHealth, b: CaptureHealth): boolean;
61
+ /** Prefix of a reason or warning that is the baseline's, in results.json. */
62
+ export declare const BASELINE_HEALTH_PREFIX = "baseline: ";
63
+ /**
64
+ * The per-snapshot `captureHealth` (schema 2.7.0): both recorded blocks, which
65
+ * side is unhealthy, and the reasons and warnings (the baseline's marked
66
+ * "baseline: "), the current capture's first. Undefined when neither side
67
+ * recorded health: not checked, which is neither healthy nor unhealthy.
68
+ * `sameOnBothSides: true` (schema 2.8.0) when both sides are unhealthy with
69
+ * the same failures (sameHealthFailures): the pass tolerances then still
70
+ * apply, noise auto-pass never does.
71
+ */
72
+ export declare function snapshotCaptureHealth(baseline: CaptureHealth | null, candidate: CaptureHealth | null): SnapshotCaptureHealth | undefined;
73
+ /** The fields of a snapshot result the health rules read. */
74
+ export interface HealthSnapshotInput {
75
+ captureHealth?: Pick<SnapshotCaptureHealth, 'unhealthy' | 'sameOnBothSides'> | null;
76
+ }
77
+ /** Whether either side of the snapshot was captured unhealthy. */
78
+ export declare function isUnhealthyCapture(snapshot: HealthSnapshotInput): boolean;
79
+ /**
80
+ * Whether the pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance)
81
+ * may judge the snapshot: always for a healthy or unrecorded one, and for an
82
+ * unhealthy one only when both sides failed the same way (sameOnBothSides).
83
+ * Noise auto-pass never judges an unhealthy one: see isUnhealthyCapture.
84
+ */
85
+ export declare function healthAllowsTolerances(snapshot: HealthSnapshotInput): boolean;
86
+ /** What to do about an unhealthy current capture. */
87
+ export declare const UNHEALTHY_CAPTURE_NEXT = "an unhealthy capture is a glitch of the capture or a broken asset, not render noise: fix the page, the server or the test and re-run; never approve it as a baseline";
88
+ /** What to do about an unhealthy baseline. */
89
+ export declare const UNHEALTHY_BASELINE_NEXT = "re-baseline from a healthy run";
90
+ /** What to do when both sides failed the same way (sameOnBothSides). */
91
+ export declare const SAME_FAILURES_NEXT = "the same assets failed in both captures, so the pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance) still apply, but this is never render noise and noise auto-pass never applies: fix the broken asset, then re-baseline from a healthy run";
92
+ export interface CaptureHealthReading {
93
+ side: 'candidate' | 'baseline' | 'both';
94
+ /** Present (true) when both sides failed the same way: the tolerances still apply, noise auto-pass never does. */
95
+ sameOnBothSides?: true;
96
+ /** The reasons, in plain words: the current capture's, then the baseline called out. */
97
+ detail: string;
98
+ /** What to do next. */
99
+ next: string;
100
+ }
101
+ /**
102
+ * The "Unhealthy capture" reading every surface shows, or null when neither
103
+ * side is unhealthy. An unhealthy current capture names what failed and says
104
+ * it is a glitch or a broken asset to fix, never to approve; an unhealthy
105
+ * baseline is called out on every compare against it, with "re-baseline from
106
+ * a healthy run".
107
+ */
108
+ export declare function captureHealthReading(health: SnapshotCaptureHealth | null | undefined): CaptureHealthReading | null;
109
+ /**
110
+ * The warning for approving a capture that is unhealthy (`testivai approve`,
111
+ * the approve action's reply), or null for a healthy capture or one that
112
+ * recorded no health. Approving still happens: the human decides.
113
+ */
114
+ export declare function approveHealthWarning(health: CaptureHealth | null | undefined): string | null;
115
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/report/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,mBAAmB,CAAC;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,SAAS,EAAE,OAAO,CAAC;IACnB,4GAA4G;IAC5G,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2HAA2H;IAC3H,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA2CD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAsDpE;AAaD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAgB9E;AAED,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,GAAG,qBAAqB,GAAG,SAAS,CAcxI;AAED,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,iBAAiB,CAAC,GAAG,IAAI,CAAC;CACrF;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAG7E;AAED,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,yKACqI,CAAC;AAEzK,8CAA8C;AAC9C,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,6PAC6N,CAAC;AAE7P,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IACxC,kHAAkH;IAClH,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAgBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,GAAG,IAAI,CAiBlH;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ5F"}
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ /**
3
+ * TestivAI Report — capture health: when a capture is unhealthy, and the words
4
+ *
5
+ * A capture is UNHEALTHY when what it shows is not the page as built: web
6
+ * fonts were still loading, a web font failed to load (the page rendered with
7
+ * a fallback font), a stylesheet failed to load, or a stylesheet request
8
+ * answered 400 or more. Such a diff is a glitch of the capture or a broken
9
+ * asset, never render noise: the DOM can be identical and the computed styles
10
+ * match (the style digest hashes the declared font-family, not the font that
11
+ * rendered), so without this the diff read "likely render noise".
12
+ *
13
+ * A font request that answered 400 or more makes the capture unhealthy only
14
+ * with a web font that failed, or with no Font Loading API to say none did: a
15
+ * face whose first `src` 404s and whose next one loads rendered as built.
16
+ *
17
+ * Broken images, failed scripts and other requests, and a failed font request
18
+ * while no web font failed, are WARNINGS: recorded and shown, but they do not
19
+ * make a capture unhealthy on their own. A broken image shows as a changed
20
+ * region where it sits, which the pixel and DOM layers already read; telling a
21
+ * broken image that matters from a tracking pixel needs its position compared
22
+ * with the diff's regions (a follow-up).
23
+ *
24
+ * An unhealthy capture is never render noise, and no pass criterion passes
25
+ * one whose pixels differ, with one exception: when both sides failed in
26
+ * exactly the same way (a permanently broken asset, `sameOnBothSides`), the
27
+ * pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance) still
28
+ * apply, because both images miss the same assets. Noise auto-pass never does.
29
+ *
30
+ * Deterministic and read from results.json fields only (`captureHealth`), so
31
+ * the report, @testivai/mcp and the GitHub Action agree. No Node dependencies.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.SAME_FAILURES_NEXT = exports.UNHEALTHY_BASELINE_NEXT = exports.UNHEALTHY_CAPTURE_NEXT = exports.BASELINE_HEALTH_PREFIX = void 0;
35
+ exports.assessHealth = assessHealth;
36
+ exports.sameHealthFailures = sameHealthFailures;
37
+ exports.snapshotCaptureHealth = snapshotCaptureHealth;
38
+ exports.isUnhealthyCapture = isUnhealthyCapture;
39
+ exports.healthAllowsTolerances = healthAllowsTolerances;
40
+ exports.captureHealthReading = captureHealthReading;
41
+ exports.approveHealthWarning = approveHealthWarning;
42
+ const health_limits_1 = require("../capture/health-limits");
43
+ /** Items named per kind before the rest are counted. */
44
+ const NAMED = 3;
45
+ const plural = (n, noun) => `${n} ${noun}${n === 1 ? '' : 's'}`;
46
+ /** "stylesheet example.com/a.css returned 404", or "… failed to load (net::ERR_…)" without a status. */
47
+ function requestLine(noun, entry) {
48
+ const url = entry.url || '(no URL)';
49
+ if (entry.status > 0)
50
+ return `${noun} ${url} returned ${entry.status}`;
51
+ return `${noun} ${url} failed to load${entry.error ? ` (${entry.error})` : ''}`;
52
+ }
53
+ /** One line per named request, then the count of the rest. */
54
+ function requestLines(noun, entries, total) {
55
+ const shown = entries.slice(0, NAMED);
56
+ const lines = shown.map((e) => requestLine(noun, e));
57
+ const rest = total - shown.length;
58
+ if (rest > 0)
59
+ lines.push(`${plural(rest, `more ${noun}`)} failed to load`);
60
+ return lines;
61
+ }
62
+ /** Elements that failed (stylesheets, images), named with their request's status when it is known. */
63
+ function elementLines(noun, urls, total, requests) {
64
+ const byUrl = new Map(requests.map((e) => [e.url, e]));
65
+ const shown = urls.slice(0, NAMED);
66
+ const lines = shown.map((url) => {
67
+ const request = byUrl.get(url);
68
+ return request ? requestLine(noun, request) : `${noun} ${url || '(no URL)'} did not load`;
69
+ });
70
+ const rest = total - shown.length;
71
+ if (rest > 0)
72
+ lines.push(`${plural(rest, `more ${noun}`)} did not load`);
73
+ return lines;
74
+ }
75
+ function fontLine(families) {
76
+ const quoted = families.map((f) => `'${f}'`);
77
+ if (quoted.length === 1)
78
+ return `web font ${quoted[0]} failed to load, so the page rendered with a fallback font`;
79
+ const names = quoted.length === 2 ? `${quoted[0]} and ${quoted[1]}` : `${quoted.slice(0, 2).join(', ')} and ${quoted.length - 2} more`;
80
+ return `web fonts ${names} failed to load, so the page rendered with fallback fonts`;
81
+ }
82
+ /** Judge one capture's health: unhealthy or not, why, and what else failed. */
83
+ function assessHealth(health) {
84
+ const reasons = [];
85
+ const warnings = [];
86
+ const problems = new Set();
87
+ const failed = health.resources.failed;
88
+ if (health.fonts.status === 'loading') {
89
+ reasons.push('web fonts were still loading when the page was captured');
90
+ problems.add('web fonts still loading');
91
+ }
92
+ const families = [...new Set(health.fonts.failed.map((f) => f.family))];
93
+ if (families.length > 0) {
94
+ reasons.push(fontLine(families));
95
+ problems.add('a failed web font');
96
+ }
97
+ // A failed font request on its own is not a fallback font: the face can
98
+ // load from its next `src`. Unhealthy with a face that failed, or when the
99
+ // page had no Font Loading API to say whether one did.
100
+ if (failed.font && failed.font.count > 0) {
101
+ const lines = requestLines('font file', failed.font.entries, failed.font.count);
102
+ if (families.length > 0 || health.fonts.status === 'unavailable') {
103
+ reasons.push(...lines);
104
+ problems.add('a failed web font');
105
+ }
106
+ else {
107
+ warnings.push(...lines);
108
+ }
109
+ }
110
+ // A failed <link> stylesheet is named once, with its request's status when
111
+ // the request is known; a failed stylesheet request with no <link> (an
112
+ // @import) gets its own line.
113
+ const css = failed.css ?? { count: 0, entries: [] };
114
+ if (health.stylesheets.failed > 0) {
115
+ reasons.push(...elementLines('stylesheet', health.stylesheets.urls, health.stylesheets.failed, css.entries));
116
+ problems.add('a failed stylesheet');
117
+ }
118
+ const linked = new Set(health.stylesheets.urls);
119
+ const unlinked = css.entries.filter((e) => !linked.has(e.url));
120
+ const unlinkedCount = css.count - (css.entries.length - unlinked.length);
121
+ if (unlinkedCount > 0) {
122
+ reasons.push(...requestLines('stylesheet', unlinked, unlinkedCount));
123
+ problems.add('a failed stylesheet');
124
+ }
125
+ const img = failed.img ?? { count: 0, entries: [] };
126
+ if (health.images.broken > 0)
127
+ warnings.push(...elementLines('image', health.images.urls, health.images.broken, img.entries));
128
+ const shownImages = new Set(health.images.urls);
129
+ const otherImages = img.entries.filter((e) => !shownImages.has(e.url));
130
+ const otherImageCount = img.count - (img.entries.length - otherImages.length);
131
+ if (otherImageCount > 0)
132
+ warnings.push(...requestLines('image', otherImages, otherImageCount));
133
+ if (failed.script && failed.script.count > 0)
134
+ warnings.push(...requestLines('script', failed.script.entries, failed.script.count));
135
+ if (failed.other && failed.other.count > 0)
136
+ warnings.push(...requestLines('request', failed.other.entries, failed.other.count));
137
+ return { unhealthy: problems.size > 0, problems: [...problems], reasons, warnings };
138
+ }
139
+ /** One failed request, as compared between the two sides: host and path, and the status (the error text varies run to run). */
140
+ const requestKey = (e) => `${e.url}\u0000${e.status}`;
141
+ /** The sorted keys of a list, or null when the list may have been cut (a count above its entries, or a list at the cap). */
142
+ function completeKeys(items, total, key) {
143
+ if (items.length !== total || items.length >= health_limits_1.HEALTH_LIMITS.list)
144
+ return null;
145
+ return items.map(key).sort();
146
+ }
147
+ const sameKeys = (a, b) => a !== null && b !== null && a.length === b.length && a.every((k, i) => k === b[i]);
148
+ /**
149
+ * Whether two captures failed in exactly the same way, on everything that can
150
+ * make a capture unhealthy: the same web font faces (family, weight, style),
151
+ * the same stylesheets, and the same stylesheet and font requests (host and
152
+ * path, and status). A permanently broken asset (a font that 404s on every
153
+ * run) then shows in both images alike, so the diff compares like with like.
154
+ *
155
+ * Never the same when web fonts were still loading on either side (what
156
+ * rendered depends on timing), when the two sides could not see the same
157
+ * things (Font Loading API or request statuses available on one side only),
158
+ * or when a list may have been cut at HEALTH_LIMITS.list: what is not
159
+ * recorded cannot be proven the same. Broken images and failed scripts and
160
+ * other requests are warnings and do not count.
161
+ */
162
+ function sameHealthFailures(a, b) {
163
+ if (a.fonts.status === 'loading' || b.fonts.status === 'loading')
164
+ return false;
165
+ if (a.fonts.status !== b.fonts.status || a.resources.status !== b.resources.status)
166
+ return false;
167
+ const fontKey = (f) => `${f.family}\u0000${f.weight}\u0000${f.style}`;
168
+ const fonts = (h) => completeKeys(h.fonts.failed, h.fonts.failed.length, fontKey);
169
+ const sheets = (h) => completeKeys(h.stylesheets.urls, h.stylesheets.failed, (u) => u);
170
+ const requests = (h, type) => {
171
+ const group = h.resources.failed[type] ?? { count: 0, entries: [] };
172
+ return completeKeys(group.entries, group.count, requestKey);
173
+ };
174
+ return (sameKeys(fonts(a), fonts(b)) &&
175
+ sameKeys(sheets(a), sheets(b)) &&
176
+ sameKeys(requests(a, 'css'), requests(b, 'css')) &&
177
+ sameKeys(requests(a, 'font'), requests(b, 'font')));
178
+ }
179
+ /** Prefix of a reason or warning that is the baseline's, in results.json. */
180
+ exports.BASELINE_HEALTH_PREFIX = 'baseline: ';
181
+ /**
182
+ * The per-snapshot `captureHealth` (schema 2.7.0): both recorded blocks, which
183
+ * side is unhealthy, and the reasons and warnings (the baseline's marked
184
+ * "baseline: "), the current capture's first. Undefined when neither side
185
+ * recorded health: not checked, which is neither healthy nor unhealthy.
186
+ * `sameOnBothSides: true` (schema 2.8.0) when both sides are unhealthy with
187
+ * the same failures (sameHealthFailures): the pass tolerances then still
188
+ * apply, noise auto-pass never does.
189
+ */
190
+ function snapshotCaptureHealth(baseline, candidate) {
191
+ if (!baseline && !candidate)
192
+ return undefined;
193
+ const c = candidate ? assessHealth(candidate) : null;
194
+ const b = baseline ? assessHealth(baseline) : null;
195
+ const unhealthy = c?.unhealthy && b?.unhealthy ? 'both' : c?.unhealthy ? 'candidate' : b?.unhealthy ? 'baseline' : null;
196
+ const fromBaseline = (lines) => lines.map((l) => `${exports.BASELINE_HEALTH_PREFIX}${l}`);
197
+ return {
198
+ ...(candidate ? { candidate } : {}),
199
+ ...(baseline ? { baseline } : {}),
200
+ unhealthy,
201
+ ...(unhealthy === 'both' && baseline && candidate && sameHealthFailures(baseline, candidate) ? { sameOnBothSides: true } : {}),
202
+ reasons: [...(c?.reasons ?? []), ...fromBaseline(b?.reasons ?? [])],
203
+ warnings: [...(c?.warnings ?? []), ...fromBaseline(b?.warnings ?? [])],
204
+ };
205
+ }
206
+ /** Whether either side of the snapshot was captured unhealthy. */
207
+ function isUnhealthyCapture(snapshot) {
208
+ return Boolean(snapshot.captureHealth?.unhealthy);
209
+ }
210
+ /**
211
+ * Whether the pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance)
212
+ * may judge the snapshot: always for a healthy or unrecorded one, and for an
213
+ * unhealthy one only when both sides failed the same way (sameOnBothSides).
214
+ * Noise auto-pass never judges an unhealthy one: see isUnhealthyCapture.
215
+ */
216
+ function healthAllowsTolerances(snapshot) {
217
+ if (!isUnhealthyCapture(snapshot))
218
+ return true;
219
+ return snapshot.captureHealth?.unhealthy === 'both' && snapshot.captureHealth.sameOnBothSides === true;
220
+ }
221
+ /** What to do about an unhealthy current capture. */
222
+ exports.UNHEALTHY_CAPTURE_NEXT = 'an unhealthy capture is a glitch of the capture or a broken asset, not render noise: fix the page, the server or the test and re-run; never approve it as a baseline';
223
+ /** What to do about an unhealthy baseline. */
224
+ exports.UNHEALTHY_BASELINE_NEXT = 're-baseline from a healthy run';
225
+ /** What to do when both sides failed the same way (sameOnBothSides). */
226
+ exports.SAME_FAILURES_NEXT = 'the same assets failed in both captures, so the pass tolerances (maxDiffPercent, maxDiffPixels, shiftTolerance) still apply, but this is never render noise and noise auto-pass never applies: fix the broken asset, then re-baseline from a healthy run';
227
+ const joinAnd = (items) => (items.length <= 1 ? items.join('') : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`);
228
+ /** One side's assessment: from its recorded block, else from the reasons (results written by another tool). */
229
+ function sideAssessment(health, side) {
230
+ const block = side === 'candidate' ? health.candidate : health.baseline;
231
+ if (block)
232
+ return assessHealth(block);
233
+ const isBaseline = (r) => r.startsWith(exports.BASELINE_HEALTH_PREFIX);
234
+ const reasons = side === 'baseline'
235
+ ? health.reasons.filter(isBaseline).map((r) => r.slice(exports.BASELINE_HEALTH_PREFIX.length))
236
+ : health.reasons.filter((r) => !isBaseline(r));
237
+ return { problems: [], reasons };
238
+ }
239
+ /**
240
+ * The "Unhealthy capture" reading every surface shows, or null when neither
241
+ * side is unhealthy. An unhealthy current capture names what failed and says
242
+ * it is a glitch or a broken asset to fix, never to approve; an unhealthy
243
+ * baseline is called out on every compare against it, with "re-baseline from
244
+ * a healthy run".
245
+ */
246
+ function captureHealthReading(health) {
247
+ if (!health?.unhealthy)
248
+ return null;
249
+ const side = health.unhealthy;
250
+ if (side === 'both' && health.sameOnBothSides === true) {
251
+ const detail = `${sideAssessment(health, 'candidate').reasons.join('; ')}; the baseline was captured with the same failures`;
252
+ return { side, sameOnBothSides: true, detail, next: exports.SAME_FAILURES_NEXT };
253
+ }
254
+ const parts = [];
255
+ if (side !== 'baseline')
256
+ parts.push(sideAssessment(health, 'candidate').reasons.join('; '));
257
+ if (side !== 'candidate') {
258
+ const b = sideAssessment(health, 'baseline');
259
+ const what = b.problems.length > 0 ? `with ${joinAnd(b.problems)}` : 'unhealthy';
260
+ parts.push(`the baseline was captured ${what}${side === 'both' ? ' too' : ''} (${b.reasons.join('; ')})`);
261
+ }
262
+ const next = side === 'candidate' ? exports.UNHEALTHY_CAPTURE_NEXT : side === 'baseline' ? exports.UNHEALTHY_BASELINE_NEXT : `${exports.UNHEALTHY_CAPTURE_NEXT}, and ${exports.UNHEALTHY_BASELINE_NEXT}`;
263
+ return { side, detail: parts.filter(Boolean).join('; '), next };
264
+ }
265
+ /**
266
+ * The warning for approving a capture that is unhealthy (`testivai approve`,
267
+ * the approve action's reply), or null for a healthy capture or one that
268
+ * recorded no health. Approving still happens: the human decides.
269
+ */
270
+ function approveHealthWarning(health) {
271
+ if (!health)
272
+ return null;
273
+ const assessment = assessHealth(health);
274
+ if (!assessment.unhealthy)
275
+ return null;
276
+ return (`approving an unhealthy capture as the baseline — ${assessment.reasons.join('; ')}. ` +
277
+ 'Every compare against it will say so: fix the page, the server or the test, re-run and approve a healthy capture');
278
+ }
279
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/report/health.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AA2DH,oCAsDC;AA2BD,gDAgBC;AAcD,sDAcC;AAQD,gDAEC;AAQD,wDAGC;AA4CD,oDAiBC;AAOD,oDAQC;AAtRD,4DAAyD;AAczD,wDAAwD;AACxD,MAAM,KAAK,GAAG,CAAC,CAAC;AAEhB,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,IAAY,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAEhF,wGAAwG;AACxG,SAAS,WAAW,CAAC,IAAY,EAAE,KAA0C;IAC3E,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,UAAU,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,IAAI,IAAI,GAAG,aAAa,KAAK,CAAC,MAAM,EAAE,CAAC;IACvE,OAAO,GAAG,IAAI,IAAI,GAAG,kBAAkB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAClF,CAAC;AAED,8DAA8D;AAC9D,SAAS,YAAY,CAAC,IAAY,EAAE,OAAoC,EAAE,KAAa;IACrF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAClC,IAAI,IAAI,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,iBAAiB,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sGAAsG;AACtG,SAAS,YAAY,CAAC,IAAY,EAAE,IAAc,EAAE,KAAa,EAAE,QAAqC;IACtG,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,IAAI,UAAU,eAAe,CAAC;IAC5F,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAClC,IAAI,IAAI,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,eAAe,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,QAAkB;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,4DAA4D,CAAC;IAClH,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC;IACvI,OAAO,aAAa,KAAK,2DAA2D,CAAC;AACvF,CAAC;AAED,+EAA+E;AAC/E,SAAgB,YAAY,CAAC,MAAqB;IAChD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IAEvC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACxE,QAAQ,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAC1C,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjC,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACpC,CAAC;IACD,wEAAwE;IACxE,2EAA2E;IAC3E,uDAAuD;IACvD,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YACvB,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,uEAAuE;IACvE,8BAA8B;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACpD,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7G,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;QACrE,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACpD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7H,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,MAAM,eAAe,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9E,IAAI,eAAe,GAAG,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC;IAC/F,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnI,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAEhI,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtF,CAAC;AAED,+HAA+H;AAC/H,MAAM,UAAU,GAAG,CAAC,CAAsC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;AAE3F,4HAA4H;AAC5H,SAAS,YAAY,CAAI,KAAU,EAAE,KAAa,EAAE,GAAwB;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,6BAAa,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9E,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAkB,EAAE,CAAkB,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhJ;;;;;;;;;;;;;GAaG;AACH,SAAgB,kBAAkB,CAAC,CAAgB,EAAE,CAAgB;IACnE,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/E,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACjG,MAAM,OAAO,GAAG,CAAC,CAA2C,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;IAChH,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjG,MAAM,MAAM,GAAG,CAAC,CAAgB,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACtG,MAAM,QAAQ,GAAG,CAAC,CAAgB,EAAE,IAAoB,EAAE,EAAE;QAC1D,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACpE,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC9D,CAAC,CAAC;IACF,OAAO,CACL,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAChD,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CACnD,CAAC;AACJ,CAAC;AAED,6EAA6E;AAChE,QAAA,sBAAsB,GAAG,YAAY,CAAC;AAEnD;;;;;;;;GAQG;AACH,SAAgB,qBAAqB,CAAC,QAA8B,EAAE,SAA+B;IACnG,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnD,MAAM,SAAS,GAAG,CAAC,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACxH,MAAM,YAAY,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,8BAAsB,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5F,OAAO;QACL,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,SAAS;QACT,GAAG,CAAC,SAAS,KAAK,MAAM,IAAI,QAAQ,IAAI,SAAS,IAAI,kBAAkB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvI,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;QACnE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;KACvE,CAAC;AACJ,CAAC;AAOD,kEAAkE;AAClE,SAAgB,kBAAkB,CAAC,QAA6B;IAC9D,OAAO,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,QAA6B;IAClE,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,QAAQ,CAAC,aAAa,EAAE,SAAS,KAAK,MAAM,IAAI,QAAQ,CAAC,aAAa,CAAC,eAAe,KAAK,IAAI,CAAC;AACzG,CAAC;AAED,qDAAqD;AACxC,QAAA,sBAAsB,GACjC,sKAAsK,CAAC;AAEzK,8CAA8C;AACjC,QAAA,uBAAuB,GAAG,gCAAgC,CAAC;AAExE,wEAAwE;AAC3D,QAAA,kBAAkB,GAC7B,0PAA0P,CAAC;AAY7P,MAAM,OAAO,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AAE9I,+GAA+G;AAC/G,SAAS,cAAc,CAAC,MAA6B,EAAE,IAA8B;IACnF,MAAM,KAAK,GAAG,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACxE,IAAI,KAAK;QAAE,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,8BAAsB,CAAC,CAAC;IACvE,MAAM,OAAO,GACX,IAAI,KAAK,UAAU;QACjB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,8BAAsB,CAAC,MAAM,CAAC,CAAC;QACtF,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAAC,MAAgD;IACnF,IAAI,CAAC,MAAM,EAAE,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;IAC9B,IAAI,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,oDAAoD,CAAC;QAC7H,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAAkB,EAAE,CAAC;IAC3E,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,KAAK,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5F,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,6BAA6B,IAAI,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,IAAI,GACR,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,8BAAsB,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,+BAAuB,CAAC,CAAC,CAAC,GAAG,8BAAsB,SAAS,+BAAuB,EAAE,CAAC;IAC9J,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,MAAwC;IAC3E,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,UAAU,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,CACL,oDAAoD,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QACrF,kHAAkH,CACnH,CAAC;AACJ,CAAC"}
@@ -5,10 +5,17 @@ export { generateReport } from './generator';
5
5
  export type { GenerateReportOptions } from './generator';
6
6
  export { compareAll } from './compare';
7
7
  export type { CompareOptions, PassCriteria } from './compare';
8
- export { renderHtml } from './template';
8
+ export { renderHtml, snapshotAnchorId } from './template';
9
9
  export { diffShape, describeRegions, NOISE_MAX_DIFF_PERCENT, CONCENTRATED_MAX_PAGE_SHARE, STYLE_CHECK_DOES_NOT_COVER, } from './diff-shape';
10
10
  export type { DiffShape, DiffShapeBox, DiffShapeInput } from './diff-shape';
11
11
  export { describeSizeChange, explainHeightChange, formatSizeMismatch, formatSizes, sizeMismatchKind } from './size-mismatch';
12
12
  export type { Dimensions, HeightChangeContext, SizeMismatchInfo } from './size-mismatch';
13
- export type { ReportData, ReportSummary, SnapshotResult, SnapshotStatus, SnapshotDomSignal, } from './results';
13
+ export { commonEnvironment, describeCaptureMethod, describeEnvironmentChange, diffEnvironments, environmentChangeGroups, environmentChangeLine, isRenderingDrift, snapshotEnvironmentChange, } from './environment';
14
+ export type { EnvironmentChangeGroup, EnvironmentSnapshotInput } from './environment';
15
+ export { approveHealthWarning, assessHealth, BASELINE_HEALTH_PREFIX, captureHealthReading, healthAllowsTolerances, isUnhealthyCapture, SAME_FAILURES_NEXT, sameHealthFailures, snapshotCaptureHealth, UNHEALTHY_BASELINE_NEXT, UNHEALTHY_CAPTURE_NEXT, } from './health';
16
+ export type { CaptureHealthReading, HealthAssessment, HealthSnapshotInput } from './health';
17
+ export { parseHealth } from '../capture/health';
18
+ export type { CaptureEnvironment, RenderEnvironment } from '../capture/environment';
19
+ export type { CaptureHealth, ResourceFailures, ResourceType } from '../capture/health';
20
+ export type { ReportData, ReportSummary, SnapshotResult, SnapshotStatus, SnapshotCaptureHealth, SnapshotDomSignal, SnapshotEnvironment, } from './results';
14
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EACL,SAAS,EACT,eAAe,EACf,sBAAsB,EACtB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC7H,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACzF,YAAY,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EACL,SAAS,EACT,eAAe,EACf,sBAAsB,EACtB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC7H,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACpF,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACvF,YAAY,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,WAAW,CAAC"}
@@ -3,13 +3,14 @@
3
3
  * TestivAI Report Generator
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.sizeMismatchKind = exports.formatSizes = exports.formatSizeMismatch = exports.explainHeightChange = exports.describeSizeChange = exports.STYLE_CHECK_DOES_NOT_COVER = exports.CONCENTRATED_MAX_PAGE_SHARE = exports.NOISE_MAX_DIFF_PERCENT = exports.describeRegions = exports.diffShape = exports.renderHtml = exports.compareAll = exports.generateReport = void 0;
6
+ exports.parseHealth = exports.UNHEALTHY_CAPTURE_NEXT = exports.UNHEALTHY_BASELINE_NEXT = exports.snapshotCaptureHealth = exports.sameHealthFailures = exports.SAME_FAILURES_NEXT = exports.isUnhealthyCapture = exports.healthAllowsTolerances = exports.captureHealthReading = exports.BASELINE_HEALTH_PREFIX = exports.assessHealth = exports.approveHealthWarning = exports.snapshotEnvironmentChange = exports.isRenderingDrift = exports.environmentChangeLine = exports.environmentChangeGroups = exports.diffEnvironments = exports.describeEnvironmentChange = exports.describeCaptureMethod = exports.commonEnvironment = exports.sizeMismatchKind = exports.formatSizes = exports.formatSizeMismatch = exports.explainHeightChange = exports.describeSizeChange = exports.STYLE_CHECK_DOES_NOT_COVER = exports.CONCENTRATED_MAX_PAGE_SHARE = exports.NOISE_MAX_DIFF_PERCENT = exports.describeRegions = exports.diffShape = exports.snapshotAnchorId = exports.renderHtml = exports.compareAll = exports.generateReport = void 0;
7
7
  var generator_1 = require("./generator");
8
8
  Object.defineProperty(exports, "generateReport", { enumerable: true, get: function () { return generator_1.generateReport; } });
9
9
  var compare_1 = require("./compare");
10
10
  Object.defineProperty(exports, "compareAll", { enumerable: true, get: function () { return compare_1.compareAll; } });
11
11
  var template_1 = require("./template");
12
12
  Object.defineProperty(exports, "renderHtml", { enumerable: true, get: function () { return template_1.renderHtml; } });
13
+ Object.defineProperty(exports, "snapshotAnchorId", { enumerable: true, get: function () { return template_1.snapshotAnchorId; } });
13
14
  var diff_shape_1 = require("./diff-shape");
14
15
  Object.defineProperty(exports, "diffShape", { enumerable: true, get: function () { return diff_shape_1.diffShape; } });
15
16
  Object.defineProperty(exports, "describeRegions", { enumerable: true, get: function () { return diff_shape_1.describeRegions; } });
@@ -22,4 +23,27 @@ Object.defineProperty(exports, "explainHeightChange", { enumerable: true, get: f
22
23
  Object.defineProperty(exports, "formatSizeMismatch", { enumerable: true, get: function () { return size_mismatch_1.formatSizeMismatch; } });
23
24
  Object.defineProperty(exports, "formatSizes", { enumerable: true, get: function () { return size_mismatch_1.formatSizes; } });
24
25
  Object.defineProperty(exports, "sizeMismatchKind", { enumerable: true, get: function () { return size_mismatch_1.sizeMismatchKind; } });
26
+ var environment_1 = require("./environment");
27
+ Object.defineProperty(exports, "commonEnvironment", { enumerable: true, get: function () { return environment_1.commonEnvironment; } });
28
+ Object.defineProperty(exports, "describeCaptureMethod", { enumerable: true, get: function () { return environment_1.describeCaptureMethod; } });
29
+ Object.defineProperty(exports, "describeEnvironmentChange", { enumerable: true, get: function () { return environment_1.describeEnvironmentChange; } });
30
+ Object.defineProperty(exports, "diffEnvironments", { enumerable: true, get: function () { return environment_1.diffEnvironments; } });
31
+ Object.defineProperty(exports, "environmentChangeGroups", { enumerable: true, get: function () { return environment_1.environmentChangeGroups; } });
32
+ Object.defineProperty(exports, "environmentChangeLine", { enumerable: true, get: function () { return environment_1.environmentChangeLine; } });
33
+ Object.defineProperty(exports, "isRenderingDrift", { enumerable: true, get: function () { return environment_1.isRenderingDrift; } });
34
+ Object.defineProperty(exports, "snapshotEnvironmentChange", { enumerable: true, get: function () { return environment_1.snapshotEnvironmentChange; } });
35
+ var health_1 = require("./health");
36
+ Object.defineProperty(exports, "approveHealthWarning", { enumerable: true, get: function () { return health_1.approveHealthWarning; } });
37
+ Object.defineProperty(exports, "assessHealth", { enumerable: true, get: function () { return health_1.assessHealth; } });
38
+ Object.defineProperty(exports, "BASELINE_HEALTH_PREFIX", { enumerable: true, get: function () { return health_1.BASELINE_HEALTH_PREFIX; } });
39
+ Object.defineProperty(exports, "captureHealthReading", { enumerable: true, get: function () { return health_1.captureHealthReading; } });
40
+ Object.defineProperty(exports, "healthAllowsTolerances", { enumerable: true, get: function () { return health_1.healthAllowsTolerances; } });
41
+ Object.defineProperty(exports, "isUnhealthyCapture", { enumerable: true, get: function () { return health_1.isUnhealthyCapture; } });
42
+ Object.defineProperty(exports, "SAME_FAILURES_NEXT", { enumerable: true, get: function () { return health_1.SAME_FAILURES_NEXT; } });
43
+ Object.defineProperty(exports, "sameHealthFailures", { enumerable: true, get: function () { return health_1.sameHealthFailures; } });
44
+ Object.defineProperty(exports, "snapshotCaptureHealth", { enumerable: true, get: function () { return health_1.snapshotCaptureHealth; } });
45
+ Object.defineProperty(exports, "UNHEALTHY_BASELINE_NEXT", { enumerable: true, get: function () { return health_1.UNHEALTHY_BASELINE_NEXT; } });
46
+ Object.defineProperty(exports, "UNHEALTHY_CAPTURE_NEXT", { enumerable: true, get: function () { return health_1.UNHEALTHY_CAPTURE_NEXT; } });
47
+ var health_2 = require("../capture/health");
48
+ Object.defineProperty(exports, "parseHealth", { enumerable: true, get: function () { return health_2.parseHealth; } });
25
49
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,yCAA6C;AAApC,2GAAA,cAAc,OAAA;AAEvB,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,uCAAwC;AAA/B,sGAAA,UAAU,OAAA;AACnB,2CAMsB;AALpB,uGAAA,SAAS,OAAA;AACT,6GAAA,eAAe,OAAA;AACf,oHAAA,sBAAsB,OAAA;AACtB,yHAAA,2BAA2B,OAAA;AAC3B,wHAAA,0BAA0B,OAAA;AAG5B,iDAA6H;AAApH,mHAAA,kBAAkB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAAE,4GAAA,WAAW,OAAA;AAAE,iHAAA,gBAAgB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,yCAA6C;AAApC,2GAAA,cAAc,OAAA;AAEvB,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,uCAA0D;AAAjD,sGAAA,UAAU,OAAA;AAAE,4GAAA,gBAAgB,OAAA;AACrC,2CAMsB;AALpB,uGAAA,SAAS,OAAA;AACT,6GAAA,eAAe,OAAA;AACf,oHAAA,sBAAsB,OAAA;AACtB,yHAAA,2BAA2B,OAAA;AAC3B,wHAAA,0BAA0B,OAAA;AAG5B,iDAA6H;AAApH,mHAAA,kBAAkB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAAE,4GAAA,WAAW,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AAEnG,6CASuB;AARrB,gHAAA,iBAAiB,OAAA;AACjB,oHAAA,qBAAqB,OAAA;AACrB,wHAAA,yBAAyB,OAAA;AACzB,+GAAA,gBAAgB,OAAA;AAChB,sHAAA,uBAAuB,OAAA;AACvB,oHAAA,qBAAqB,OAAA;AACrB,+GAAA,gBAAgB,OAAA;AAChB,wHAAA,yBAAyB,OAAA;AAG3B,mCAYkB;AAXhB,8GAAA,oBAAoB,OAAA;AACpB,sGAAA,YAAY,OAAA;AACZ,gHAAA,sBAAsB,OAAA;AACtB,8GAAA,oBAAoB,OAAA;AACpB,gHAAA,sBAAsB,OAAA;AACtB,4GAAA,kBAAkB,OAAA;AAClB,4GAAA,kBAAkB,OAAA;AAClB,4GAAA,kBAAkB,OAAA;AAClB,+GAAA,qBAAqB,OAAA;AACrB,iHAAA,uBAAuB,OAAA;AACvB,gHAAA,sBAAsB,OAAA;AAGxB,4CAAgD;AAAvC,qGAAA,WAAW,OAAA"}
@@ -60,6 +60,14 @@ export interface SnapshotResult {
60
60
  currentPath?: string;
61
61
  /** Relative path to diff image (if any) */
62
62
  diffPath?: string;
63
+ /**
64
+ * Relative path to the thumbnail (schema 2.6.0): the changed area of the
65
+ * baseline, the current capture and the diff side by side, at most 800×400
66
+ * px (report/thumbnail.ts). Only for 'changed' snapshots with a diff image
67
+ * that has a differing pixel; never for passed (auto-passed included) or
68
+ * new ones.
69
+ */
70
+ thumbPath?: string;
63
71
  /**
64
72
  * DOM-level signal. Present only when both baseline and candidate
65
73
  * captured DOM HTML alongside the screenshot.
@@ -91,7 +99,10 @@ export interface SnapshotResult {
91
99
  * diff not concentrated in one area, within noiseMaxDiffPercent; a diff
92
100
  * whose styles were not compared or changed, or whose regions all sit in
93
101
  * one area, is never auto-passed as noise), or 'shift' (every region is a pure
94
- * element shift within shiftTolerance px). The diff image is still
102
+ * element shift within shiftTolerance px). Never present when
103
+ * `captureHealth.unhealthy` is set (schema 2.7.0), except 'threshold' or
104
+ * 'shift' with `captureHealth.sameOnBothSides` (schema 2.8.0); never
105
+ * 'noise' then. The diff image is still
95
106
  * written — auto-passes stay auditable.
96
107
  */
97
108
  autoPassed?: 'threshold' | 'noise' | 'shift';
@@ -121,6 +132,62 @@ export interface SnapshotResult {
121
132
  height: number;
122
133
  };
123
134
  };
135
+ /**
136
+ * Capture provenance (schema 2.5.0): present when the baseline and the
137
+ * current capture both recorded the environment they were captured in
138
+ * (metadata.json `environment`). See report/environment.ts.
139
+ */
140
+ environment?: SnapshotEnvironment;
141
+ /**
142
+ * Capture health (schema 2.7.0): present when the baseline or the current
143
+ * capture recorded what failed to load (metadata.json `health`). An
144
+ * unhealthy side is never render noise: `dom.noiseHint` is false and no
145
+ * pass criterion applies while pixels differ. See report/health.ts.
146
+ */
147
+ captureHealth?: SnapshotCaptureHealth;
148
+ }
149
+ /** What failed to load in each capture of a snapshot, and whether that makes it unhealthy. */
150
+ export interface SnapshotCaptureHealth {
151
+ /** The current capture's recorded health. Absent when it recorded none (not checked). */
152
+ candidate?: import('../capture/health').CaptureHealth;
153
+ /** The baseline's recorded health, frozen on approve. Absent when it recorded none (not checked). */
154
+ baseline?: import('../capture/health').CaptureHealth;
155
+ /** Which side is unhealthy, or null when neither recorded side is. */
156
+ unhealthy: 'candidate' | 'baseline' | 'both' | null;
157
+ /**
158
+ * Schema 2.8.0: present (true) only when both sides are unhealthy with the
159
+ * same failures (the same web font faces, stylesheets, and stylesheet and
160
+ * font requests; never while fonts were still loading, never when a list
161
+ * was cut). The pass tolerances (maxDiffPercent, maxDiffPixels,
162
+ * shiftTolerance) then still apply; noise auto-pass never does.
163
+ */
164
+ sameOnBothSides?: true;
165
+ /** Why, in plain words; the baseline's start with "baseline: ". Empty when not unhealthy. */
166
+ reasons: string[];
167
+ /** What else failed to load (broken images, failed scripts and other requests): shown, never unhealthy on its own. */
168
+ warnings: string[];
169
+ }
170
+ /** How the environment of the current capture differs from the baseline's. */
171
+ export interface SnapshotEnvironment {
172
+ /** Every field that differs, as dotted paths ("browser.version"), sorted. Empty when nothing differs. */
173
+ delta: string[];
174
+ /**
175
+ * The subset of `delta` that can change how the page renders: the
176
+ * browser, the OS platform, distro and architecture (the OS release on
177
+ * macOS and Windows), the render settings, the capture method, a
178
+ * container and the environment label. Framework, adapter and driver
179
+ * versions, the CI runner image and the project are context only.
180
+ */
181
+ renderingDelta: string[];
182
+ /** The baseline's recorded environment. Present when `delta` is non-empty. */
183
+ baseline?: import('../capture/environment').CaptureEnvironment;
184
+ /** The current capture's recorded environment. Present when `delta` is non-empty. */
185
+ candidate?: import('../capture/environment').CaptureEnvironment;
186
+ /**
187
+ * 'strict' when environmentPolicy "strict" made this snapshot changed
188
+ * because its rendering environment changed, whatever its pixels say.
189
+ */
190
+ policy?: 'strict';
124
191
  }
125
192
  export interface ReportSummary {
126
193
  total: number;
@@ -141,5 +208,10 @@ export interface ReportData {
141
208
  snapshots: SnapshotResult[];
142
209
  /** Names of baselines with no capture this run (schema 2.3.0+). */
143
210
  missingBaselines?: string[];
211
+ /**
212
+ * The environment of this run's captures (schema 2.5.0): the fields every
213
+ * capture that recorded one agrees on. Absent when none recorded one.
214
+ */
215
+ environment?: Partial<import('../capture/environment').CaptureEnvironment>;
144
216
  }
145
217
  //# sourceMappingURL=results.d.ts.map
@@ -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;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;IAClD,wEAAwE;IACxE,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACtD;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,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;IAC3D;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,cAAc,EAAE,QAAQ,EAAE,CAAC;IAC1C,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,qBAAqB,EAAE,SAAS,CAAC;IACpD;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE;QAAE,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC9G;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;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;IAC5B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B"}
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":"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"}