renderdiff 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/report.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import sharp from "sharp";
4
+ import { fmtComputed, groupFindings } from "./computed.js";
3
5
  import { CONTEXT_OPTIONS, name } from "./config.js";
4
- import { floor4, fmtBand, fmtElements, fmtFailed, fmtRegion, pct, signed } from "./format.js";
6
+ import { floor4, fmtBand, fmtElements, fmtFailed, fmtLine, fmtRegion, pct, signed, } from "./format.js";
5
7
  /** Sort ascending by score; surface the mean and the worst (lowest-scoring) row. */
6
8
  export function stats(rows) {
7
9
  const sorted = [...rows].sort((a, b) => a.score - b.score);
@@ -25,8 +27,10 @@ export function writeResults(results, meta, outDir, stale) {
25
27
  belowThreshold: meta.threshold === null
26
28
  ? 0
27
29
  : compared.filter((r) => !r.accepted && r.score < meta.threshold).length,
30
+ differing: open.filter((r) => r.diffPixels > 0).length,
28
31
  accepted: compared.filter((r) => r.accepted).length,
29
32
  stale,
33
+ ...(compared.some((r) => "computed" in r) ? { computed: groupFindings(results) } : {}),
30
34
  },
31
35
  };
32
36
  const rows = results.map((r) => ` ${JSON.stringify(r, round)}`).join(",\n");
