tuffgal 0.1.0-alpha.9 → 0.2.0-alpha.2
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/CHANGELOG.md +149 -2
- package/README.md +78 -3
- package/dist/cli.d.ts +50 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +204 -18
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/config.d.ts +38 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -1
- package/dist/config.js.map +1 -1
- package/dist/coverage/screens.d.ts +11 -6
- package/dist/coverage/screens.d.ts.map +1 -1
- package/dist/coverage/screens.js +12 -7
- package/dist/coverage/screens.js.map +1 -1
- package/dist/reporter/assets/report.css +415 -51
- package/dist/reporter/assets/report.js +502 -68
- package/dist/reporter/template.d.ts +8 -1
- package/dist/reporter/template.d.ts.map +1 -1
- package/dist/reporter/template.js +517 -85
- package/dist/reporter/template.js.map +1 -1
- package/dist/reporter/writeReport.d.ts +2 -2
- package/dist/reporter/writeReport.d.ts.map +1 -1
- package/dist/reporter/writeReport.js +3 -3
- package/dist/reporter/writeReport.js.map +1 -1
- package/dist/runner/approve.d.ts +34 -9
- package/dist/runner/approve.d.ts.map +1 -1
- package/dist/runner/approve.js +54 -10
- package/dist/runner/approve.js.map +1 -1
- package/dist/runner/approveFrom.d.ts +58 -0
- package/dist/runner/approveFrom.d.ts.map +1 -0
- package/dist/runner/approveFrom.js +384 -0
- package/dist/runner/approveFrom.js.map +1 -0
- package/dist/runner/breakpointPasses.d.ts +39 -0
- package/dist/runner/breakpointPasses.d.ts.map +1 -0
- package/dist/runner/breakpointPasses.js +107 -0
- package/dist/runner/breakpointPasses.js.map +1 -0
- package/dist/runner/bridges/database.d.ts +4 -3
- package/dist/runner/bridges/database.d.ts.map +1 -1
- package/dist/runner/bridges/database.js +4 -3
- package/dist/runner/bridges/database.js.map +1 -1
- package/dist/runner/exitCode.d.ts +34 -0
- package/dist/runner/exitCode.d.ts.map +1 -0
- package/dist/runner/exitCode.js +41 -0
- package/dist/runner/exitCode.js.map +1 -0
- package/dist/runner/manifest.d.ts +149 -0
- package/dist/runner/manifest.d.ts.map +1 -0
- package/dist/runner/manifest.js +229 -0
- package/dist/runner/manifest.js.map +1 -0
- package/dist/runner/mode.d.ts +41 -0
- package/dist/runner/mode.d.ts.map +1 -0
- package/dist/runner/mode.js +43 -0
- package/dist/runner/mode.js.map +1 -0
- package/dist/runner/orphanScan.d.ts +68 -0
- package/dist/runner/orphanScan.d.ts.map +1 -0
- package/dist/runner/orphanScan.js +150 -0
- package/dist/runner/orphanScan.js.map +1 -0
- package/dist/runner/run.d.ts +85 -1
- package/dist/runner/run.d.ts.map +1 -1
- package/dist/runner/run.js +267 -41
- package/dist/runner/run.js.map +1 -1
- package/dist/runner/runAction.d.ts +16 -0
- package/dist/runner/runAction.d.ts.map +1 -1
- package/dist/runner/runAction.js +112 -13
- package/dist/runner/runAction.js.map +1 -1
- package/dist/runner/runStory.d.ts +15 -4
- package/dist/runner/runStory.d.ts.map +1 -1
- package/dist/runner/runStory.js +41 -19
- package/dist/runner/runStory.js.map +1 -1
- package/dist/runner/storyFilter.d.ts +8 -0
- package/dist/runner/storyFilter.d.ts.map +1 -1
- package/dist/runner/storyFilter.js +11 -0
- package/dist/runner/storyFilter.js.map +1 -1
- package/dist/schema/action.d.ts +11 -11
- package/dist/schema/result.d.ts +125 -6
- package/dist/schema/result.d.ts.map +1 -1
- package/dist/schema/result.js.map +1 -1
- package/dist/screenshots/baselineStore.d.ts +92 -10
- package/dist/screenshots/baselineStore.d.ts.map +1 -1
- package/dist/screenshots/baselineStore.js +102 -18
- package/dist/screenshots/baselineStore.js.map +1 -1
- package/dist/screenshots/capture.d.ts +10 -5
- package/dist/screenshots/capture.d.ts.map +1 -1
- package/dist/screenshots/capture.js +31 -18
- package/dist/screenshots/capture.js.map +1 -1
- package/package.json +1 -1
package/dist/schema/action.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const hintSchema: z.ZodObject<{
|
|
|
9
9
|
text: z.ZodOptional<z.ZodString>;
|
|
10
10
|
role: z.ZodOptional<z.ZodEnum<{
|
|
11
11
|
link: "link";
|
|
12
|
+
status: "status";
|
|
12
13
|
alert: "alert";
|
|
13
14
|
banner: "banner";
|
|
14
15
|
button: "button";
|
|
@@ -29,7 +30,6 @@ export declare const hintSchema: z.ZodObject<{
|
|
|
29
30
|
region: "region";
|
|
30
31
|
row: "row";
|
|
31
32
|
searchbox: "searchbox";
|
|
32
|
-
status: "status";
|
|
33
33
|
switch: "switch";
|
|
34
34
|
tab: "tab";
|
|
35
35
|
table: "table";
|
|
@@ -59,6 +59,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
59
59
|
text: z.ZodOptional<z.ZodString>;
|
|
60
60
|
role: z.ZodOptional<z.ZodEnum<{
|
|
61
61
|
link: "link";
|
|
62
|
+
status: "status";
|
|
62
63
|
alert: "alert";
|
|
63
64
|
banner: "banner";
|
|
64
65
|
button: "button";
|
|
@@ -79,7 +80,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
79
80
|
region: "region";
|
|
80
81
|
row: "row";
|
|
81
82
|
searchbox: "searchbox";
|
|
82
|
-
status: "status";
|
|
83
83
|
switch: "switch";
|
|
84
84
|
tab: "tab";
|
|
85
85
|
table: "table";
|
|
@@ -99,6 +99,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
99
99
|
text: z.ZodOptional<z.ZodString>;
|
|
100
100
|
role: z.ZodOptional<z.ZodEnum<{
|
|
101
101
|
link: "link";
|
|
102
|
+
status: "status";
|
|
102
103
|
alert: "alert";
|
|
103
104
|
banner: "banner";
|
|
104
105
|
button: "button";
|
|
@@ -119,7 +120,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
119
120
|
region: "region";
|
|
120
121
|
row: "row";
|
|
121
122
|
searchbox: "searchbox";
|
|
122
|
-
status: "status";
|
|
123
123
|
switch: "switch";
|
|
124
124
|
tab: "tab";
|
|
125
125
|
table: "table";
|
|
@@ -161,6 +161,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
161
161
|
text: z.ZodOptional<z.ZodString>;
|
|
162
162
|
role: z.ZodOptional<z.ZodEnum<{
|
|
163
163
|
link: "link";
|
|
164
|
+
status: "status";
|
|
164
165
|
alert: "alert";
|
|
165
166
|
banner: "banner";
|
|
166
167
|
button: "button";
|
|
@@ -181,7 +182,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
181
182
|
region: "region";
|
|
182
183
|
row: "row";
|
|
183
184
|
searchbox: "searchbox";
|
|
184
|
-
status: "status";
|
|
185
185
|
switch: "switch";
|
|
186
186
|
tab: "tab";
|
|
187
187
|
table: "table";
|
|
@@ -201,6 +201,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
201
201
|
text: z.ZodOptional<z.ZodString>;
|
|
202
202
|
role: z.ZodOptional<z.ZodEnum<{
|
|
203
203
|
link: "link";
|
|
204
|
+
status: "status";
|
|
204
205
|
alert: "alert";
|
|
205
206
|
banner: "banner";
|
|
206
207
|
button: "button";
|
|
@@ -221,7 +222,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
221
222
|
region: "region";
|
|
222
223
|
row: "row";
|
|
223
224
|
searchbox: "searchbox";
|
|
224
|
-
status: "status";
|
|
225
225
|
switch: "switch";
|
|
226
226
|
tab: "tab";
|
|
227
227
|
table: "table";
|
|
@@ -261,6 +261,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
261
261
|
text: z.ZodOptional<z.ZodString>;
|
|
262
262
|
role: z.ZodOptional<z.ZodEnum<{
|
|
263
263
|
link: "link";
|
|
264
|
+
status: "status";
|
|
264
265
|
alert: "alert";
|
|
265
266
|
banner: "banner";
|
|
266
267
|
button: "button";
|
|
@@ -281,7 +282,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
281
282
|
region: "region";
|
|
282
283
|
row: "row";
|
|
283
284
|
searchbox: "searchbox";
|
|
284
|
-
status: "status";
|
|
285
285
|
switch: "switch";
|
|
286
286
|
tab: "tab";
|
|
287
287
|
table: "table";
|
|
@@ -301,6 +301,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
301
301
|
text: z.ZodOptional<z.ZodString>;
|
|
302
302
|
role: z.ZodOptional<z.ZodEnum<{
|
|
303
303
|
link: "link";
|
|
304
|
+
status: "status";
|
|
304
305
|
alert: "alert";
|
|
305
306
|
banner: "banner";
|
|
306
307
|
button: "button";
|
|
@@ -321,7 +322,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
321
322
|
region: "region";
|
|
322
323
|
row: "row";
|
|
323
324
|
searchbox: "searchbox";
|
|
324
|
-
status: "status";
|
|
325
325
|
switch: "switch";
|
|
326
326
|
tab: "tab";
|
|
327
327
|
table: "table";
|
|
@@ -363,6 +363,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
363
363
|
text: z.ZodOptional<z.ZodString>;
|
|
364
364
|
role: z.ZodOptional<z.ZodEnum<{
|
|
365
365
|
link: "link";
|
|
366
|
+
status: "status";
|
|
366
367
|
alert: "alert";
|
|
367
368
|
banner: "banner";
|
|
368
369
|
button: "button";
|
|
@@ -383,7 +384,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
383
384
|
region: "region";
|
|
384
385
|
row: "row";
|
|
385
386
|
searchbox: "searchbox";
|
|
386
|
-
status: "status";
|
|
387
387
|
switch: "switch";
|
|
388
388
|
tab: "tab";
|
|
389
389
|
table: "table";
|
|
@@ -403,6 +403,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
403
403
|
text: z.ZodOptional<z.ZodString>;
|
|
404
404
|
role: z.ZodOptional<z.ZodEnum<{
|
|
405
405
|
link: "link";
|
|
406
|
+
status: "status";
|
|
406
407
|
alert: "alert";
|
|
407
408
|
banner: "banner";
|
|
408
409
|
button: "button";
|
|
@@ -423,7 +424,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
423
424
|
region: "region";
|
|
424
425
|
row: "row";
|
|
425
426
|
searchbox: "searchbox";
|
|
426
|
-
status: "status";
|
|
427
427
|
switch: "switch";
|
|
428
428
|
tab: "tab";
|
|
429
429
|
table: "table";
|
|
@@ -449,6 +449,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
449
449
|
text: z.ZodOptional<z.ZodString>;
|
|
450
450
|
role: z.ZodOptional<z.ZodEnum<{
|
|
451
451
|
link: "link";
|
|
452
|
+
status: "status";
|
|
452
453
|
alert: "alert";
|
|
453
454
|
banner: "banner";
|
|
454
455
|
button: "button";
|
|
@@ -469,7 +470,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
469
470
|
region: "region";
|
|
470
471
|
row: "row";
|
|
471
472
|
searchbox: "searchbox";
|
|
472
|
-
status: "status";
|
|
473
473
|
switch: "switch";
|
|
474
474
|
tab: "tab";
|
|
475
475
|
table: "table";
|
|
@@ -488,6 +488,7 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
488
488
|
text: z.ZodOptional<z.ZodString>;
|
|
489
489
|
role: z.ZodOptional<z.ZodEnum<{
|
|
490
490
|
link: "link";
|
|
491
|
+
status: "status";
|
|
491
492
|
alert: "alert";
|
|
492
493
|
banner: "banner";
|
|
493
494
|
button: "button";
|
|
@@ -508,7 +509,6 @@ export declare const actionSchema: z.ZodObject<{
|
|
|
508
509
|
region: "region";
|
|
509
510
|
row: "row";
|
|
510
511
|
searchbox: "searchbox";
|
|
511
|
-
status: "status";
|
|
512
512
|
switch: "switch";
|
|
513
513
|
tab: "tab";
|
|
514
514
|
table: "table";
|
package/dist/schema/result.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import type { RunMode } from '../runner/mode.ts';
|
|
2
|
+
import type { EnvironmentManifest } from '../runner/manifest.ts';
|
|
1
3
|
/**
|
|
2
4
|
* Outcome model. The runner emits a `RunResult` per invocation; the reporter
|
|
3
5
|
* consumes it to render the HTML report. Status values map onto the three
|
|
4
6
|
* outcomes the framework distinguishes:
|
|
5
7
|
*
|
|
6
8
|
* - `pass` — action succeeded and screenshot matched baseline (or no baseline).
|
|
7
|
-
* - `changed` — action succeeded but
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* - `changed` — action succeeded but the baseline drifted: the screenshot moved
|
|
10
|
+
* past the threshold, or (CI mode) pixels matched while the accessibility-tree
|
|
11
|
+
* snapshot drifted. The story does not fail. The user reviews and either
|
|
12
|
+
* approves the new baseline or files a bug.
|
|
13
|
+
* - `new` — no baseline existed; one was written this run. Informational, not a
|
|
14
|
+
* regression — there is nothing to compare against yet.
|
|
10
15
|
* - `failed` — a step threw. The story fails fast and skips any later actions.
|
|
11
16
|
*/
|
|
12
17
|
export type ActionStatus = 'pass' | 'changed' | 'failed' | 'skipped' | 'new';
|
|
@@ -41,6 +46,25 @@ export interface ActionResult {
|
|
|
41
46
|
*/
|
|
42
47
|
failedStepIndex?: number;
|
|
43
48
|
failureMessage?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Structured baseline/actual dimensions for the size-mismatch `changed` branch
|
|
51
|
+
* (a screenshot whose baseline and actual differ in size, so no pixel diff can
|
|
52
|
+
* be computed). Carried alongside `failureMessage` — which holds the same fact
|
|
53
|
+
* as prose — so the reporter can render the dimensions in an accessible split
|
|
54
|
+
* idiom (a spoken "W by H pixels" longhand) instead of string-parsing the
|
|
55
|
+
* message. Set ONLY by the size-mismatch branch of `runAction`; absent on every
|
|
56
|
+
* other result, in which case the reporter falls back to `failureMessage`.
|
|
57
|
+
*/
|
|
58
|
+
sizeMismatch?: {
|
|
59
|
+
baseline: {
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
};
|
|
63
|
+
actual: {
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
44
68
|
baselinePath?: string;
|
|
45
69
|
actualPath?: string;
|
|
46
70
|
diffPath?: string;
|
|
@@ -50,8 +74,21 @@ export interface ActionResult {
|
|
|
50
74
|
ssimScore?: number;
|
|
51
75
|
/**
|
|
52
76
|
* `true` when the captured page accessibility tree differs from the
|
|
53
|
-
* baseline tree.
|
|
54
|
-
*
|
|
77
|
+
* baseline tree. Advisory in local mode (flagged, never gates status). In CI
|
|
78
|
+
* mode it DOES gate: when pixels pass but the aria snapshot drifts, the status
|
|
79
|
+
* becomes `changed` and a candidate pair is written, so a drifted committed
|
|
80
|
+
* `a11y.yaml` stays re-approvable under the sole-writer model.
|
|
81
|
+
*
|
|
82
|
+
* DISCRIMINATOR CONTRACT. A11y-only drift is identified POSITIVELY by
|
|
83
|
+
* `a11yChanged === true`, never by the absence of `diffPath` alone. Both
|
|
84
|
+
* pixel-drift branches (SSIM-fail and size-mismatch) carry a `diffPath` /
|
|
85
|
+
* `failureMessage` and deliberately leave `a11yChanged` unset, so the two
|
|
86
|
+
* classes never collide — but the size-mismatch branch's omission of
|
|
87
|
+
* `a11yChanged` is the load-bearing half of that guarantee and is locked by
|
|
88
|
+
* runAction's `never carries a11yChanged` test. Consumers MUST branch on
|
|
89
|
+
* `a11yChanged === true`; a `!diffPath` heuristic would misread a future
|
|
90
|
+
* result that carries neither, or a size-mismatch result if that omission
|
|
91
|
+
* ever regressed.
|
|
55
92
|
*/
|
|
56
93
|
a11yChanged?: boolean;
|
|
57
94
|
/**
|
|
@@ -63,7 +100,13 @@ export interface ActionResult {
|
|
|
63
100
|
/** Path to the accessibility-tree snapshot captured during this run. */
|
|
64
101
|
a11yActualPath?: string;
|
|
65
102
|
}
|
|
66
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Story-wide rollup of its action results. Precedence (worst wins):
|
|
105
|
+
* `failed` > `changed` > `new` > `pass`. A story is `new` only when it wrote at
|
|
106
|
+
* least one fresh baseline and had no `changed`/`failed` action — so a run of
|
|
107
|
+
* first-time stories no longer masquerades as `changed`.
|
|
108
|
+
*/
|
|
109
|
+
export type StoryStatus = 'pass' | 'changed' | 'failed' | 'new';
|
|
67
110
|
export interface StoryResult {
|
|
68
111
|
story: string;
|
|
69
112
|
file: string;
|
|
@@ -81,6 +124,26 @@ export interface CoverageMetric {
|
|
|
81
124
|
ratio: number;
|
|
82
125
|
missing: string[];
|
|
83
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* One orphaned committed baseline: an entry under `paths.baselines` whose action
|
|
129
|
+
* ran no story this run, so nothing compared against it. Recorded (CI mode,
|
|
130
|
+
* unfiltered runs only) so the report can surface it and a later `approve
|
|
131
|
+
* --prune` can delete it — this wave detects, it never removes.
|
|
132
|
+
*
|
|
133
|
+
* Keyed per breakpoint rather than per action so a partially-orphaned action
|
|
134
|
+
* (some breakpoints retired, others still live) could in principle be expressed;
|
|
135
|
+
* today an orphan action is retired wholesale, one entry per baseline file group
|
|
136
|
+
* it left behind. `breakpoint` is the mode name (`mobile`/`desktop`/…) for the
|
|
137
|
+
* breakpoint-keyed `<action>/<breakpoint>.png` layout, or `'legacy'` for the
|
|
138
|
+
* pre-breakpoint `<action>/0.png` layout. `baselinePaths` are the absolute paths
|
|
139
|
+
* the prune step deletes — the PNG plus its a11y companion when one exists — so
|
|
140
|
+
* wave 7 prunes a flat path list without re-deriving layout.
|
|
141
|
+
*/
|
|
142
|
+
export interface DeletedBaseline {
|
|
143
|
+
action: string;
|
|
144
|
+
breakpoint: string;
|
|
145
|
+
baselinePaths: string[];
|
|
146
|
+
}
|
|
84
147
|
/**
|
|
85
148
|
* Parses and shape-checks a `results.json` blob. `approve` re-reads the run's
|
|
86
149
|
* own output, the one JSON re-entry point that skips the zod validation every
|
|
@@ -90,16 +153,72 @@ export interface CoverageMetric {
|
|
|
90
153
|
* `stories[].actions[]` walk, not a full structural mirror.
|
|
91
154
|
*/
|
|
92
155
|
export declare function parseRunResult(raw: string, sourcePath: string): RunResult;
|
|
156
|
+
/**
|
|
157
|
+
* The capture-environment block recorded on every run. Lets the report, the
|
|
158
|
+
* Action, and a human diagnose an environment drift between the committed
|
|
159
|
+
* baselines and the run that compared against them.
|
|
160
|
+
*
|
|
161
|
+
* - `expected` — the committed `<baselines>/manifest.json` when one exists and
|
|
162
|
+
* parses, else `null`. `null` covers both the bootstrap case (no manifest
|
|
163
|
+
* written yet — the first `approve --from` creates it) and local mode, which
|
|
164
|
+
* never reads `paths.baselines` at all.
|
|
165
|
+
* - `actual` — the environment this run actually captured under.
|
|
166
|
+
* - `mismatch` — `true` when a pixel-affecting key diverged (CI mode with a
|
|
167
|
+
* present manifest), or when the committed manifest was unreadable. Always
|
|
168
|
+
* `false` in local mode and on the bootstrap (missing-manifest) case.
|
|
169
|
+
* - `mismatchKeys` — the specific diverging keys (see `PIXEL_AFFECTING_KEYS`),
|
|
170
|
+
* or `['manifest']` when the committed manifest could not be parsed. Empty
|
|
171
|
+
* when `mismatch` is `false`.
|
|
172
|
+
*/
|
|
173
|
+
export interface EnvironmentReport {
|
|
174
|
+
expected: EnvironmentManifest | null;
|
|
175
|
+
actual: EnvironmentManifest;
|
|
176
|
+
mismatch: boolean;
|
|
177
|
+
mismatchKeys: string[];
|
|
178
|
+
}
|
|
93
179
|
export interface RunResult {
|
|
94
180
|
startedAt: string;
|
|
95
181
|
finishedAt: string;
|
|
96
182
|
durationMs: number;
|
|
183
|
+
/**
|
|
184
|
+
* Comparison contract the run executed under (`'ci' | 'local'`). Recorded so
|
|
185
|
+
* the report, the Action, and `approve --from` can distinguish a committed-
|
|
186
|
+
* baseline gate run from an advisory local self-diff. Optional in the type
|
|
187
|
+
* only as a defensive parse guard for older `results.json` artifacts that
|
|
188
|
+
* predate this field; every result the current runner emits carries it.
|
|
189
|
+
*/
|
|
190
|
+
mode?: RunMode;
|
|
97
191
|
totals: {
|
|
98
192
|
stories: number;
|
|
99
193
|
passed: number;
|
|
100
194
|
changed: number;
|
|
101
195
|
failed: number;
|
|
196
|
+
/** Stories whose rollup status is `new` (wrote a fresh baseline, no drift). */
|
|
197
|
+
new: number;
|
|
198
|
+
/**
|
|
199
|
+
* Orphaned committed baselines detected this run — the length of `deleted`.
|
|
200
|
+
* CI mode + unfiltered runs only; `0` in local mode and on any filtered run
|
|
201
|
+
* (where unselected stories' baselines are not orphans, merely unvisited).
|
|
202
|
+
*/
|
|
203
|
+
deleted: number;
|
|
102
204
|
};
|
|
205
|
+
/**
|
|
206
|
+
* Orphaned committed baselines: entries under `paths.baselines` whose action
|
|
207
|
+
* ran no story this run. Populated only for an unfiltered CI run (see
|
|
208
|
+
* {@link DeletedBaseline}); empty in local mode and on filtered runs, where a
|
|
209
|
+
* baseline going unvisited says nothing about whether its story still exists.
|
|
210
|
+
* Detection only — pruning is a later wave.
|
|
211
|
+
*/
|
|
212
|
+
deleted: DeletedBaseline[];
|
|
213
|
+
/**
|
|
214
|
+
* Capture-environment provenance for this run (see {@link EnvironmentReport}).
|
|
215
|
+
* `expected` is the committed manifest (null in local mode / bootstrap),
|
|
216
|
+
* `actual` is what this run captured under, and `mismatch`/`mismatchKeys`
|
|
217
|
+
* drive the report banner and the `3` exit code. Optional in the type only as
|
|
218
|
+
* a defensive parse guard for older `results.json` artifacts that predate this
|
|
219
|
+
* block; every result the current runner emits carries it.
|
|
220
|
+
*/
|
|
221
|
+
environment?: EnvironmentReport;
|
|
103
222
|
/**
|
|
104
223
|
* Custom coverage metrics layered on top of V8 line coverage:
|
|
105
224
|
* `screens` = baselined visit-* actions / declared screens,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"AAmJA;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,UAAkB;IAC5D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,KAAK,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,CAAC,KAAK,CAAC,OAAO,CAAE,MAAgC,CAAC,OAAO,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,2BAA2B,UAAU,+BAA+B;YAClE,0CAA0C,CAC7C,CAAC;IACJ,CAAC;IACD,OAAO,MAAmB,CAAC;AAC7B,CAAC"}
|
|
@@ -15,9 +15,28 @@ export interface BaselinePaths {
|
|
|
15
15
|
legacyBaseline: string;
|
|
16
16
|
/** Pre-breakpoint a11y baseline (`<action>/a11y.yaml`); see `legacyBaseline`. */
|
|
17
17
|
legacyA11yBaseline: string;
|
|
18
|
+
/**
|
|
19
|
+
* Proposed-new-baseline PNG for CI mode, under
|
|
20
|
+
* `<report>/candidates/<action>/<breakpoint>.png`. Mirrors the `baseline`
|
|
21
|
+
* layout exactly (action dir + breakpoint filename) so approving a candidate
|
|
22
|
+
* set is a plain tree copy into `paths.baselines`. Always report-rooted —
|
|
23
|
+
* never affected by the comparison root, which only moves the baseline side.
|
|
24
|
+
*/
|
|
25
|
+
candidate: string;
|
|
26
|
+
/** Candidate a11y snapshot (`<report>/candidates/<action>/<breakpoint>.a11y.yaml`). */
|
|
27
|
+
a11yCandidate: string;
|
|
18
28
|
}
|
|
19
29
|
export interface StoreOptions {
|
|
20
30
|
baselinesDir: string;
|
|
31
|
+
/**
|
|
32
|
+
* Root the baseline-side paths (`baseline`, `a11yBaseline`, and the two
|
|
33
|
+
* `legacy*` fallbacks) resolve under. Defaults to `baselinesDir`, so an
|
|
34
|
+
* omitted root reproduces the committed-baselines layout byte-for-byte — the
|
|
35
|
+
* zero-behaviour-change default. Local mode passes the per-machine cache dir
|
|
36
|
+
* here to self-diff against it instead. Report-side artifacts (actual, diff,
|
|
37
|
+
* candidate, a11yActual, a11yCandidate) ignore this and stay report-rooted.
|
|
38
|
+
*/
|
|
39
|
+
comparisonRoot?: string;
|
|
21
40
|
reportDir: string;
|
|
22
41
|
storyFile: string;
|
|
23
42
|
actionName: string;
|
|
@@ -29,26 +48,89 @@ export interface StoreOptions {
|
|
|
29
48
|
breakpoint: string;
|
|
30
49
|
}
|
|
31
50
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
51
|
+
* The single owner of the `<report>/candidates/` path. The runner writes the
|
|
52
|
+
* candidate tree + a `results.json` copy here, and `approve --from` reads that
|
|
53
|
+
* same tree back. Centralised so the literal `'candidates'` segment has one
|
|
54
|
+
* definition instead of drifting across the runner, the store, and approve.
|
|
55
|
+
*/
|
|
56
|
+
export declare function candidatesDir(reportDir: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* The action-name shape the action schema enforces (`/^[a-z0-9-]+$/`,
|
|
59
|
+
* lowercase-kebab). Re-exported here so the trust boundary in `approve --from`
|
|
60
|
+
* can reject a directory name from an untrusted candidate artifact against the
|
|
61
|
+
* exact same invariant the runner produced its layout under, without importing
|
|
62
|
+
* the zod schema. Kept in lock-step with `schema/action.ts` by intent.
|
|
63
|
+
*/
|
|
64
|
+
export declare const ACTION_NAME_PATTERN: RegExp;
|
|
65
|
+
/**
|
|
66
|
+
* The filesystem-safe breakpoint segment shape (`/^[a-z0-9_-]+$/`) that {@link
|
|
67
|
+
* breakpointSegment} emits. `approve --from` validates a candidate PNG's
|
|
68
|
+
* breakpoint stem against this so a name outside the runner's own output shape
|
|
69
|
+
* (path separators, dots, traversal) can never reach a write.
|
|
70
|
+
*/
|
|
71
|
+
export declare const BREAKPOINT_SEGMENT_PATTERN: RegExp;
|
|
72
|
+
/**
|
|
73
|
+
* Computes deterministic paths for the baseline (the comparison target —
|
|
74
|
+
* committed baselines by default, or the local cache when `comparisonRoot` is
|
|
75
|
+
* supplied), actual (regenerated each run), diff (regenerated when a baseline
|
|
76
|
+
* existed and the diff was non-zero), and candidate (proposed-new-baseline for
|
|
77
|
+
* CI approval) PNGs. Centralised so the runner and the CLI's `approve` command
|
|
78
|
+
* agree on layout.
|
|
36
79
|
*
|
|
37
80
|
* Every path is keyed by both action name and breakpoint so per-breakpoint
|
|
38
|
-
* captures of the same action stay isolated:
|
|
39
|
-
* filename under the action directory
|
|
40
|
-
* report-side artifacts splice
|
|
41
|
-
* (`<action>.<breakpoint>.actual.png`) since they already
|
|
42
|
-
* directory.
|
|
81
|
+
* captures of the same action stay isolated: baseline- and candidate-side paths
|
|
82
|
+
* nest the breakpoint as a filename under the action directory
|
|
83
|
+
* (`<action>/<breakpoint>.png`), while report-side actual/diff artifacts splice
|
|
84
|
+
* it into the filename (`<action>.<breakpoint>.actual.png`) since they already
|
|
85
|
+
* share one per-story directory.
|
|
86
|
+
*
|
|
87
|
+
* The baseline side resolves under `comparisonRoot` (default `baselinesDir`), so
|
|
88
|
+
* omitting the root reproduces the committed-baselines layout unchanged; the
|
|
89
|
+
* candidate and report-side paths are always report-rooted and ignore it.
|
|
43
90
|
*/
|
|
44
91
|
export declare function pathsFor(options: StoreOptions): BaselinePaths;
|
|
45
92
|
export declare function withBaselineLock<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
46
93
|
export declare function readBaseline(path: string): Promise<Buffer | undefined>;
|
|
47
94
|
export declare function readJsonBaseline(path: string): Promise<string | undefined>;
|
|
48
95
|
export declare function writeText(path: string, content: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Losslessly recompresses a PNG buffer. Playwright emits PNGs tuned for capture
|
|
98
|
+
* speed, not size; re-encoding through pngjs applies its default
|
|
99
|
+
* `deflateLevel: 9` plus adaptive per-scanline filtering (filter type chosen per
|
|
100
|
+
* row from the full `[0..4]` set), which typically shrinks the file with zero
|
|
101
|
+
* pixel change. The decode→re-encode round-trip is lossless: pngjs reads and
|
|
102
|
+
* writes 8-bit RGBA, so the recompressed buffer decodes to a byte-identical
|
|
103
|
+
* pixel array (`recompress.test.ts` asserts this).
|
|
104
|
+
*
|
|
105
|
+
* Two safety guarantees:
|
|
106
|
+
* - Never grows a file. A source that is already densely packed (e.g. a
|
|
107
|
+
* future oxipng-optimised baseline flowing through `approve --from`) can
|
|
108
|
+
* re-encode *larger*; in that case the original buffer is returned unchanged.
|
|
109
|
+
* - Never corrupts. Any decode/encode failure (a non-PNG buffer, a codec edge
|
|
110
|
+
* case) is swallowed and the original buffer is returned, so a recompress
|
|
111
|
+
* miss degrades to "write the bytes we were given" rather than a torn file.
|
|
112
|
+
*
|
|
113
|
+
* This is the single lossless-recompress seam for every PNG the tool writes:
|
|
114
|
+
* `writePng` routes through it, so baseline, candidate, cache, actual, and diff
|
|
115
|
+
* writes all inherit it without per-callsite wiring. Deterministic — pngjs's
|
|
116
|
+
* encoder is a pure function of the pixel data and the fixed options above.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately NOT palette-quantised and never lossy: colour type and bit depth
|
|
119
|
+
* are preserved by the RGBA round-trip.
|
|
120
|
+
*/
|
|
121
|
+
export declare function recompressPng(png: Buffer): Buffer;
|
|
49
122
|
export declare function writePng(path: string, png: Buffer): Promise<void>;
|
|
50
123
|
export declare function deleteIfExists(path: string): Promise<void>;
|
|
51
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Copies an already-written PNG (the run's `actual`, or a `candidate`) to a new
|
|
126
|
+
* destination verbatim. No recompress here on purpose: the source was produced
|
|
127
|
+
* by `writePng`, which already ran `recompressPng`, so the bytes on disk are the
|
|
128
|
+
* recompressed ones. Copying them forward keeps the destination losslessly
|
|
129
|
+
* recompressed for free. The only non-test caller is `approve` refreshing the
|
|
130
|
+
* per-machine cache; `approve --from` does NOT use this — it reads each source
|
|
131
|
+
* once during validation and writes the retained buffer through {@link writePng}.
|
|
132
|
+
*/
|
|
133
|
+
export declare function copyRecompressedPng(actualPath: string, destinationPath: string): Promise<void>;
|
|
52
134
|
/**
|
|
53
135
|
* Turns an absolute screenshot path into a path that is relative to the
|
|
54
136
|
* generated report directory so the HTML report can reference it with a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baselineStore.d.ts","sourceRoot":"","sources":["../../src/screenshots/baselineStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"baselineStore.d.ts","sourceRoot":"","sources":["../../src/screenshots/baselineStore.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CA6C7D;AAiBD,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO5E;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWjD;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER"}
|