@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.
- package/README.md +9 -2
- package/dist/baselines/store.d.ts +31 -0
- package/dist/baselines/store.d.ts.map +1 -1
- package/dist/baselines/store.js +38 -1
- package/dist/baselines/store.js.map +1 -1
- package/dist/bin/testivai.js +2 -0
- package/dist/bin/testivai.js.map +1 -1
- package/dist/capture/element-map.d.ts +23 -2
- package/dist/capture/element-map.d.ts.map +1 -1
- package/dist/capture/element-map.js +102 -4
- package/dist/capture/element-map.js.map +1 -1
- package/dist/capture/environment.d.ts +243 -0
- package/dist/capture/environment.d.ts.map +1 -0
- package/dist/capture/environment.js +403 -0
- package/dist/capture/environment.js.map +1 -0
- package/dist/capture/health-limits.d.ts +14 -0
- package/dist/capture/health-limits.d.ts.map +1 -0
- package/dist/capture/health-limits.js +17 -0
- package/dist/capture/health-limits.js.map +1 -0
- package/dist/capture/health.d.ts +124 -0
- package/dist/capture/health.d.ts.map +1 -0
- package/dist/capture/health.js +327 -0
- package/dist/capture/health.js.map +1 -0
- package/dist/capture/native-app.d.ts +77 -0
- package/dist/capture/native-app.d.ts.map +1 -0
- package/dist/capture/native-app.js +172 -0
- package/dist/capture/native-app.js.map +1 -0
- package/dist/capture/shard.d.ts +17 -0
- package/dist/capture/shard.d.ts.map +1 -1
- package/dist/capture/shard.js +8 -1
- package/dist/capture/shard.js.map +1 -1
- package/dist/commands/approve.d.ts.map +1 -1
- package/dist/commands/approve.js +20 -0
- package/dist/commands/approve.js.map +1 -1
- package/dist/commands/env.d.ts +37 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +113 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/merge-captures.d.ts.map +1 -1
- package/dist/commands/merge-captures.js +10 -0
- package/dist/commands/merge-captures.js.map +1 -1
- package/dist/config/local-config.d.ts +27 -0
- package/dist/config/local-config.d.ts.map +1 -1
- package/dist/config/local-config.js +1 -0
- package/dist/config/local-config.js.map +1 -1
- package/dist/config/validate-config.d.ts.map +1 -1
- package/dist/config/validate-config.js +22 -0
- package/dist/config/validate-config.js.map +1 -1
- package/dist/diff/attribution.d.ts +16 -2
- package/dist/diff/attribution.d.ts.map +1 -1
- package/dist/diff/attribution.js +5 -4
- package/dist/diff/attribution.js.map +1 -1
- package/dist/diff/dom-diff.d.ts.map +1 -1
- package/dist/diff/dom-diff.js +7 -1
- package/dist/diff/dom-diff.js.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -1
- package/dist/index.js.map +1 -1
- package/dist/report/anchor.d.ts +18 -0
- package/dist/report/anchor.d.ts.map +1 -0
- package/dist/report/anchor.js +51 -0
- package/dist/report/anchor.js.map +1 -0
- package/dist/report/compare.d.ts +18 -1
- package/dist/report/compare.d.ts.map +1 -1
- package/dist/report/compare.js +107 -16
- package/dist/report/compare.js.map +1 -1
- package/dist/report/environment.d.ts +101 -0
- package/dist/report/environment.d.ts.map +1 -0
- package/dist/report/environment.js +354 -0
- package/dist/report/environment.js.map +1 -0
- package/dist/report/generator.d.ts +20 -2
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +46 -6
- package/dist/report/generator.js.map +1 -1
- package/dist/report/health.d.ts +115 -0
- package/dist/report/health.d.ts.map +1 -0
- package/dist/report/health.js +279 -0
- package/dist/report/health.js.map +1 -0
- package/dist/report/index.d.ts +9 -2
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +25 -1
- package/dist/report/index.js.map +1 -1
- package/dist/report/results.d.ts +73 -1
- package/dist/report/results.d.ts.map +1 -1
- package/dist/report/shift-check.d.ts +40 -0
- package/dist/report/shift-check.d.ts.map +1 -0
- package/dist/report/shift-check.js +109 -0
- package/dist/report/shift-check.js.map +1 -0
- package/dist/report/template.d.ts +28 -1
- package/dist/report/template.d.ts.map +1 -1
- package/dist/report/template.js +179 -16
- package/dist/report/template.js.map +1 -1
- package/dist/report/thumbnail.d.ts +76 -0
- package/dist/report/thumbnail.d.ts.map +1 -0
- package/dist/report/thumbnail.js +166 -0
- package/dist/report/thumbnail.js.map +1 -0
- package/dist/report/validate.d.ts +53 -0
- package/dist/report/validate.d.ts.map +1 -0
- package/dist/report/validate.js +383 -0
- package/dist/report/validate.js.map +1 -0
- package/dist/standalone/run.d.ts +39 -1
- package/dist/standalone/run.d.ts.map +1 -1
- package/dist/standalone/run.js +142 -3
- package/dist/standalone/run.js.map +1 -1
- 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":"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"}
|
package/dist/report/index.d.ts
CHANGED
|
@@ -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
|
|
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;
|
|
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"}
|
package/dist/report/index.js
CHANGED
|
@@ -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
|
package/dist/report/index.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/report/results.d.ts
CHANGED
|
@@ -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).
|
|
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
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":"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"}
|