@@ -61,7 +65,8 @@ function section(rows) {
61
65
  const LIST_CAP = 5;
62
66
  const TEXT_CAP = 20;
63
67
  const dom = (els) => els?.length ? `<span class="dom"> — ${esc(fmtElements(els))}</span>` : "";
64
- function findings(r) {
68
+ const cropLink = (c) => `<a class="crop" href="${esc(c.path)}" target="_blank" title="${c.kind}, left ${c.layout === "stacked" ? "on top" : "on the left"}"><img loading="lazy" src="${esc(c.path)}"></a>`;
69
+ function findings(r, crop = cropLink) {
65
70
  const items = [];
66
71
  const failed = fmtFailed(r.failed);
67
72
  if (failed)
@@ -69,13 +74,11 @@ function findings(r) {
69
74
  if (r.regions.length) {
70
75
  items.push(`<p>${r.regions.length} region(s)</p><ul class="regions">${r.regions
71
76
  .slice(0, LIST_CAP)
72
- .map((g) => `<li>${fmtRegion(g)} — ${g.pixels.toLocaleString("en-US")} px${dom(g.elements)}</li>`)
77
+ .map((g) => `<li>${fmtRegion(g)} — ${g.pixels.toLocaleString("en-US")} px${g.line ? `, ${fmtLine(g.line)}` : ""}${dom(g.elements)}</li>`)
73
78
  .join("")}</ul>`);
74
79
  }
75
80
  if (r.crops.length) {
76
- items.push(`<div class="crops">${r.crops
77
- .map((c) => `<a class="crop" href="${c.path}" target="_blank" title="${c.kind}, left ${c.layout === "stacked" ? "on top" : "on the left"}"><img loading="lazy" src="${c.path}"></a>`)
78
- .join("")}</div>`);
81
+ items.push(`<div class="crops">${r.crops.map(crop).join("")}</div>`);
79
82
  }
80
83
  if (r.bands?.length) {
81
84
  items.push(`<ul class="bands">${r.bands.map((b) => `<li>band ${fmtBand(b)}${dom(b.elements)}</li>`).join("")}</ul>`);
@@ -94,10 +97,7 @@ function findings(r) {
94
97
  const reasons = (r) => [
95
98
  ...new Set([...(r.bands ?? []), ...r.regions].flatMap((g) => (g.accepted ? [g.accepted] : []))),
96
99
  ].join("; ");
97
- function card(r) {
98
- const imgs = r.images;
99
- return `
100
- <article class="card">
100
+ const head = (r) => `
101
101
  <header>
102
102
  <span class="score ${band(r.score)}">${pct(r.score)}</span>
103
103
  <code>${esc(name(r))}</code>
@@ -108,8 +108,13 @@ function card(r) {
108
108
  ${r.widthDelta ? `<span class="delta">width ${signed(r.widthDelta)}px</span>` : ""}
109
109
  ${r.unstablePixels ? `<span class="unstable">${r.unstablePixels.toLocaleString("en-US")} px unstable</span>` : ""}
110
110
  ${r.alignedScore !== null ? `<span class="aligned">aligned ${pct(r.alignedScore)}</span>` : ""}
111
+ ${r.computed ? `<span class="w">${r.computed.dom ? `dom differs (${r.computed.dom})` : "dom identical"}</span>` : ""}
111
112
  <span class="px">${r.diffPixels.toLocaleString()} / ${r.totalPixels.toLocaleString()} px differ</span>
112
- </header>
113
+ </header>`;
114
+ function card(r) {
115
+ const imgs = r.images;
116
+ return `
117
+ <article class="card">${head(r)}
113
118
  ${findings(r)}
114
119
  <div class="grid">
115
120
  <figure><figcaption>left</figcaption><a href="${imgs.left}" target="_blank"><img loading="lazy" src="${imgs.left}"></a></figure>
@@ -118,13 +123,7 @@ function card(r) {
118
123
  </div>
119
124
  </article>`;
120
125
  }
121
- export async function writeReport(results, meta, cfg, stale) {
122
- const compared = results.filter((r) => r.status === "compared");
123
- const errors = results.filter((r) => r.status === "error");
124
- const html = `<!doctype html>
125
- <meta charset="utf-8">
126
- <title>Visual diff</title>
127
- <style>
126
+ const STYLE = `<style>
128
127
  :root { color-scheme: light dark; --bg:#fff; --fg:#1a1a1a; --mut:#666; --line:#e3e3e3; --card:#fafafa; }
129
128
  @media (prefers-color-scheme: dark) { :root { --bg:#16181c; --fg:#e8e8e8; --mut:#999; --line:#2c2f36; --card:#1c1f24; } }
130
129
  * { box-sizing: border-box }
@@ -160,56 +159,197 @@ export async function writeReport(results, meta, cfg, stale) {
160
159
  code { font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace }
161
160
  details { margin:1rem 0; color:var(--mut) }
162
161
  ul { margin:.5rem 0; padding-left:1.25rem }
163
- </style>
164
-
165
- <h1>Visual diff</h1>
166
- <p class="meta">
167
- left <code>${esc(meta.left)}</code> vs right <code>${esc(meta.right)}</code><br>
168
- ${esc(meta.generatedAt)} &middot; ${esc(meta.browser.name)} ${esc(meta.browser.version)}${["left", "right"]
169
- .filter((s) => meta.storageState[s])
170
- .map((s) => ` &middot; ${s} storage state <code>${esc(meta.storageState[s])}</code>`)
171
- .join("")}
172
- ${meta.rightReplayedFrom ? `&middot; right replayed from a recording of <code>${esc(meta.rightReplayedFrom)}</code>` : ""}
173
- </p>
174
-
175
- ${section(compared)}
176
-
177
- ${errors.length
178
- ? `<section><h2 class="bad">Errors</h2>
162
+ </style>`;
163
+ function computedSection(results) {
164
+ const lines = fmtComputed(results, [...new Set(results.map(name))]);
165
+ return lines.length
166
+ ? `<section><h2>Computed styles</h2><pre class="computed">${esc(lines.join("\n"))}</pre></section>`
167
+ : "";
168
+ }
169
+ const listed = (errors, stale) => (errors.length
170
+ ? `<section><h2 class="bad">Errors</h2>
179
171
  <p class="blurb">These produced no measurement, so they are neither a pass nor a fail.</p>
180
172
  <ul>${errors.map((r) => `<li><code>${esc(name(r))}</code> @${r.width}px — ${esc(r.error)}</li>`).join("")}</ul></section>`
181
- : ""}
182
-
183
- ${stale.length
173
+ : "") +
174
+ (stale.length
184
175
  ? `<section><h2>Stale accepted differences</h2>
185
176
  <p class="blurb">Accepted, but not seen on this run: prune them from the accepted file.</p>
186
177
  <ul>${stale.map((a) => `<li><code>${esc(a.route)}</code> @${a.width}px — ${esc(a.reason)}</li>`).join("")}</ul></section>`
187
- : ""}
188
-
189
- <details>
178
+ : "");
179
+ const neutralized = (meta) => `<details>
190
180
  <summary>What was neutralized</summary>
191
181
  <p>Blocked on both sides — analytics, consent, and A/B tooling that would otherwise
192
182
  rewrite the DOM differently on each load:</p>
193
- <ul>${cfg.blockUrlPatterns.map((p) => `<li><code>${esc(p)}</code></li>`).join("")}</ul>
183
+ <ul>${meta.blockUrlPatterns.map((p) => `<li><code>${esc(p)}</code></li>`).join("")}</ul>
194
184
  <p>Masked, because they drift — painted over on both sides, so they can neither pass
195
185
  nor fail:</p>
196
- ${cfg.masks.length
197
- ? `<ul>${cfg.masks.map((m) => `<li>${esc(m.reason)}</li>`).join("")}</ul>`
198
- : `<p><em>None configured.</em></p>`}
186
+ ${meta.masks.length
187
+ ? `<ul>${meta.masks.map((m) => `<li>${esc(m.reason)}</li>`).join("")}</ul>`
188
+ : `<p><em>None configured.</em></p>`}
199
189
  <p>Pinned on both sides: clock starts at <code>${esc(meta.clock)}</code> on every load, a seeded
200
190
  <code>Math.random</code>, timezone <code>${CONTEXT_OPTIONS.timezoneId}</code>, locale
201
191
  <code>${CONTEXT_OPTIONS.locale}</code>, <code>${CONTEXT_OPTIONS.colorScheme}</code> colour
202
- scheme, reduced motion, CSS animations and transitions off, videos paused at frame 0.</p>
192
+ scheme, reduced motion, CSS animations and transitions off${meta.freezeVideo ? ", videos kept from playing with video.js overlays hidden" : ""}.</p>
203
193
  <p>Unstable pixels — ones that changed between two shots of the same load — are painted
204
194
  magenta on both sides, counted on each card, and excluded from the measurement. A route
205
195
  more than 25% unstable is an error.</p>
206
196
  <p>Magenta is also padding where one page is taller or wider than the other. It counts
207
197
  against the score on purpose — see the height delta on the row.</p>
208
- </details>
198
+ </details>`;
199
+ export async function writeReport(results, meta, cfg, stale) {
200
+ const compared = results.filter((r) => r.status === "compared");
201
+ const errors = results.filter((r) => r.status === "error");
202
+ const html = `<!doctype html>
203
+ <meta charset="utf-8">
204
+ <title>Visual diff</title>
205
+ ${STYLE}
206
+
207
+ <h1>Visual diff</h1>
208
+ <p class="meta">
209
+ left <code>${esc(meta.left)}</code> vs right <code>${esc(meta.right)}</code><br>
210
+ ${esc(meta.generatedAt)} &middot; ${esc(meta.browser.name)} ${esc(meta.browser.version)}${["left", "right"]
211
+ .filter((s) => meta.storageState[s])
212
+ .map((s) => ` &middot; ${s} storage state <code>${esc(meta.storageState[s])}</code>`)
213
+ .join("")}
214
+ ${meta.rightReplayedFrom ? `&middot; right replayed from a recording of <code>${esc(meta.rightReplayedFrom)}</code>` : ""}
215
+ </p>
216
+
217
+ ${computedSection(results)}
218
+
219
+ ${section(compared)}
220
+
221
+ ${listed(errors, stale)}
222
+
223
+ ${neutralized(meta)}
209
224
  `;
210
225
  fs.mkdirSync(cfg.outDir, { recursive: true });
211
226
  const out = path.join(cfg.outDir, "report.html");
212
227
  fs.writeFileSync(out, html);
213
228
  return out;
214
229
  }
230
+ // ponytail: hard 12MB ceiling on embedded crops (base64) to fit a 16MB Artifact; split files if runs hit it.
231
+ const EVIDENCE_BUDGET = 12_000_000;
232
+ /** One self-contained page for a ticket: the verdict, the banner and the non-clean shots' crops. */
233
+ export async function writeEvidence({ meta, results, summary }, outDir, verdict, title = "Visual parity evidence") {
234
+ // Lazy, so importing the package's types never loads Playwright.
235
+ const { compact, fails, reusable } = await import("./run.js");
236
+ const routes = [...new Set(results.map(name))];
237
+ const compared = results.filter((r) => r.status === "compared");
238
+ const errors = results.filter((r) => r.status === "error");
239
+ const byScore = (a, b) => a.score - b.score;
240
+ const cards = compared.filter((r) => !reusable(r)).sort(byScore);
241
+ const acceptedCards = compared.filter((r) => r.accepted).sort(byScore);
242
+ const worst = new Map();
243
+ for (const r of cards)
244
+ if (r.crops[0] && !worst.has(name(r)))
245
+ worst.set(name(r), r.crops[0]);
246
+ const embedded = new Map();
247
+ let used = 0;
248
+ let omitted = 0;
249
+ const all = [...worst.values(), ...[...cards, ...acceptedCards].flatMap((r) => r.crops)];
250
+ for (const c of new Set(all)) {
251
+ let webp;
252
+ try {
253
+ webp = await sharp(path.join(outDir, c.path))
254
+ .resize({ width: 1000, withoutEnlargement: true })
255
+ .webp({ quality: 80 })
256
+ .toBuffer();
257
+ }
258
+ catch {
259
+ omitted++;
260
+ continue;
261
+ }
262
+ const uri = `data:image/webp;base64,${webp.toString("base64")}`;
263
+ if (used + uri.length > EVIDENCE_BUDGET) {
264
+ omitted++;
265
+ continue;
266
+ }
267
+ used += uri.length;
268
+ embedded.set(c, uri);
269
+ }
270
+ const crop = (c) => embedded.has(c)
271
+ ? `<img src="${embedded.get(c)}" title="${c.kind}, left ${c.layout === "stacked" ? "on top" : "on the left"}">`
272
+ : `<p class="omitted">crop omitted</p>`;
273
+ const open = compared.filter((r) => !r.accepted);
274
+ const count = (n, what) => (n ? [`${n} ${what}`] : []);
275
+ const notes = [
276
+ ...count(errors.length, "error(s)"),
277
+ ...count(open.filter((r) => r.unstablePixels).length, "shot(s) with unstable pixels"),
278
+ ...count(open.filter((r) => fmtFailed(r.failed)).length, "shot(s) with failed assets"),
279
+ ...count(open.filter((r) => r.diffPixels > 0 && !fails(r, meta.threshold)).length, "shot(s) differ under threshold"),
280
+ ...count(meta.rerun?.reused ?? 0, "shot(s) reused by --rerun-failed"),
281
+ ...count(omitted, "crop(s) omitted (over the size budget or missing)"),
282
+ ];
283
+ const [cls, label] = compared.some((r) => fails(r, meta.threshold))
284
+ ? ["bad", "Differs"]
285
+ : !results.length || meta.rerun?.reused || !results.every(reusable)
286
+ ? ["warn", "Not verified"]
287
+ : ["good", "Parity"];
288
+ const shots = (route) => results.filter((r) => name(r) === route);
289
+ const clean = routes.filter((rt) => shots(rt).every((r) => r.status === "compared" && !r.accepted && reusable(r)));
290
+ const accepted = routes.filter((rt) => !clean.includes(rt) && shots(rt).every(reusable));
291
+ const why = (rt) => [...new Set(compared.filter((r) => name(r) === rt && r.accepted).map(reasons))].join("; ");
292
+ const html = `<!doctype html>
293
+ <meta charset="utf-8">
294
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data:; style-src 'unsafe-inline'">
295
+ <title>${esc(title)}</title>
296
+ ${STYLE}
297
+ <style>
298
+ .banner { font-size:1.2rem; font-weight:700; padding:.6rem 1rem; border-radius:8px;
299
+ border:2px solid currentColor; margin:0 0 .5rem }
300
+ .verdict { white-space:pre-wrap; border-left:3px solid var(--line); padding-left:1rem }
301
+ .crops img { max-height:none }
302
+ .omitted { font-style:italic }
303
+ </style>
304
+
305
+ <h1>${esc(title)}</h1>
306
+ <p class="meta">
307
+ left <code>${esc(meta.left)}</code> vs right <code>${esc(meta.right)}</code><br>
308
+ ${esc(meta.generatedAt)} &middot; renderdiff ${esc(meta.version)} &middot;
309
+ widths ${meta.widths.join(", ")} &middot;
310
+ threshold ${meta.threshold === null ? "none" : pct(meta.threshold)} &middot;
311
+ ${esc(meta.platform ?? "unknown platform")} &middot; ${esc(meta.browser.name)} ${esc(meta.browser.version)}
312
+ </p>
313
+
314
+ <div class="banner ${cls}">${label}</div>
315
+ ${notes.length ? `<p class="blurb">${esc(notes.join(" · "))}</p>` : ""}
316
+ <p><code>${esc(compact(results, routes, "results.json", meta.threshold).split("\n").at(-1))}</code></p>
317
+
318
+ <section><h2>Verdict</h2>
319
+ ${verdict?.trim() ? `<div class="verdict">${esc(verdict.trim())}</div>` : `<p class="blurb"><em>No verdict attached.</em></p>`}
320
+ </section>
321
+
322
+ <section><h2>Coverage</h2>
323
+ <details><summary>${clean.length} clean route(s)</summary>
324
+ <ul>${clean.map((rt) => `<li><code>${esc(rt)}</code></li>`).join("")}</ul></details>
325
+ ${accepted.length
326
+ ? `<p>Accepted:</p><ul>${accepted.map((rt) => `<li><code>${esc(rt)}</code> — ${esc(why(rt))}</li>`).join("")}</ul>`
327
+ : ""}
328
+ </section>
329
+
330
+ ${listed(errors, summary.stale)}
331
+
332
+ ${computedSection(results)}
333
+
334
+ ${cards.length
335
+ ? `<section><h2>Not clean</h2>${cards
336
+ .map((r) => `<article class="card">${head(r)}${findings(r, crop)}</article>`)
337
+ .join("\n")}</section>`
338
+ : ""}
339
+
340
+ ${acceptedCards.length
341
+ ? `<section><h2>Accepted</h2>${acceptedCards
342
+ .map((r) => `<article class="card">${head(r)}${findings(r, crop)}</article>`)
343
+ .join("\n")}</section>`
344
+ : ""}
345
+
346
+ ${neutralized(meta)}
347
+ `;
348
+ const out = path.join(outDir, "evidence.html");
349
+ fs.writeFileSync(out, html);
350
+ if (Buffer.byteLength(html) > 16 * 1024 * 1024) {
351
+ console.warn("evidence.html is over 16 MB: too big to publish as an Artifact");
352
+ }
353
+ return out;
354
+ }
215
355
  //# sourceMappingURL=report.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
package/dist/run.d.ts CHANGED
@@ -14,6 +14,9 @@ interface RunOpts {
14
14
  /** Replay the right side's traffic from the out dir's recording, or record one. */
15
15
  cacheRight: boolean;
16
16
  }
17
+ export declare const reusable: (r: RouteResult) => boolean;
18
+ /** A shot that exits 1: unaccepted and differing, or with a threshold, scoring below it. */
19
+ export declare const fails: (r: RouteResult, threshold: number | null) => boolean;
17
20
  export declare function run({ routes, cfg, open, progress, accept, rerun, acceptedFile, cacheRight, }: RunOpts): Promise<number>;
18
21
  /**
19
22
  * The non-TTY summary, sized for an agent's context: clean routes on one line, then each
package/dist/run.js CHANGED
@@ -4,16 +4,19 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { setTimeout as sleep } from "node:timers/promises";
6
6
  import { chromium, errors } from "playwright";
7
- import { applyAccepted, readAccepted, updateAccepted } from "./accept.js";
7
+ import { applyAccepted, readAccepted, unacceptable, updateAccepted } from "./accept.js";
8
8
  import { CACHE_DIR, cacheFile, replayable, REPLAY_TTL, saveRecording, shotId } from "./cache.js";
9
9
  import { cancelUnstable, capture, HookError, prepare } from "./capture.js";
10
10
  import { compare, crops } from "./compare.js";
11
- import { locate } from "./locate.js";
11
+ import { diffSnapshots, fmtComputed, pageError } from "./computed.js";
12
+ import { locate, snapshot } from "./locate.js";
12
13
  import { CONTEXT_OPTIONS, label, masksFor, name, selectorText, version, } from "./config.js";
13
- import { fmtBand, fmtElements, fmtFailed, fmtRegion, pct, signed } from "./format.js";
14
+ import { fmtBand, fmtElements, fmtFailed, fmtLine, fmtRegion, pct, signed } from "./format.js";
14
15
  import { stats, writeReport, writeResults, } from "./report.js";
15
16
  /** A busy main thread can stall the probe; the elements are a hint, so give up rather than wait. */
16
17
  const LOCATE_TIMEOUT = 5_000;
18
+ /** A full-page walk reads every element's style; past this, report the snapshot as failed. */
19
+ const COMPUTED_TIMEOUT = 15_000;
17
20
  /** Past this share of a page unstable, too little was measured for a score to mean anything. */
18
21
  const MAX_UNSTABLE_SHARE = 0.25;
19
22
  const slug = (url) => url === "/"
@@ -43,6 +46,7 @@ function slugs(routes) {
43
46
  }
44
47
  /** Only a directory holding nothing but files renderdiff writes is safe to wipe. */
45
48
  const ours = (f) => f === "report.html" ||
49
+ f === "evidence.html" ||
46
50
  f === "results.json" ||
47
51
  f === CACHE_DIR ||
48
52
  /^[a-z0-9_~@-]+\.[\d.]+\.(left|right|diff|c\d+)\.png$/.test(f);
@@ -53,8 +57,12 @@ function wipeable(dir) {
53
57
  }
54
58
  /** A stylesheet or font face failed on a side; older results.json files lack the field. */
55
59
  const broken = (r) => !!(r.failed?.left.length || r.failed?.right.length);
56
- const reusable = (r) => r.status === "compared" &&
60
+ export const reusable = (r) => r.status === "compared" &&
57
61
  ((r.diffPixels === 0 && r.unstablePixels === 0 && !broken(r)) || r.accepted);
62
+ /** A shot that exits 1: unaccepted and differing, or with a threshold, scoring below it. */
63
+ export const fails = (r, threshold) => r.status === "compared" &&
64
+ !r.accepted &&
65
+ (threshold === null ? r.diffPixels > 0 : r.score < threshold);
58
66
  const files = (r) => r.status === "compared" ? [...Object.values(r.images), ...r.crops.map((c) => c.path)] : [];
59
67
  /** Everything that decides how a shot is taken, hook and `locate` sources included. */
60
68
  function configHash(cfg, routes) {
@@ -83,7 +91,7 @@ function previous(file, now) {
83
91
  };
84
92
  for (const [key, value] of Object.entries(now)) {
85
93
  if (key === "config" && value !== was[key]) {
86
- return (`the config (masks, hooks, hidden selectors, blocked URLs, CSS, storage state) ` +
94
+ return (`the config (masks, hooks, hidden selectors, blocked URLs, CSS, storage state, --computed) ` +
87
95
  `differs from the one that wrote ${file}; run without --rerun-failed to start over`);
88
96
  }
89
97
  if (JSON.stringify(value) !== JSON.stringify(was[key])) {
@@ -102,6 +110,8 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
102
110
  blockUrlPatterns: cfg.blockUrlPatterns,
103
111
  hideSelectors: cfg.hideSelectors,
104
112
  masks: cfg.masks.map(({ urlPath, reason }) => ({ urlPath, reason })),
113
+ freezeVideo: cfg.freezeVideo,
114
+ platform: process.platform,
105
115
  storageState: {
106
116
  left: cfg.storageState.left && path.basename(cfg.storageState.left),
107
117
  right: cfg.storageState.right && path.basename(cfg.storageState.right),
@@ -257,6 +267,18 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
257
267
  ]),
258
268
  ]);
259
269
  named?.forEach((els, i) => (spots[i].target.elements = els));
270
+ const snap = (page, side) => snapshot(page, masksFor(cfg, page, route), COMPUTED_TIMEOUT, route.selector ? page.locator(route.selector[side]) : null).catch((e) => {
271
+ throw new Error(pageError(e));
272
+ });
273
+ const computed = cfg.computed
274
+ ? await Promise.race([
275
+ Promise.all([snap(leftPage, "left"), snap(rightPage, "right")]).then(([l, r]) => ({ computed: diffSnapshots(l, r) }), (e) => ({ computed: null, computedError: e.message })),
276
+ sleep(COMPUTED_TIMEOUT + 5_000, null, { ref: false }).then(() => ({
277
+ computed: null,
278
+ computedError: `took over ${(COMPUTED_TIMEOUT + 5_000) / 1000}s`,
279
+ })),
280
+ ])
281
+ : null;
260
282
  const cropped = made.map(({ png, ...c }, i) => ({ path: `${base}.c${i + 1}.png`, ...c }));
261
283
  await Promise.all([
262
284
  write(images.left, leftShot.png),
@@ -275,6 +297,7 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
275
297
  crops: cropped,
276
298
  accepted: false,
277
299
  failed: { left: leftShot.failed, right: rightShot.failed },
300
+ ...computed,
278
301
  ...(replayedAt ? { rightReplayedFrom: new Date(replayedAt).toISOString() } : {}),
279
302
  });
280
303
  const { regions, bands, alignedScore, text } = metrics;
@@ -286,6 +309,7 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
286
309
  regions.length
287
310
  ? `${regions.length} region${regions.length > 1 ? "s" : ""}, largest ${fmtRegion(regions[0])}`
288
311
  : "",
312
+ ...more(regions.flatMap((g) => (g.line ? [fmtLine(g.line)] : []))),
289
313
  ...(bands ?? []).map((b) => `band ${fmtBand(b)}`),
290
314
  alignedScore !== null ? `aligned ${pct(alignedScore)}` : "",
291
315
  bands === null ? "rows too different to align" : "",
@@ -293,6 +317,12 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
293
317
  ? `text: ${text.added.length} added, ${text.removed.length} removed`
294
318
  : "",
295
319
  dom.length ? `dom ${fmtElements(dom)}` : "",
320
+ computed && !computed.computed
321
+ ? `computed: snapshot failed, ${computed.computedError}`
322
+ : "",
323
+ computed?.computed
324
+ ? `computed ${computed.computed.dom ? `dom differs (${computed.computed.dom})` : "dom identical"}, ${computed.computed.changes.length + computed.computed.truncated} change(s)`
325
+ : "",
296
326
  ].filter(Boolean);
297
327
  ++done;
298
328
  if (!progress)
@@ -360,6 +390,18 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
360
390
  ({ stale } = applyAccepted(results, readAccepted(acceptedFile)));
361
391
  }
362
392
  console.log(`accepted ${added.length} difference(s), pruned ${pruned} stale — ${acceptedFile}`);
393
+ const why = new Map();
394
+ for (const r of results) {
395
+ const w = r.status === "compared" && r.diffPixels > 0 && !r.accepted && unacceptable(r);
396
+ if (w)
397
+ why.set(w, (why.get(w) ?? 0) + 1);
398
+ }
399
+ const skipped = [...why.values()].reduce((a, b) => a + b, 0);
400
+ if (skipped) {
401
+ console.log(`not accepted: ${skipped} differing shot(s) can't be (` +
402
+ [...why].map(([w, n]) => `${n} ${w}`).join(", ") +
403
+ `) — reference.md § Masks, accepts and thresholds`);
404
+ }
363
405
  }
364
406
  const { left, right, widths, ...rest } = setup;
365
407
  const meta = {
@@ -375,6 +417,7 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
375
417
  browser: browserMeta,
376
418
  config,
377
419
  rightReplayedFrom: replays.sort()[0] ?? null,
420
+ rerun: prev ? { rerun: jobs.length, reused: results.length - jobs.length } : null,
378
421
  };
379
422
  const reportPath = await writeReport(results, meta, cfg, stale);
380
423
  const resultsPath = writeResults(results, meta, cfg.outDir, stale);
@@ -395,17 +438,20 @@ export async function run({ routes, cfg, open, progress, accept, rerun, accepted
395
438
  console.log(`stale accepted: ${stale.map((a) => `${a.route}@${a.width}`).join(" ")} — ${acceptedFile}`);
396
439
  }
397
440
  if (progress)
398
- summarize(results, reportPath, resultsPath);
441
+ summarize(results, names, reportPath, resultsPath);
399
442
  else
400
443
  console.log(compact(results, names, resultsPath, cfg.threshold));
401
444
  if (open) {
402
445
  const opener = process.platform === "darwin" ? "open" : "xdg-open";
403
446
  spawn(opener, [reportPath], { detached: true, stdio: "ignore" }).unref();
404
447
  }
405
- // A measured failure outranks an unmeasured route, so an error can't hide a threshold failure.
406
- const below = results.filter((r) => r.status === "compared" && !r.accepted && cfg.threshold !== null && r.score < cfg.threshold);
448
+ // A measured failure outranks an unmeasured route, so an error can't hide a diff.
449
+ const below = results.filter((r) => fails(r, cfg.threshold));
407
450
  if (below.length) {
408
- console.error(`\n✗ ${below.length} route(s) below ${pct(cfg.threshold)}.`);
451
+ const n = new Set(below.map(name)).size;
452
+ const what = cfg.threshold === null ? (n === 1 ? "differs" : "differ") : `below ${pct(cfg.threshold)}`;
453
+ const shots = `${below.length} shot${below.length === 1 ? "" : "s"}`;
454
+ console.error(`\n✗ ${n} route${n === 1 ? "" : "s"} ${what} (${shots}).`);
409
455
  }
410
456
  return below.length ? 1 : results.some((r) => r.status === "error") ? 3 : 0;
411
457
  }
@@ -449,7 +495,7 @@ async function shoot(page, url, route, side, width, cfg, now) {
449
495
  return await capture(page, url, route, side, width, cfg, now);
450
496
  }
451
497
  }
452
- function summarize(results, reportPath, resultsPath) {
498
+ function summarize(results, routes, reportPath, resultsPath) {
453
499
  const compared = results.filter((r) => r.status === "compared");
454
500
  const open = compared.filter((r) => !r.accepted);
455
501
  if (open.length) {
@@ -470,6 +516,9 @@ function summarize(results, reportPath, resultsPath) {
470
516
  for (const e of errors)
471
517
  console.log(` ${name(e)}@${e.width}: ${e.error}`);
472
518
  }
519
+ const computed = fmtComputed(results, routes);
520
+ if (computed.length)
521
+ console.log(`\n${computed.join("\n")}`);
473
522
  console.log(`\nReport: ${reportPath}`);
474
523
  console.log(`Results: ${resultsPath}`);
475
524
  }
@@ -552,6 +601,7 @@ export function compact(results, routes, resultsPath, threshold) {
552
601
  out.unshift(`accepted: ${accepted.join(" ")}`);
553
602
  if (clean.length)
554
603
  out.unshift(`clean: ${clean.join(" ")}`);
604
+ out.push(...fmtComputed(results, routes));
555
605
  const all = results.filter((r) => r.status === "compared");
556
606
  const compared = all.filter((r) => !r.accepted);
557
607
  const shots = compared.filter((r) => r.diffPixels > 0).length;
@@ -566,6 +616,8 @@ export function compact(results, routes, resultsPath, threshold) {
566
616
  ` — ${resultsPath}`);
567
617
  return out.join("\n");
568
618
  }
619
+ /** The first three, and how many more there are. */
620
+ const more = (lines) => lines.length > 3 ? [...lines.slice(0, 3), `(+${lines.length - 3} more)`] : lines;
569
621
  /** One compact row for comparisons that agree on everything but score, regions and unstable px. */
570
622
  function row(g) {
571
623
  const [r] = g;
@@ -573,7 +625,14 @@ function row(g) {
573
625
  const [lo, hi] = [Math.min(...ns), Math.max(...ns)];
574
626
  return lo === hi ? f(lo) : `${f(lo)}–${f(hi)}`;
575
627
  };
576
- const regions = g.map((x) => x.regions.length);
628
+ const drawn = (c) => c.line && !c.accepted;
629
+ const regions = g.map((x) => x.regions.filter((c) => !drawn(c)).length);
630
+ const lined = g.find((x) => x.regions.some(drawn)) ?? r;
631
+ const ys = (x) => x.regions
632
+ .filter(drawn)
633
+ .map((c) => c.line.y)
634
+ .join();
635
+ const approx = new Set(g.map(ys)).size > 1;
577
636
  const accepted = g.map((x) => [...(x.bands ?? []), ...x.regions].filter((c) => c.accepted).length);
578
637
  const unstable = Math.max(...g.map((x) => x.unstablePixels));
579
638
  // Band y varies with width; a collapsed row shows the first one's, marked approximate.
@@ -586,6 +645,7 @@ function row(g) {
586
645
  r.bands === null ? "unaligned" : "",
587
646
  ...(r.bands ?? []).map((b) => `band ${b.side === "left" ? "+" : "-"}${b.h}${y(b)}`),
588
647
  r.alignedScore !== null ? `aligned ${pct(r.alignedScore)}` : "",
648
+ ...more(lined.regions.flatMap((c) => (drawn(c) ? [fmtLine(c.line, approx)] : []))),
589
649
  Math.max(...regions) ? `${span(regions, String)} reg` : "",
590
650
  unstable ? `unstable ${unstable.toLocaleString("en-US")}px` : "",
591
651
  Math.max(...accepted) ? `${span(accepted, String)} accepted` : "",