// scripts/e2e-timings.mjs — per-spec totals from a Playwright JSON report, and // the delta against the branch's last run. The reports here are synthetic, in // the shape Playwright's `json` reporter writes. import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { after, test } from "node:test"; import { fileURLToPath } from "node:url"; import { HISTORY_KEEP, baselines, historyFile, placeRun, readHistory, render, suiteFromPath, summarize, writeHistory, } from "./e2e-timings.mjs"; const SCRIPT = path.join(path.dirname(fileURLToPath(import.meta.url)), "e2e-timings.mjs"); const made = []; after(() => { for (const dir of made) fs.rmSync(dir, { recursive: true, force: true }); }); function tmp() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "e2e-timings-test-")); made.push(dir); return dir; } const result = (duration, status = "passed") => ({ duration, status, retry: 0 }); const t = (status, ...results) => ({ status, projectName: "chromium", results }); // Two files; one nests a describe; one test failed, one retried to flaky, one skipped. function report({ pacingMs = 60_000, startTime = "2026-10-09T20:40:00.000Z", wall = 90_000 } = {}) { return { config: {}, errors: [], stats: { startTime, duration: wall, expected: 3, unexpected: 1, flaky: 1, skipped: 1 }, suites: [ { title: "pacing.spec.ts", file: "pacing.spec.ts", specs: [ { title: "a live 429 backs off", file: "pacing.spec.ts", tests: [t("expected", result(pacingMs))] }, { title: "deferred", file: "pacing.spec.ts", tests: [t("unexpected", result(4_000, "failed"))] }, ], }, { title: "channel-work.spec.ts", file: "channel-work.spec.ts", specs: [{ title: "top", file: "channel-work.spec.ts", tests: [t("expected", result(1_500))] }], suites: [ { title: "inline", file: "channel-work.spec.ts", specs: [ { title: "retried", file: "channel-work.spec.ts", tests: [t("flaky", result(2_000, "failed"), result(1_000))], }, { title: "skipped", file: "channel-work.spec.ts", tests: [t("skipped", result(0, "skipped"))] }, ], }, ], }, ], }; } test("summarize totals every result per spec file, nested describes and retries included", () => { const s = summarize(report()); assert.equal(s.startTime, "2026-10-09T20:40:00.000Z"); assert.equal(s.wallMs, 90_000); assert.deepEqual(s.totals, { tests: 5, passed: 2, failed: 1, flaky: 1, skipped: 1 }); assert.deepEqual(s.specs["pacing.spec.ts"], { tests: 2, passed: 1, failed: 1, flaky: 0, skipped: 0, ms: 64_000 }); assert.deepEqual(s.specs["channel-work.spec.ts"], { tests: 3, passed: 1, failed: 0, flaky: 1, skipped: 1, ms: 4_500 }); }); test("placeRun: earlier is every run before this one; a run already recorded is not added twice", () => { const a = { startTime: "a", specs: {} }; const b = { startTime: "b", specs: {} }; const first = placeRun([], a); assert.deepEqual(first.earlier, []); assert.deepEqual(first.runs, [a]); const second = placeRun(first.runs, b); assert.deepEqual(second.earlier, [a]); assert.deepEqual(second.runs, [a, b]); const again = placeRun(second.runs, { ...b }); assert.equal(again.recorded, false); assert.deepEqual(again.earlier, [a]); assert.equal(again.runs.length, 2); let runs = []; for (let i = 0; i < HISTORY_KEEP + 5; i++) runs = placeRun(runs, { startTime: String(i) }).runs; assert.equal(runs.length, HISTORY_KEEP); assert.equal(runs[0].startTime, "5"); }); const row = (ms) => ({ tests: 1, passed: 1, failed: 0, flaky: 0, skipped: 0, ms }); test("baselines: the wall against the last run of the same specs; each spec against its last run", () => { const full1 = { startTime: "1", wallMs: 100, specs: { "a.spec.ts": row(10), "b.spec.ts": row(20) } }; const one = { startTime: "2", wallMs: 5, specs: { "a.spec.ts": row(4) } }; const now = { startTime: "3", wallMs: 90, specs: { "a.spec.ts": row(3), "b.spec.ts": row(25) } }; const b = baselines([full1, one], now); assert.equal(b.same, full1); assert.deepEqual(b.perSpec, { "a.spec.ts": row(4), "b.spec.ts": row(20) }); assert.equal(baselines([one], now).same, null); }); test("render: slowest first, a signed delta per spec, a spec never run before is new", () => { const before = { ...summarize(report()), head: "aaaaaaaa11" }; const now = summarize(report({ pacingMs: 10_000, startTime: "2026-10-10T01:00:00.000Z", wall: 40_000 })); const lines = render({ suite: "editor", branch: "r20/e2e-speed", head: "bbbbbbbb22", run: now, earlier: [before] }); assert.equal(lines[0], "e2e timings — editor on r20/e2e-speed (bbbbbbbb), 2026-10-10 01:00 UTC, wall 40.0s"); assert.equal(lines[1], " 5 tests: 2 passed, 1 failed, 1 flaky, 1 skipped"); assert.equal(lines[2], " against 2026-10-09 20:40 UTC (aaaaaaaa), the same specs: wall −50.0s"); const body = lines.slice(4); assert.match(body[1], /^ {2}pacing\.spec\.ts\s+2\s+14\.0s\s+−50\.0s {2}\(1 failed\)$/); assert.match(body[2], /^ {2}channel-work\.spec\.ts\s+3\s+4\.5s\s+±0\.0s$/); now.specs["new.spec.ts"] = row(500); const mixed = render({ suite: "editor", branch: "b", head: null, run: now, earlier: [before] }); assert.equal(mixed[2], " no earlier run of these same specs on this branch: each spec against the last run that had it"); assert.match(mixed.at(-1), /^ {2}new\.spec\.ts\s+1\s+0\.5s\s+new$/); }); test("render with no earlier run says this run is the baseline", () => { const lines = render({ suite: "umtool", branch: "main", head: null, run: summarize(report()), earlier: [] }); assert.equal(lines[2], " no earlier run of this suite on this branch: this one is the baseline"); assert.doesNotMatch(lines[4], /Δ/); }); test("the suite name comes from where the report lives", () => { assert.equal(suiteFromPath("/x/editor/test-results/timings.json"), "editor"); assert.equal(suiteFromPath("/x/export/test-results-hub/timings.json"), "export-hub"); assert.equal(suiteFromPath("/x/export/test-results-report/timings.json"), "export-report"); }); test("history round-trips, and a garbled file reads as no history", () => { const dir = tmp(); const file = historyFile(dir, "editor", "r20/e2e-speed"); assert.equal(path.basename(file), "r20_e2e-speed.json"); assert.deepEqual(readHistory(file), []); writeHistory(file, [{ startTime: "a" }]); assert.deepEqual(readHistory(file), [{ startTime: "a" }]); fs.writeFileSync(file, "{nope"); assert.deepEqual(readHistory(file), []); }); test("the CLI records the first run, then prints the second against it", () => { const dir = tmp(); const reportFile = path.join(dir, "editor", "test-results", "timings.json"); fs.mkdirSync(path.dirname(reportFile), { recursive: true }); const hist = path.join(dir, "hist"); const run = () => execFileSync(process.execPath, [SCRIPT, reportFile, "--history-dir", hist, "--branch", "b"], { encoding: "utf8", }); fs.writeFileSync(reportFile, JSON.stringify(report())); const one = run(); assert.match(one, /this one is the baseline/); assert.match(one, /recorded in /); assert.match(run(), /this run was already recorded/); fs.writeFileSync(reportFile, JSON.stringify(report({ pacingMs: 5_000, startTime: "2026-10-10T02:00:00.000Z" }))); const two = run(); assert.match(two, /against 2026-10-09 20:40 UTC, the same specs: wall ±0\.0s/); assert.match(two, /pacing\.spec\.ts\s+2\s+9\.0s\s+−55\.0s/); assert.equal(readHistory(historyFile(hist, "editor", "b")).length, 2); });