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/README.md +39 -104
- package/dist/accept.d.ts +3 -1
- package/dist/accept.js +9 -1
- package/dist/accept.js.map +1 -1
- package/dist/cache.js +9 -1
- package/dist/cache.js.map +1 -1
- package/dist/capture.js +46 -10
- package/dist/capture.js.map +1 -1
- package/dist/cli.js +65 -5
- package/dist/cli.js.map +1 -1
- package/dist/compare.d.ts +10 -0
- package/dist/compare.js +51 -8
- package/dist/compare.js.map +1 -1
- package/dist/computed.d.ts +54 -0
- package/dist/computed.js +218 -0
- package/dist/computed.js.map +1 -0
- package/dist/config.d.ts +16 -1
- package/dist/config.js +26 -2
- package/dist/config.js.map +1 -1
- package/dist/format.d.ts +3 -0
- package/dist/format.js +5 -2
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/locate.d.ts +11 -0
- package/dist/locate.js +172 -10
- package/dist/locate.js.map +1 -1
- package/dist/report.d.ts +18 -0
- package/dist/report.js +186 -46
- package/dist/report.js.map +1 -1
- package/dist/run.d.ts +3 -0
- package/dist/run.js +71 -11
- package/dist/run.js.map +1 -1
- package/package.json +2 -2
- package/renderdiff.config.example.ts +20 -7
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
${meta.rightReplayedFrom ? `· 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>${
|
|
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
|
-
${
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
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)} · ${esc(meta.browser.name)} ${esc(meta.browser.version)}${["left", "right"]
|
|
211
|
+
.filter((s) => meta.storageState[s])
|
|
212
|
+
.map((s) => ` · ${s} storage state <code>${esc(meta.storageState[s])}</code>`)
|
|
213
|
+
.join("")}
|
|
214
|
+
${meta.rightReplayedFrom ? `· 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)} · renderdiff ${esc(meta.version)} ·
|
|
309
|
+
widths ${meta.widths.join(", ")} ·
|
|
310
|
+
threshold ${meta.threshold === null ? "none" : pct(meta.threshold)} ·
|
|
311
|
+
${esc(meta.platform ?? "unknown platform")} · ${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
|
package/dist/report.js.map
CHANGED
|
@@ -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 {
|
|
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
|
|
406
|
-
const below = results.filter((r) => r
|
|
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
|
-
|
|
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
|
|
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` : "",
|