@fairfox/polly 0.81.0 → 0.82.1
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/cli/polly.js +21 -1
- package/dist/cli/polly.js.map +3 -3
- package/dist/gallery/gallery.css +1589 -0
- package/dist/gallery/gallery.js +5354 -0
- package/dist/gallery/index.html +13 -0
- package/dist/src/polly-ui/index.js +32 -1
- package/dist/src/polly-ui/index.js.map +3 -3
- package/dist/src/polly-ui/markdown.js +547 -233
- package/dist/src/polly-ui/markdown.js.map +4 -4
- package/dist/tools/gallery/src/cli.js +211 -0
- package/dist/tools/gallery/src/cli.js.map +11 -0
- package/dist/tools/mutate/src/cli.js +8 -1
- package/dist/tools/mutate/src/cli.js.map +3 -3
- package/dist/tools/test/src/coverage-policy/cli.js +5 -1
- package/dist/tools/test/src/coverage-policy/cli.js.map +3 -3
- package/dist/tools/test/src/tiers/args.d.ts +5 -0
- package/dist/tools/test/src/tiers/cli.js +77 -23
- package/dist/tools/test/src/tiers/cli.js.map +8 -7
- package/dist/tools/test/src/tiers/index.d.ts +2 -1
- package/dist/tools/test/src/tiers/order.d.ts +25 -0
- package/dist/tools/test/src/tiers/types.d.ts +15 -0
- package/dist/tools/verify/src/cli.js +20 -15
- package/dist/tools/verify/src/cli.js.map +3 -3
- package/dist/tools/visualize/src/cli.js +17 -13
- package/dist/tools/visualize/src/cli.js.map +3 -3
- package/package.json +5 -26
|
@@ -28,11 +28,45 @@ var BOOL_FLAGS = {
|
|
|
28
28
|
"--full": "full",
|
|
29
29
|
"--list": "list",
|
|
30
30
|
"--bail": "bail",
|
|
31
|
+
"--fail-fast": "failFast",
|
|
31
32
|
"--strict-needs": "strictNeeds"
|
|
32
33
|
};
|
|
34
|
+
function parseOrder(value) {
|
|
35
|
+
if (value === "default" || value === "fast" || value === "cost")
|
|
36
|
+
return value;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
33
39
|
function listValue(arg, name) {
|
|
34
40
|
return arg.startsWith(name) ? arg.slice(name.length).split(",") : [];
|
|
35
41
|
}
|
|
42
|
+
function applyValueFlag(args, arg) {
|
|
43
|
+
if (arg === "--json") {
|
|
44
|
+
args.json = DEFAULT_JSON;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (arg.startsWith("--json=")) {
|
|
48
|
+
args.json = arg.slice("--json=".length);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (arg.startsWith("--tier=")) {
|
|
52
|
+
args.tiers.push(...listValue(arg, "--tier="));
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (arg.startsWith("--only=")) {
|
|
56
|
+
args.only.push(...listValue(arg, "--only="));
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (arg.startsWith("--order=")) {
|
|
60
|
+
const order = parseOrder(arg.slice("--order=".length));
|
|
61
|
+
if (order === null) {
|
|
62
|
+
console.log("Unknown --order value (expected default, fast, or cost)");
|
|
63
|
+
process.exit(2);
|
|
64
|
+
}
|
|
65
|
+
args.order = order;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
36
70
|
function parseTierArgs(argv) {
|
|
37
71
|
const args = {
|
|
38
72
|
tiers: [],
|
|
@@ -41,6 +75,8 @@ function parseTierArgs(argv) {
|
|
|
41
75
|
full: false,
|
|
42
76
|
list: false,
|
|
43
77
|
bail: false,
|
|
78
|
+
failFast: false,
|
|
79
|
+
order: null,
|
|
44
80
|
strictNeeds: false,
|
|
45
81
|
json: null
|
|
46
82
|
};
|
|
@@ -50,20 +86,7 @@ function parseTierArgs(argv) {
|
|
|
50
86
|
args[boolKey] = true;
|
|
51
87
|
continue;
|
|
52
88
|
}
|
|
53
|
-
if (arg
|
|
54
|
-
args.json = DEFAULT_JSON;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (arg.startsWith("--json=")) {
|
|
58
|
-
args.json = arg.slice("--json=".length);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (arg.startsWith("--tier=")) {
|
|
62
|
-
args.tiers.push(...listValue(arg, "--tier="));
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (arg.startsWith("--only=")) {
|
|
66
|
-
args.only.push(...listValue(arg, "--only="));
|
|
89
|
+
if (applyValueFlag(args, arg)) {
|
|
67
90
|
continue;
|
|
68
91
|
}
|
|
69
92
|
if (arg.startsWith("-")) {
|
|
@@ -211,6 +234,22 @@ async function firstUnmetNeed(needs) {
|
|
|
211
234
|
return null;
|
|
212
235
|
}
|
|
213
236
|
|
|
237
|
+
// tools/test/src/tiers/order.ts
|
|
238
|
+
var COST_WEIGHT = {
|
|
239
|
+
light: 0,
|
|
240
|
+
medium: 1,
|
|
241
|
+
heavy: 2
|
|
242
|
+
};
|
|
243
|
+
function weightOf(spec) {
|
|
244
|
+
return COST_WEIGHT[spec.cost ?? "medium"];
|
|
245
|
+
}
|
|
246
|
+
function orderCases(cases, order) {
|
|
247
|
+
if (order === "default")
|
|
248
|
+
return cases;
|
|
249
|
+
const direction = order === "cost" ? -1 : 1;
|
|
250
|
+
return cases.map((spec, index) => ({ spec, index })).sort((a, b) => (weightOf(a.spec) - weightOf(b.spec)) * direction || a.index - b.index).map((entry) => entry.spec);
|
|
251
|
+
}
|
|
252
|
+
|
|
214
253
|
// tools/test/src/tiers/protocol.ts
|
|
215
254
|
var SENTINEL = "__TIER_RESULT__";
|
|
216
255
|
|
|
@@ -349,40 +388,52 @@ async function runOneCase(spec, tier, options, worker) {
|
|
|
349
388
|
}
|
|
350
389
|
async function runTier(tier, options, worker) {
|
|
351
390
|
const log = options.log ?? ((m) => console.log(m));
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
391
|
+
const matched = tier.cases.filter((c) => caseMatches(c, options.only));
|
|
392
|
+
if (matched.length === 0)
|
|
354
393
|
return [];
|
|
394
|
+
const selected = orderCases(matched, options.order ?? "default");
|
|
355
395
|
log(`
|
|
356
396
|
▶ ${tier.name}${tier.description ? ` — ${tier.description}` : ""} (${selected.length} case${selected.length === 1 ? "" : "s"})`);
|
|
357
397
|
const concurrency = Math.max(1, tier.concurrency ?? 1);
|
|
398
|
+
const failFast = options.failFast ?? false;
|
|
358
399
|
const reports = new Array(selected.length);
|
|
359
400
|
let next = 0;
|
|
401
|
+
let aborted = false;
|
|
360
402
|
async function worker_loop() {
|
|
361
|
-
while (
|
|
403
|
+
while (!aborted) {
|
|
362
404
|
const i = next++;
|
|
363
405
|
const spec = selected[i];
|
|
364
406
|
if (!spec)
|
|
365
407
|
return;
|
|
366
|
-
|
|
408
|
+
const report = await runOneCase(spec, tier, options, worker);
|
|
409
|
+
reports[i] = report;
|
|
410
|
+
if (failFast && (report.outcome === "fail" || report.outcome === "timeout")) {
|
|
411
|
+
aborted = true;
|
|
412
|
+
}
|
|
367
413
|
}
|
|
368
414
|
}
|
|
369
415
|
await Promise.all(Array.from({ length: Math.min(concurrency, selected.length) }, worker_loop));
|
|
370
|
-
|
|
416
|
+
const done = reports.filter((r) => r !== undefined);
|
|
417
|
+
if (aborted && done.length < selected.length) {
|
|
418
|
+
log(` ⏹ fail-fast: stopped after a failure — ${selected.length - done.length} case(s) not run`);
|
|
419
|
+
}
|
|
420
|
+
return done;
|
|
371
421
|
}
|
|
372
422
|
async function runPlan(plan, options = {}) {
|
|
373
423
|
const log = options.log ?? ((m) => console.log(m));
|
|
374
424
|
const worker = await workerPath();
|
|
375
425
|
const wanted = options.tiers && options.tiers.length > 0 ? new Set(options.tiers) : null;
|
|
376
426
|
const started = performance.now();
|
|
427
|
+
const stopOnFail = Boolean(options.bail || options.failFast);
|
|
377
428
|
const all = [];
|
|
378
429
|
for (const tier of plan.tiers) {
|
|
379
430
|
if (wanted && !wanted.has(tier.name))
|
|
380
431
|
continue;
|
|
381
432
|
const reports = await runTier(tier, options, worker);
|
|
382
433
|
all.push(...reports);
|
|
383
|
-
if (
|
|
434
|
+
if (stopOnFail && reports.some((r) => r.outcome === "fail" || r.outcome === "timeout")) {
|
|
384
435
|
log(`
|
|
385
|
-
⏹
|
|
436
|
+
⏹ ${options.failFast ? "fail-fast" : "bail"}: stopping after failing tier "${tier.name}"`);
|
|
386
437
|
break;
|
|
387
438
|
}
|
|
388
439
|
}
|
|
@@ -427,7 +478,8 @@ function formatPlan(plan) {
|
|
|
427
478
|
lines.push(`${tier.name}${conc}${tier.description ? ` — ${tier.description}` : ""}`);
|
|
428
479
|
for (const c of tier.cases) {
|
|
429
480
|
const needs = c.needs && c.needs.length > 0 ? ` (needs ${c.needs.join(", ")})` : "";
|
|
430
|
-
|
|
481
|
+
const cost = c.cost ? ` [${c.cost}]` : "";
|
|
482
|
+
lines.push(` ${c.id}${cost}${needs}`);
|
|
431
483
|
}
|
|
432
484
|
lines.push("");
|
|
433
485
|
}
|
|
@@ -471,7 +523,9 @@ async function main() {
|
|
|
471
523
|
const report = await runPlan(plan, {
|
|
472
524
|
tiers,
|
|
473
525
|
only: args.only,
|
|
526
|
+
order: args.order ?? (args.failFast ? "fast" : "default"),
|
|
474
527
|
bail: args.bail,
|
|
528
|
+
failFast: args.failFast,
|
|
475
529
|
strictNeeds: args.strictNeeds,
|
|
476
530
|
cwd: root,
|
|
477
531
|
log: (m) => console.log(m)
|
|
@@ -487,4 +541,4 @@ wrote ${args.json}`);
|
|
|
487
541
|
}
|
|
488
542
|
await main();
|
|
489
543
|
|
|
490
|
-
//# debugId=
|
|
544
|
+
//# debugId=CD4A2E88C15B8A6864756E2164756E21
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../tools/test/src/tiers/cli.ts", "../tools/test/src/tiers/args.ts", "../tools/test/src/tiers/discover.ts", "../tools/test/src/tiers/detect.ts", "../tools/test/src/tiers/protocol.ts", "../tools/test/src/tiers/engine.ts", "../tools/test/src/tiers/reporter.ts"],
|
|
3
|
+
"sources": ["../tools/test/src/tiers/cli.ts", "../tools/test/src/tiers/args.ts", "../tools/test/src/tiers/discover.ts", "../tools/test/src/tiers/detect.ts", "../tools/test/src/tiers/order.ts", "../tools/test/src/tiers/protocol.ts", "../tools/test/src/tiers/engine.ts", "../tools/test/src/tiers/reporter.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"#!/usr/bin/env bun\n/**\n * Consumer-facing tiered test runner — what `polly test --tier …` runs from a\n * consumer's own Polly project. It auto-discovers the project's tiers (see\n * discover.ts) and runs them through the shared engine, exactly as Polly's own\n * internal suite does.\n *\n * Usage (from a Polly project root):\n * polly test --tier # discovered unit + integration (fast loop)\n * polly test --tier --all # every discovered tier\n * polly test --tier=browser # one tier\n * polly test --tier --list # show what was discovered, run nothing\n * polly test --tier --json # write test-results/tiers.json\n *\n * (Bare `polly test` still delegates to `bun test`; the --tier-family flags\n * switch into this runner.)\n */\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { parseTierArgs } from \"./args\";\nimport { discoverPlan } from \"./discover\";\nimport { runPlan } from \"./engine\";\nimport { formatPlan, formatSummary, toJSON } from \"./reporter\";\n\n/** Tiers run when no tier is named (fast inner loop), if they were discovered. */\nconst DEFAULT_TIERS = [\"unit\", \"integration\"];\n\nasync function main(): Promise<void> {\n const args = parseTierArgs(process.argv.slice(2));\n const root = process.cwd();\n const plan = await discoverPlan(root);\n\n if (plan.tiers.length === 0) {\n console.log(\n \"polly test: no tiers discovered.\\n\" +\n \" Looked for *.test.{ts,tsx}, *.browser.{ts,tsx}, and scripts/e2e-*.{ts,tsx}.\"\n );\n process.exit(0);\n }\n\n if (args.list) {\n console.log(formatPlan(plan));\n return;\n }\n\n const discovered = plan.tiers.map((t) => t.name);\n const known = new Set(discovered);\n let tiers: string[];\n if (args.tiers.length > 0) {\n const unknown = args.tiers.filter((t) => !known.has(t));\n if (unknown.length > 0) {\n console.log(`Unknown tier(s): ${unknown.join(\", \")}`);\n console.log(`Discovered tiers: ${discovered.join(\", \")}`);\n process.exit(2);\n }\n tiers = args.tiers;\n } else if (args.all || args.full) {\n tiers = discovered;\n } else {\n const fast = discovered.filter((t) => DEFAULT_TIERS.includes(t));\n tiers = fast.length > 0 ? fast : discovered;\n }\n\n console.log(\n `polly test — tiers: ${tiers.join(\" → \")}${args.only.length ? ` only: ${args.only.join(\", \")}` : \"\"}`\n );\n\n const report = await runPlan(plan, {\n tiers,\n only: args.only,\n bail: args.bail,\n strictNeeds: args.strictNeeds,\n cwd: root,\n log: (m) => console.log(m),\n });\n\n console.log(formatSummary(report));\n\n if (args.json) {\n mkdirSync(dirname(args.json), { recursive: true });\n await Bun.write(args.json, toJSON(report));\n console.log(`\\nwrote ${args.json}`);\n }\n\n process.exit(report.ok ? 0 : 1);\n}\n\nawait main();\n",
|
|
6
|
-
"/**\n * Shared flag parsing for the tiered runner CLIs.\n *\n * Both front-ends (Polly's internal scripts/test/cli.ts and the consumer-facing\n * tools/test/src/tiers/cli.ts) accept the same flags; only their tier *sets*\n * differ. This keeps the surface identical.\n */\n\nexport interface TierArgs {\n /** Explicit tier names (positional or via --tier=a,b). */\n tiers: string[];\n /** --only=substr filters across tiers. */\n only: string[];\n all: boolean;\n full: boolean;\n list: boolean;\n bail: boolean;\n strictNeeds: boolean;\n /** Path to write JSON results, or null. */\n json: string | null;\n}\n\nexport const DEFAULT_JSON = \"test-results/tiers.json\";\n\nconst BOOL_FLAGS: Record<string, \"all\" | \"full\" | \"list\" | \"bail\" | \"strictNeeds\"> = {\n \"--all\": \"all\",\n \"--full\": \"full\",\n \"--list\": \"list\",\n \"--bail\": \"bail\",\n \"--strict-needs\": \"strictNeeds\",\n};\n\nfunction listValue(arg: string, name: string): string[] {\n return arg.startsWith(name) ? arg.slice(name.length).split(\",\") : [];\n}\n\n/**
|
|
5
|
+
"#!/usr/bin/env bun\n/**\n * Consumer-facing tiered test runner — what `polly test --tier …` runs from a\n * consumer's own Polly project. It auto-discovers the project's tiers (see\n * discover.ts) and runs them through the shared engine, exactly as Polly's own\n * internal suite does.\n *\n * Usage (from a Polly project root):\n * polly test --tier # discovered unit + integration (fast loop)\n * polly test --tier --all # every discovered tier\n * polly test --tier=browser # one tier\n * polly test --tier --all --order=cost # heaviest cases first (min wall-clock)\n * polly test --tier --all --fail-fast # soft early-stop on first failure\n * polly test --tier --list # show what was discovered, run nothing\n * polly test --tier --json # write test-results/tiers.json\n *\n * (Bare `polly test` still delegates to `bun test`; the --tier-family flags\n * switch into this runner.)\n */\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { parseTierArgs } from \"./args\";\nimport { discoverPlan } from \"./discover\";\nimport { runPlan } from \"./engine\";\nimport { formatPlan, formatSummary, toJSON } from \"./reporter\";\n\n/** Tiers run when no tier is named (fast inner loop), if they were discovered. */\nconst DEFAULT_TIERS = [\"unit\", \"integration\"];\n\nasync function main(): Promise<void> {\n const args = parseTierArgs(process.argv.slice(2));\n const root = process.cwd();\n const plan = await discoverPlan(root);\n\n if (plan.tiers.length === 0) {\n console.log(\n \"polly test: no tiers discovered.\\n\" +\n \" Looked for *.test.{ts,tsx}, *.browser.{ts,tsx}, and scripts/e2e-*.{ts,tsx}.\"\n );\n process.exit(0);\n }\n\n if (args.list) {\n console.log(formatPlan(plan));\n return;\n }\n\n const discovered = plan.tiers.map((t) => t.name);\n const known = new Set(discovered);\n let tiers: string[];\n if (args.tiers.length > 0) {\n const unknown = args.tiers.filter((t) => !known.has(t));\n if (unknown.length > 0) {\n console.log(`Unknown tier(s): ${unknown.join(\", \")}`);\n console.log(`Discovered tiers: ${discovered.join(\", \")}`);\n process.exit(2);\n }\n tiers = args.tiers;\n } else if (args.all || args.full) {\n tiers = discovered;\n } else {\n const fast = discovered.filter((t) => DEFAULT_TIERS.includes(t));\n tiers = fast.length > 0 ? fast : discovered;\n }\n\n console.log(\n `polly test — tiers: ${tiers.join(\" → \")}${args.only.length ? ` only: ${args.only.join(\", \")}` : \"\"}`\n );\n\n const report = await runPlan(plan, {\n tiers,\n only: args.only,\n // --fail-fast wants cheap, high-signal cases first so the abort trips\n // soonest; an explicit --order always wins.\n order: args.order ?? (args.failFast ? \"fast\" : \"default\"),\n bail: args.bail,\n failFast: args.failFast,\n strictNeeds: args.strictNeeds,\n cwd: root,\n log: (m) => console.log(m),\n });\n\n console.log(formatSummary(report));\n\n if (args.json) {\n mkdirSync(dirname(args.json), { recursive: true });\n await Bun.write(args.json, toJSON(report));\n console.log(`\\nwrote ${args.json}`);\n }\n\n process.exit(report.ok ? 0 : 1);\n}\n\nawait main();\n",
|
|
6
|
+
"/**\n * Shared flag parsing for the tiered runner CLIs.\n *\n * Both front-ends (Polly's internal scripts/test/cli.ts and the consumer-facing\n * tools/test/src/tiers/cli.ts) accept the same flags; only their tier *sets*\n * differ. This keeps the surface identical.\n */\n\nimport type { CaseOrder } from \"./types\";\n\nexport interface TierArgs {\n /** Explicit tier names (positional or via --tier=a,b). */\n tiers: string[];\n /** --only=substr filters across tiers. */\n only: string[];\n all: boolean;\n full: boolean;\n list: boolean;\n bail: boolean;\n /** --fail-fast: soft early-stop on the first failing case. */\n failFast: boolean;\n /** --order=fast|cost|default within-tier ordering, or null to leave unset. */\n order: CaseOrder | null;\n strictNeeds: boolean;\n /** Path to write JSON results, or null. */\n json: string | null;\n}\n\nexport const DEFAULT_JSON = \"test-results/tiers.json\";\n\nconst BOOL_FLAGS: Record<string, \"all\" | \"full\" | \"list\" | \"bail\" | \"failFast\" | \"strictNeeds\"> = {\n \"--all\": \"all\",\n \"--full\": \"full\",\n \"--list\": \"list\",\n \"--bail\": \"bail\",\n \"--fail-fast\": \"failFast\",\n \"--strict-needs\": \"strictNeeds\",\n};\n\n/** Narrow a raw `--order=` value to a {@link CaseOrder}, or null if unknown. */\nfunction parseOrder(value: string): CaseOrder | null {\n if (value === \"default\" || value === \"fast\" || value === \"cost\") return value;\n return null;\n}\n\nfunction listValue(arg: string, name: string): string[] {\n return arg.startsWith(name) ? arg.slice(name.length).split(\",\") : [];\n}\n\n/** Apply a value-bearing flag (`--name=value`, or bare `--json`). Returns false\n * if `arg` isn't one, so the caller can fall through to tier names. Exits(2) on\n * a malformed `--order=`. */\nfunction applyValueFlag(args: TierArgs, arg: string): boolean {\n if (arg === \"--json\") {\n args.json = DEFAULT_JSON;\n return true;\n }\n if (arg.startsWith(\"--json=\")) {\n args.json = arg.slice(\"--json=\".length);\n return true;\n }\n if (arg.startsWith(\"--tier=\")) {\n args.tiers.push(...listValue(arg, \"--tier=\"));\n return true;\n }\n if (arg.startsWith(\"--only=\")) {\n args.only.push(...listValue(arg, \"--only=\"));\n return true;\n }\n if (arg.startsWith(\"--order=\")) {\n const order = parseOrder(arg.slice(\"--order=\".length));\n if (order === null) {\n console.log(\"Unknown --order value (expected default, fast, or cost)\");\n process.exit(2);\n }\n args.order = order;\n return true;\n }\n return false;\n}\n\n/** Parse argv into {@link TierArgs}. Exits(2) on an unknown `--flag`. */\nexport function parseTierArgs(argv: string[]): TierArgs {\n const args: TierArgs = {\n tiers: [],\n only: [],\n all: false,\n full: false,\n list: false,\n bail: false,\n failFast: false,\n order: null,\n strictNeeds: false,\n json: null,\n };\n for (const arg of argv) {\n const boolKey = BOOL_FLAGS[arg];\n if (boolKey) {\n args[boolKey] = true;\n continue;\n }\n if (applyValueFlag(args, arg)) {\n continue;\n }\n if (arg.startsWith(\"-\")) {\n console.log(`Unknown flag: ${arg}`);\n process.exit(2);\n }\n args.tiers.push(arg);\n }\n return args;\n}\n",
|
|
7
7
|
"/**\n * Zero-config tier discovery for a consumer's Polly project.\n *\n * `polly test --tier` from a consumer's project builds its {@link TierPlan} by\n * convention — no config required:\n * - unit `bun test` (or `bun test tests/unit` if that dir exists)\n * - integration `bun test tests/integration` (when the dir exists)\n * - browser the Polly browser runner over `*.browser.{ts,tsx}`\n * - e2e `scripts/e2e-*.{ts,tsx}` exporting `run(ctx)` — the same\n * contract Polly dogfoods, so consumer e2e scripts written with\n * `@fairfox/polly/test` helpers slot straight in.\n *\n * A tier only appears when its inputs exist, so a project with just unit tests\n * gets just a unit tier.\n */\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { CaseSpec, Tier, TierPlan } from \"./types\";\n\n/** Files matching `pattern` under `root`, excluding node_modules, sorted. */\nasync function globFiles(root: string, pattern: string): Promise<string[]> {\n const glob = new Bun.Glob(pattern);\n const out: string[] = [];\n for await (const file of glob.scan({ cwd: root, onlyFiles: true })) {\n if (file.includes(\"node_modules\") || file.startsWith(\"dist/\")) continue;\n out.push(file);\n }\n return out.sort();\n}\n\n/** Resolve the browser runner, preferring the bundled build. */\nfunction browserRunner(): string {\n const bundled = `${import.meta.dir}/../browser/run.js`;\n const source = `${import.meta.dir}/../browser/run.ts`;\n return existsSync(bundled) ? bundled : source;\n}\n\n/** A safe directory to hand the browser runner (never the project root, which\n * would make it scan node_modules). */\nfunction browserDir(root: string, files: string[]): string {\n if (existsSync(join(root, \"tests/browser\"))) return \"tests/browser\";\n if (existsSync(join(root, \"tests\"))) return \"tests\";\n return dirname(files[0] ?? \".\") || \".\";\n}\n\n/** `scripts/e2e-foo-bar.ts` → `foo-bar`. */\nfunction e2eId(file: string): string {\n const base = file.replace(/^.*\\//, \"\").replace(/\\.(ts|tsx)$/, \"\");\n return base.replace(/^e2e-/, \"\") || base;\n}\n\nexport async function discoverPlan(root: string): Promise<TierPlan> {\n const tiers: Tier[] = [];\n\n const hasUnitDir = existsSync(join(root, \"tests/unit\"));\n const hasIntegrationDir = existsSync(join(root, \"tests/integration\"));\n const testFiles = await globFiles(root, \"**/*.test.{ts,tsx}\");\n\n if (hasUnitDir) {\n tiers.push({\n name: \"unit\",\n description: \"bun test tests/unit\",\n cases: [\n { id: \"unit\", exec: { kind: \"command\", argv: [\"bun\", \"test\", \"tests/unit\"], cwd: root } },\n ],\n });\n } else if (testFiles.length > 0) {\n tiers.push({\n name: \"unit\",\n description: `bun test (${testFiles.length} files)`,\n cases: [{ id: \"unit\", exec: { kind: \"command\", argv: [\"bun\", \"test\"], cwd: root } }],\n });\n }\n\n if (hasIntegrationDir) {\n tiers.push({\n name: \"integration\",\n description: \"bun test tests/integration\",\n cases: [\n {\n id: \"integration\",\n exec: { kind: \"command\", argv: [\"bun\", \"test\", \"tests/integration\"], cwd: root },\n },\n ],\n });\n }\n\n const browserFiles = await globFiles(root, \"**/*.browser.{ts,tsx}\");\n if (browserFiles.length > 0) {\n tiers.push({\n name: \"browser\",\n description: `Puppeteer over ${browserFiles.length} *.browser file(s)`,\n timeoutMs: 180_000,\n cases: [\n {\n id: \"browser\",\n needs: [\"browser\"],\n exec: {\n kind: \"command\",\n argv: [\"bun\", browserRunner(), browserDir(root, browserFiles)],\n cwd: root,\n },\n },\n ],\n });\n }\n\n const e2eFiles = await globFiles(root, \"scripts/e2e-*.{ts,tsx}\");\n if (e2eFiles.length > 0) {\n const cases: CaseSpec[] = e2eFiles.map((file) => ({\n id: e2eId(file),\n tags: [\"e2e\"],\n exec: { kind: \"module\", modulePath: join(root, file) },\n }));\n tiers.push({\n name: \"e2e\",\n description: `run()-export scripts (${e2eFiles.length})`,\n concurrency: 2,\n timeoutMs: 240_000,\n cases,\n });\n }\n\n return { tiers };\n}\n",
|
|
8
8
|
"/**\n * Host capability detection for need-gating.\n *\n * A case declaring `needs: [\"docker\"]` is skipped (not failed) when the host\n * can't satisfy it, unless `--strict-needs` is set. Detection is cached for the\n * lifetime of the process so a 30-case run probes `docker info` once.\n */\nimport type { Need } from \"./types\";\n\nconst cache = new Map<Need, boolean>();\n\nasync function commandSucceeds(argv: string[]): Promise<boolean> {\n try {\n const proc = Bun.spawn(argv, { stdout: \"ignore\", stderr: \"ignore\", stdin: \"ignore\" });\n return (await proc.exited) === 0;\n } catch {\n return false;\n }\n}\n\nasync function probe(need: Need): Promise<boolean> {\n switch (need) {\n case \"docker\":\n // `docker info` fails fast when the daemon is down, unlike `docker --version`.\n return commandSucceeds([\"docker\", \"info\"]);\n case \"browser\":\n // Puppeteer ships Chromium as a devDependency; assume present in-repo.\n // A real probe would resolve the executable path, but that pulls puppeteer\n // into the engine. Front-ends that need a stricter check can override.\n return true;\n case \"network\":\n return true;\n default:\n return false;\n }\n}\n\n/** Resolve whether a need is satisfied, memoised. */\nexport async function hasNeed(need: Need): Promise<boolean> {\n const cached = cache.get(need);\n if (cached !== undefined) return cached;\n const result = await probe(need);\n cache.set(need, result);\n return result;\n}\n\n/** First unmet need among `needs`, or null if all are satisfied. */\nexport async function firstUnmetNeed(needs: Need[] | undefined): Promise<Need | null> {\n for (const need of needs ?? []) {\n if (!(await hasNeed(need))) return need;\n }\n return null;\n}\n",
|
|
9
|
+
"/**\n * Within-tier case ordering.\n *\n * The engine drains a tier's cases through a bounded pool in array order, so\n * where a case sits decides when it launches. Reordering by a coarse cost hint\n * buys two different things depending on intent:\n *\n * - \"cost\" (heaviest first): the long poles launch in the first `concurrency`\n * slots and everything cheaper packs in behind them, so wall-clock collapses\n * toward the single slowest case instead of the sum. Use for a full run.\n * - \"fast\" (lightest first): cheap, high-signal cases run first, so under\n * soft-fail-fast they trip the abort soonest — you can't un-spawn a 4-minute\n * case, so you want it to *not* have started when a 2-second case fails.\n * - \"default\": registry definition order, unchanged.\n *\n * Pure and dependency-free on purpose: this is the one piece of the engine that\n * is worth unit-testing in isolation, and keeping it out of engine.ts keeps the\n * subprocess-heavy engine off the unit-coverage table.\n */\nimport type { CaseOrder, CaseSpec } from \"./types\";\n\nconst COST_WEIGHT: Record<NonNullable<CaseSpec[\"cost\"]>, number> = {\n light: 0,\n medium: 1,\n heavy: 2,\n};\n\n/** Weight of a case; unset cost is treated as \"medium\". */\nfunction weightOf(spec: CaseSpec): number {\n return COST_WEIGHT[spec.cost ?? \"medium\"];\n}\n\n/**\n * Return a copy of `cases` reordered per `order`. Stable: cases of equal weight\n * keep their original relative order, and \"default\" returns the input as-is.\n */\nexport function orderCases(cases: CaseSpec[], order: CaseOrder): CaseSpec[] {\n if (order === \"default\") return cases;\n const direction = order === \"cost\" ? -1 : 1; // cost → heaviest first; fast → lightest first\n return cases\n .map((spec, index) => ({ spec, index }))\n .sort((a, b) => (weightOf(a.spec) - weightOf(b.spec)) * direction || a.index - b.index)\n .map((entry) => entry.spec);\n}\n",
|
|
9
10
|
"/**\n * The one shared constant between the engine and its subprocess worker.\n *\n * It lives in its own leaf module so the engine never has to *import* the\n * worker (only spawn it by path). If the engine imported the worker, a\n * `splitting: false` bundle would inline the worker's `import.meta.main`\n * self-exec block into the CLI bundle and run it on startup. Keeping the\n * sentinel here avoids that.\n */\nexport const SENTINEL = \"__TIER_RESULT__\";\n",
|
|
10
|
-
"/**\n * The tiered test engine.\n *\n * Runs a {@link TierPlan} tier-by-tier in order. Within a tier, cases run with\n * bounded concurrency. Each case is its own subprocess (see worker.ts) so the\n * isolation the e2e scripts assume is preserved. Cases whose host needs are\n * unmet are skipped (logged), not failed, unless `strictNeeds` is set.\n */\nimport { firstUnmetNeed } from \"./detect\";\nimport { SENTINEL } from \"./protocol\";\nimport type {\n CaseOutcome,\n CaseReport,\n CaseSpec,\n EngineOptions,\n RunReport,\n Tier,\n TierPlan,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nasync function workerPath(): Promise<string> {\n const bundled = `${import.meta.dir}/worker.js`;\n const source = `${import.meta.dir}/worker.ts`;\n return (await Bun.file(bundled).exists()) ? bundled : source;\n}\n\nfunction caseLabel(spec: CaseSpec): string {\n return spec.label ?? spec.id;\n}\n\nfunction caseMatches(spec: CaseSpec, only: string[] | undefined): boolean {\n if (!only || only.length === 0) return true;\n const haystacks = [spec.id, caseLabel(spec), ...(spec.tags ?? [])].map((s) => s.toLowerCase());\n return only.some((needle) => {\n const n = needle.toLowerCase();\n return haystacks.some((h) => h.includes(n));\n });\n}\n\ninterface SubprocOutcome {\n exitCode: number;\n stdout: string;\n timedOut: boolean;\n}\n\nasync function runSubprocess(\n argv: string[],\n opts: { cwd?: string; env: Record<string, string | undefined>; timeoutMs: number },\n onLine: (line: string) => void\n): Promise<SubprocOutcome> {\n const proc = Bun.spawn(argv, {\n cwd: opts.cwd,\n env: opts.env,\n stdout: \"pipe\",\n stderr: \"pipe\",\n stdin: \"ignore\",\n });\n\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n proc.kill();\n }, opts.timeoutMs);\n\n const chunks: string[] = [];\n const pump = async (stream: ReadableStream<Uint8Array>): Promise<void> => {\n const decoder = new TextDecoder();\n let buffered = \"\";\n for await (const chunk of stream) {\n buffered += decoder.decode(chunk, { stream: true });\n let nl = buffered.indexOf(\"\\n\");\n while (nl !== -1) {\n const line = buffered.slice(0, nl);\n chunks.push(line);\n onLine(line);\n buffered = buffered.slice(nl + 1);\n nl = buffered.indexOf(\"\\n\");\n }\n }\n if (buffered) {\n chunks.push(buffered);\n onLine(buffered);\n }\n };\n\n await Promise.all([pump(proc.stdout), pump(proc.stderr)]);\n const exitCode = await proc.exited;\n clearTimeout(timer);\n\n return { exitCode, stdout: chunks.join(\"\\n\"), timedOut };\n}\n\nfunction parseSentinel(stdout: string): { pass: boolean; message?: string } | null {\n const idx = stdout.lastIndexOf(SENTINEL);\n if (idx === -1) return null;\n const after = stdout.slice(idx + SENTINEL.length);\n const newline = after.indexOf(\"\\n\");\n const json = newline === -1 ? after : after.slice(0, newline);\n try {\n const parsed = JSON.parse(json.trim());\n return { pass: Boolean(parsed.pass), message: parsed.message };\n } catch {\n return null;\n }\n}\n\ninterface Verdict {\n outcome: CaseOutcome;\n message?: string;\n}\n\nconst VERDICT_ICON: Record<CaseOutcome, string> = {\n pass: \"✓\",\n fail: \"✗\",\n skip: \"⊘\",\n timeout: \"⏱\",\n};\n\n/** Resolve argv + cwd for a case, falling back to the run-wide cwd. */\nfunction buildInvocation(\n spec: CaseSpec,\n worker: string,\n options: EngineOptions\n): { argv: string[]; cwd?: string } {\n if (spec.exec.kind === \"module\") {\n return { argv: [\"bun\", worker, spec.exec.modulePath, spec.id], cwd: options.cwd };\n }\n return { argv: spec.exec.argv, cwd: spec.exec.cwd ?? options.cwd };\n}\n\n/** Decide pass/fail/timeout, preferring the sentinel over the exit code. */\nfunction decideVerdict(spec: CaseSpec, result: SubprocOutcome, timeoutMs: number): Verdict {\n if (result.timedOut) return { outcome: \"timeout\", message: `timed out after ${timeoutMs}ms` };\n const sentinel = spec.exec.kind === \"module\" ? parseSentinel(result.stdout) : null;\n const pass = sentinel ? sentinel.pass : result.exitCode === 0;\n if (pass) return { outcome: \"pass\" };\n return { outcome: \"fail\", message: sentinel?.message ?? `exit code ${result.exitCode}` };\n}\n\nasync function runOneCase(\n spec: CaseSpec,\n tier: Tier,\n options: EngineOptions,\n worker: string\n): Promise<CaseReport> {\n const label = caseLabel(spec);\n const log = options.log ?? ((m: string) => console.log(m));\n\n const unmet = await firstUnmetNeed(spec.needs);\n if (unmet && !options.strictNeeds) {\n log(` ⊘ ${label} — skipped (needs ${unmet})`);\n return {\n tier: tier.name,\n id: spec.id,\n label,\n outcome: \"skip\",\n durationMs: 0,\n skipReason: `needs ${unmet}`,\n };\n }\n\n const timeoutMs = spec.timeoutMs ?? tier.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const env: Record<string, string | undefined> = { ...process.env, ...options.env };\n const { argv, cwd } = buildInvocation(spec, worker, options);\n\n const started = performance.now();\n const prefix = ` [${spec.id}] `;\n const result = await runSubprocess(argv, { cwd, env, timeoutMs }, (line) => {\n if (!line.includes(SENTINEL) && line.trim()) log(prefix + line);\n });\n const durationMs = Math.round(performance.now() - started);\n\n const verdict = decideVerdict(spec, result, timeoutMs);\n const tail = verdict.message ? ` — ${verdict.message}` : \"\";\n log(` ${VERDICT_ICON[verdict.outcome]} ${label} (${durationMs}ms)${tail}`);\n return {\n tier: tier.name,\n id: spec.id,\n label,\n outcome: verdict.outcome,\n durationMs,\n message: verdict.message,\n };\n}\n\nasync function runTier(tier: Tier, options: EngineOptions, worker: string): Promise<CaseReport[]> {\n const log = options.log ?? ((m: string) => console.log(m));\n const
|
|
11
|
-
"/**\n * Reporting for engine runs: a human summary table (with per-case timing and a\n * slowest-N list, the data the old `&&` chains never captured) and a JSON\n * artefact for tooling. No cloud reporters — everything lands on disk locally.\n */\nimport type { RunReport, TierPlan } from \"./types\";\n\nconst ICON = { pass: \"✓\", fail: \"✗\", skip: \"⊘\", timeout: \"⏱\" } as const;\n\n/** One-line-per-case summary plus totals and slowest cases. */\nexport function formatSummary(report: RunReport): string {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(\"── results ─────────────────────────────────\");\n for (const c of report.cases) {\n const time = c.outcome === \"skip\" ? \"\" : `${c.durationMs}ms`;\n const tail = c.outcome === \"skip\" ? ` (${c.skipReason})` : c.message ? ` — ${c.message}` : \"\";\n lines.push(`${ICON[c.outcome]} ${c.tier} › ${c.label} ${time}${tail}`);\n }\n\n const ran = report.cases.filter((c) => c.outcome !== \"skip\");\n const slowest = [...ran].sort((a, b) => b.durationMs - a.durationMs).slice(0, 5);\n if (slowest.length > 0) {\n lines.push(\"\");\n lines.push(\"slowest:\");\n for (const c of slowest) lines.push(` ${c.durationMs}ms ${c.tier} › ${c.label}`);\n }\n\n lines.push(\"\");\n lines.push(\n `${report.ok ? \"PASS\" : \"FAIL\"} ${report.passed} passed, ${report.failed} failed, ` +\n `${report.skipped} skipped in ${(report.durationMs / 1000).toFixed(1)}s`\n );\n return lines.join(\"\\n\");\n}\n\n/** Stable JSON artefact written to test-results/. */\nexport function toJSON(report: RunReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/** `--list` output: tiers and their cases without running anything. */\nexport function formatPlan(plan: TierPlan): string {\n const lines: string[] = [\"Tiers (run order):\", \"\"];\n for (const tier of plan.tiers) {\n const conc = tier.concurrency && tier.concurrency > 1 ? ` ×${tier.concurrency}` : \"\";\n lines.push(`${tier.name}${conc}${tier.description ? ` — ${tier.description}` : \"\"}`);\n for (const c of tier.cases) {\n const needs = c.needs && c.needs.length > 0 ? ` (needs ${c.needs.join(\", \")})` : \"\";\n lines.push(` ${c.id}${needs}`);\n }\n lines.push(\"\");\n }\n return lines.join(\"\\n\");\n}\n"
|
|
11
|
+
"/**\n * The tiered test engine.\n *\n * Runs a {@link TierPlan} tier-by-tier in order. Within a tier, cases run with\n * bounded concurrency. Each case is its own subprocess (see worker.ts) so the\n * isolation the e2e scripts assume is preserved. Cases whose host needs are\n * unmet are skipped (logged), not failed, unless `strictNeeds` is set.\n */\nimport { firstUnmetNeed } from \"./detect\";\nimport { orderCases } from \"./order\";\nimport { SENTINEL } from \"./protocol\";\nimport type {\n CaseOutcome,\n CaseReport,\n CaseSpec,\n EngineOptions,\n RunReport,\n Tier,\n TierPlan,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nasync function workerPath(): Promise<string> {\n const bundled = `${import.meta.dir}/worker.js`;\n const source = `${import.meta.dir}/worker.ts`;\n return (await Bun.file(bundled).exists()) ? bundled : source;\n}\n\nfunction caseLabel(spec: CaseSpec): string {\n return spec.label ?? spec.id;\n}\n\nfunction caseMatches(spec: CaseSpec, only: string[] | undefined): boolean {\n if (!only || only.length === 0) return true;\n const haystacks = [spec.id, caseLabel(spec), ...(spec.tags ?? [])].map((s) => s.toLowerCase());\n return only.some((needle) => {\n const n = needle.toLowerCase();\n return haystacks.some((h) => h.includes(n));\n });\n}\n\ninterface SubprocOutcome {\n exitCode: number;\n stdout: string;\n timedOut: boolean;\n}\n\nasync function runSubprocess(\n argv: string[],\n opts: { cwd?: string; env: Record<string, string | undefined>; timeoutMs: number },\n onLine: (line: string) => void\n): Promise<SubprocOutcome> {\n const proc = Bun.spawn(argv, {\n cwd: opts.cwd,\n env: opts.env,\n stdout: \"pipe\",\n stderr: \"pipe\",\n stdin: \"ignore\",\n });\n\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n proc.kill();\n }, opts.timeoutMs);\n\n const chunks: string[] = [];\n const pump = async (stream: ReadableStream<Uint8Array>): Promise<void> => {\n const decoder = new TextDecoder();\n let buffered = \"\";\n for await (const chunk of stream) {\n buffered += decoder.decode(chunk, { stream: true });\n let nl = buffered.indexOf(\"\\n\");\n while (nl !== -1) {\n const line = buffered.slice(0, nl);\n chunks.push(line);\n onLine(line);\n buffered = buffered.slice(nl + 1);\n nl = buffered.indexOf(\"\\n\");\n }\n }\n if (buffered) {\n chunks.push(buffered);\n onLine(buffered);\n }\n };\n\n await Promise.all([pump(proc.stdout), pump(proc.stderr)]);\n const exitCode = await proc.exited;\n clearTimeout(timer);\n\n return { exitCode, stdout: chunks.join(\"\\n\"), timedOut };\n}\n\nfunction parseSentinel(stdout: string): { pass: boolean; message?: string } | null {\n const idx = stdout.lastIndexOf(SENTINEL);\n if (idx === -1) return null;\n const after = stdout.slice(idx + SENTINEL.length);\n const newline = after.indexOf(\"\\n\");\n const json = newline === -1 ? after : after.slice(0, newline);\n try {\n const parsed = JSON.parse(json.trim());\n return { pass: Boolean(parsed.pass), message: parsed.message };\n } catch {\n return null;\n }\n}\n\ninterface Verdict {\n outcome: CaseOutcome;\n message?: string;\n}\n\nconst VERDICT_ICON: Record<CaseOutcome, string> = {\n pass: \"✓\",\n fail: \"✗\",\n skip: \"⊘\",\n timeout: \"⏱\",\n};\n\n/** Resolve argv + cwd for a case, falling back to the run-wide cwd. */\nfunction buildInvocation(\n spec: CaseSpec,\n worker: string,\n options: EngineOptions\n): { argv: string[]; cwd?: string } {\n if (spec.exec.kind === \"module\") {\n return { argv: [\"bun\", worker, spec.exec.modulePath, spec.id], cwd: options.cwd };\n }\n return { argv: spec.exec.argv, cwd: spec.exec.cwd ?? options.cwd };\n}\n\n/** Decide pass/fail/timeout, preferring the sentinel over the exit code. */\nfunction decideVerdict(spec: CaseSpec, result: SubprocOutcome, timeoutMs: number): Verdict {\n if (result.timedOut) return { outcome: \"timeout\", message: `timed out after ${timeoutMs}ms` };\n const sentinel = spec.exec.kind === \"module\" ? parseSentinel(result.stdout) : null;\n const pass = sentinel ? sentinel.pass : result.exitCode === 0;\n if (pass) return { outcome: \"pass\" };\n return { outcome: \"fail\", message: sentinel?.message ?? `exit code ${result.exitCode}` };\n}\n\nasync function runOneCase(\n spec: CaseSpec,\n tier: Tier,\n options: EngineOptions,\n worker: string\n): Promise<CaseReport> {\n const label = caseLabel(spec);\n const log = options.log ?? ((m: string) => console.log(m));\n\n const unmet = await firstUnmetNeed(spec.needs);\n if (unmet && !options.strictNeeds) {\n log(` ⊘ ${label} — skipped (needs ${unmet})`);\n return {\n tier: tier.name,\n id: spec.id,\n label,\n outcome: \"skip\",\n durationMs: 0,\n skipReason: `needs ${unmet}`,\n };\n }\n\n const timeoutMs = spec.timeoutMs ?? tier.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const env: Record<string, string | undefined> = { ...process.env, ...options.env };\n const { argv, cwd } = buildInvocation(spec, worker, options);\n\n const started = performance.now();\n const prefix = ` [${spec.id}] `;\n const result = await runSubprocess(argv, { cwd, env, timeoutMs }, (line) => {\n if (!line.includes(SENTINEL) && line.trim()) log(prefix + line);\n });\n const durationMs = Math.round(performance.now() - started);\n\n const verdict = decideVerdict(spec, result, timeoutMs);\n const tail = verdict.message ? ` — ${verdict.message}` : \"\";\n log(` ${VERDICT_ICON[verdict.outcome]} ${label} (${durationMs}ms)${tail}`);\n return {\n tier: tier.name,\n id: spec.id,\n label,\n outcome: verdict.outcome,\n durationMs,\n message: verdict.message,\n };\n}\n\nasync function runTier(tier: Tier, options: EngineOptions, worker: string): Promise<CaseReport[]> {\n const log = options.log ?? ((m: string) => console.log(m));\n const matched = tier.cases.filter((c) => caseMatches(c, options.only));\n if (matched.length === 0) return [];\n const selected = orderCases(matched, options.order ?? \"default\");\n\n log(\n `\\n▶ ${tier.name}${tier.description ? ` — ${tier.description}` : \"\"} (${selected.length} case${selected.length === 1 ? \"\" : \"s\"})`\n );\n\n const concurrency = Math.max(1, tier.concurrency ?? 1);\n const failFast = options.failFast ?? false;\n // Slots left empty when soft-fail-fast aborts before claiming them; filtered out below.\n const reports: (CaseReport | undefined)[] = new Array(selected.length);\n let next = 0;\n let aborted = false;\n async function worker_loop(): Promise<void> {\n while (!aborted) {\n const i = next++;\n const spec = selected[i];\n if (!spec) return;\n const report = await runOneCase(spec, tier, options, worker);\n reports[i] = report;\n if (failFast && (report.outcome === \"fail\" || report.outcome === \"timeout\")) {\n aborted = true; // soft: in-flight siblings finish, but no new case is claimed\n }\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, selected.length) }, worker_loop));\n const done = reports.filter((r): r is CaseReport => r !== undefined);\n if (aborted && done.length < selected.length) {\n log(\n ` ⏹ fail-fast: stopped after a failure — ${selected.length - done.length} case(s) not run`\n );\n }\n return done;\n}\n\n/** Run a plan and return a structured report. Does not exit the process. */\nexport async function runPlan(plan: TierPlan, options: EngineOptions = {}): Promise<RunReport> {\n const log = options.log ?? ((m: string) => console.log(m));\n const worker = await workerPath();\n const wanted = options.tiers && options.tiers.length > 0 ? new Set(options.tiers) : null;\n\n const started = performance.now();\n const stopOnFail = Boolean(options.bail || options.failFast);\n const all: CaseReport[] = [];\n for (const tier of plan.tiers) {\n if (wanted && !wanted.has(tier.name)) continue;\n const reports = await runTier(tier, options, worker);\n all.push(...reports);\n if (stopOnFail && reports.some((r) => r.outcome === \"fail\" || r.outcome === \"timeout\")) {\n log(\n `\\n⏹ ${options.failFast ? \"fail-fast\" : \"bail\"}: stopping after failing tier \"${tier.name}\"`\n );\n break;\n }\n }\n\n const durationMs = Math.round(performance.now() - started);\n const passed = all.filter((r) => r.outcome === \"pass\").length;\n const failed = all.filter((r) => r.outcome === \"fail\" || r.outcome === \"timeout\").length;\n const skipped = all.filter((r) => r.outcome === \"skip\").length;\n return { cases: all, passed, failed, skipped, durationMs, ok: failed === 0 };\n}\n",
|
|
12
|
+
"/**\n * Reporting for engine runs: a human summary table (with per-case timing and a\n * slowest-N list, the data the old `&&` chains never captured) and a JSON\n * artefact for tooling. No cloud reporters — everything lands on disk locally.\n */\nimport type { RunReport, TierPlan } from \"./types\";\n\nconst ICON = { pass: \"✓\", fail: \"✗\", skip: \"⊘\", timeout: \"⏱\" } as const;\n\n/** One-line-per-case summary plus totals and slowest cases. */\nexport function formatSummary(report: RunReport): string {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(\"── results ─────────────────────────────────\");\n for (const c of report.cases) {\n const time = c.outcome === \"skip\" ? \"\" : `${c.durationMs}ms`;\n const tail = c.outcome === \"skip\" ? ` (${c.skipReason})` : c.message ? ` — ${c.message}` : \"\";\n lines.push(`${ICON[c.outcome]} ${c.tier} › ${c.label} ${time}${tail}`);\n }\n\n const ran = report.cases.filter((c) => c.outcome !== \"skip\");\n const slowest = [...ran].sort((a, b) => b.durationMs - a.durationMs).slice(0, 5);\n if (slowest.length > 0) {\n lines.push(\"\");\n lines.push(\"slowest:\");\n for (const c of slowest) lines.push(` ${c.durationMs}ms ${c.tier} › ${c.label}`);\n }\n\n lines.push(\"\");\n lines.push(\n `${report.ok ? \"PASS\" : \"FAIL\"} ${report.passed} passed, ${report.failed} failed, ` +\n `${report.skipped} skipped in ${(report.durationMs / 1000).toFixed(1)}s`\n );\n return lines.join(\"\\n\");\n}\n\n/** Stable JSON artefact written to test-results/. */\nexport function toJSON(report: RunReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/** `--list` output: tiers and their cases without running anything. */\nexport function formatPlan(plan: TierPlan): string {\n const lines: string[] = [\"Tiers (run order):\", \"\"];\n for (const tier of plan.tiers) {\n const conc = tier.concurrency && tier.concurrency > 1 ? ` ×${tier.concurrency}` : \"\";\n lines.push(`${tier.name}${conc}${tier.description ? ` — ${tier.description}` : \"\"}`);\n for (const c of tier.cases) {\n const needs = c.needs && c.needs.length > 0 ? ` (needs ${c.needs.join(\", \")})` : \"\";\n const cost = c.cost ? ` [${c.cost}]` : \"\";\n lines.push(` ${c.id}${cost}${needs}`);\n }\n lines.push(\"\");\n }\n return lines.join(\"\\n\");\n}\n"
|
|
12
13
|
],
|
|
13
|
-
"mappings": "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",
|
|
14
|
-
"debugId": "
|
|
14
|
+
"mappings": "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",
|
|
15
|
+
"debugId": "CD4A2E88C15B8A6864756E2164756E21",
|
|
15
16
|
"names": []
|
|
16
17
|
}
|
|
@@ -10,5 +10,6 @@ export { DEFAULT_JSON, parseTierArgs, type TierArgs } from "./args";
|
|
|
10
10
|
export { firstUnmetNeed, hasNeed } from "./detect";
|
|
11
11
|
export { discoverPlan } from "./discover";
|
|
12
12
|
export { runPlan } from "./engine";
|
|
13
|
+
export { orderCases } from "./order";
|
|
13
14
|
export { formatPlan, formatSummary, toJSON } from "./reporter";
|
|
14
|
-
export type { CaseExec, CaseReport, CaseSpec, EngineOptions, Need, RunReport, Tier, TierPlan, } from "./types";
|
|
15
|
+
export type { CaseExec, CaseOrder, CaseReport, CaseSpec, EngineOptions, Need, RunReport, Tier, TierPlan, } from "./types";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Within-tier case ordering.
|
|
3
|
+
*
|
|
4
|
+
* The engine drains a tier's cases through a bounded pool in array order, so
|
|
5
|
+
* where a case sits decides when it launches. Reordering by a coarse cost hint
|
|
6
|
+
* buys two different things depending on intent:
|
|
7
|
+
*
|
|
8
|
+
* - "cost" (heaviest first): the long poles launch in the first `concurrency`
|
|
9
|
+
* slots and everything cheaper packs in behind them, so wall-clock collapses
|
|
10
|
+
* toward the single slowest case instead of the sum. Use for a full run.
|
|
11
|
+
* - "fast" (lightest first): cheap, high-signal cases run first, so under
|
|
12
|
+
* soft-fail-fast they trip the abort soonest — you can't un-spawn a 4-minute
|
|
13
|
+
* case, so you want it to *not* have started when a 2-second case fails.
|
|
14
|
+
* - "default": registry definition order, unchanged.
|
|
15
|
+
*
|
|
16
|
+
* Pure and dependency-free on purpose: this is the one piece of the engine that
|
|
17
|
+
* is worth unit-testing in isolation, and keeping it out of engine.ts keeps the
|
|
18
|
+
* subprocess-heavy engine off the unit-coverage table.
|
|
19
|
+
*/
|
|
20
|
+
import type { CaseOrder, CaseSpec } from "./types";
|
|
21
|
+
/**
|
|
22
|
+
* Return a copy of `cases` reordered per `order`. Stable: cases of equal weight
|
|
23
|
+
* keep their original relative order, and "default" returns the input as-is.
|
|
24
|
+
*/
|
|
25
|
+
export declare function orderCases(cases: CaseSpec[], order: CaseOrder): CaseSpec[];
|
|
@@ -39,6 +39,11 @@ export interface CaseSpec {
|
|
|
39
39
|
needs?: Need[];
|
|
40
40
|
/** Per-case timeout. Defaults to the engine's tier default. */
|
|
41
41
|
timeoutMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Coarse cost hint consumed by `--order` (see order.ts). Unset = "medium".
|
|
44
|
+
* A scheduling hint, not a measurement — tune from `--json` durations.
|
|
45
|
+
*/
|
|
46
|
+
cost?: "light" | "medium" | "heavy";
|
|
42
47
|
exec: CaseExec;
|
|
43
48
|
}
|
|
44
49
|
/** An ordered group of cases sharing a realism level. */
|
|
@@ -57,6 +62,8 @@ export interface TierPlan {
|
|
|
57
62
|
tiers: Tier[];
|
|
58
63
|
}
|
|
59
64
|
export type CaseOutcome = "pass" | "fail" | "skip" | "timeout";
|
|
65
|
+
/** How cases are ordered within a tier before the pool drains them. */
|
|
66
|
+
export type CaseOrder = "default" | "fast" | "cost";
|
|
60
67
|
export interface CaseReport {
|
|
61
68
|
tier: string;
|
|
62
69
|
id: string;
|
|
@@ -81,8 +88,16 @@ export interface EngineOptions {
|
|
|
81
88
|
tiers?: string[];
|
|
82
89
|
/** Only run cases whose id/label/tags match one of these substrings. */
|
|
83
90
|
only?: string[];
|
|
91
|
+
/** Order cases within each tier. Default "default" (definition order). */
|
|
92
|
+
order?: CaseOrder;
|
|
84
93
|
/** Stop after the first tier that has a failure. */
|
|
85
94
|
bail?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Soft fail-fast: on the first failing case, the tier's pool stops *claiming*
|
|
97
|
+
* new cases (in-flight ones finish) and the run stops after that tier. Unlike
|
|
98
|
+
* `bail`, which lets every case in the failing tier finish first.
|
|
99
|
+
*/
|
|
100
|
+
failFast?: boolean;
|
|
86
101
|
/** Treat unmet-need skips as failures (CI-strict). Default false. */
|
|
87
102
|
strictNeeds?: boolean;
|
|
88
103
|
/** Forwarded into each case's environment. */
|
|
@@ -7923,7 +7923,7 @@ function displaySetupSuccess(configPath) {
|
|
|
7923
7923
|
console.log();
|
|
7924
7924
|
console.log(" 1. Review the generated configuration file");
|
|
7925
7925
|
console.log(" 2. Fill in values marked with /* CONFIGURE */");
|
|
7926
|
-
console.log(" 3. Run '
|
|
7926
|
+
console.log(" 3. Run 'polly verify' to check your configuration");
|
|
7927
7927
|
console.log();
|
|
7928
7928
|
console.log(color("\uD83D\uDCA1 Tip:", COLORS.gray));
|
|
7929
7929
|
console.log(color(" Look for comments explaining what each field needs.", COLORS.gray));
|
|
@@ -7938,7 +7938,7 @@ async function validateCommand() {
|
|
|
7938
7938
|
if (result.valid) {
|
|
7939
7939
|
console.log(color(`✅ Configuration is complete and valid!
|
|
7940
7940
|
`, COLORS.green));
|
|
7941
|
-
console.log(" You can now run '
|
|
7941
|
+
console.log(" You can now run 'polly verify' to start verification.");
|
|
7942
7942
|
console.log();
|
|
7943
7943
|
return;
|
|
7944
7944
|
}
|
|
@@ -8162,8 +8162,8 @@ async function verifyCommand() {
|
|
|
8162
8162
|
console.log(color(` ... and ${errors.length - 3} more error(s)`, COLORS.gray));
|
|
8163
8163
|
console.log();
|
|
8164
8164
|
}
|
|
8165
|
-
console.log(" Run '
|
|
8166
|
-
console.log(" Run '
|
|
8165
|
+
console.log(" Run 'polly verify --validate' to see all issues");
|
|
8166
|
+
console.log(" Run 'polly verify --setup' to regenerate configuration");
|
|
8167
8167
|
console.log();
|
|
8168
8168
|
process.exit(1);
|
|
8169
8169
|
}
|
|
@@ -8613,36 +8613,41 @@ function displayVerificationResults(result, specDir) {
|
|
|
8613
8613
|
}
|
|
8614
8614
|
function showHelp() {
|
|
8615
8615
|
console.log(`
|
|
8616
|
-
${color("
|
|
8616
|
+
${color("polly verify", COLORS.blue)} - Formal verification for web extensions
|
|
8617
|
+
|
|
8618
|
+
Tests sample a few executions; a model checker explores every reachable state.
|
|
8619
|
+
This compiles your handlers and state into TLA+ and runs TLC to prove safety
|
|
8620
|
+
invariants hold under all interleavings — the ordering and concurrency bugs
|
|
8621
|
+
tests rarely reach.
|
|
8617
8622
|
|
|
8618
8623
|
${color("Commands:", COLORS.blue)}
|
|
8619
8624
|
|
|
8620
|
-
${color("
|
|
8625
|
+
${color("polly verify", COLORS.green)}
|
|
8621
8626
|
Run verification (validates config, generates specs, runs TLC)
|
|
8622
8627
|
|
|
8623
|
-
${color("
|
|
8628
|
+
${color("polly verify --strict", COLORS.green)}
|
|
8624
8629
|
Fail closed (non-zero exit) on model-coverage gaps: a declared state
|
|
8625
8630
|
field no handler writes, or an unverified $meshState/$peerState predicate.
|
|
8626
8631
|
Also via ${color("POLLY_VERIFY_STRICT=1", COLORS.yellow)}.
|
|
8627
8632
|
|
|
8628
|
-
${color("
|
|
8633
|
+
${color("polly verify --setup", COLORS.green)}
|
|
8629
8634
|
Analyze codebase and generate configuration file
|
|
8630
8635
|
|
|
8631
|
-
${color("
|
|
8636
|
+
${color("polly verify --validate", COLORS.green)}
|
|
8632
8637
|
Validate existing configuration without running verification
|
|
8633
8638
|
|
|
8634
|
-
${color("
|
|
8639
|
+
${color("polly verify --estimate", COLORS.green)}
|
|
8635
8640
|
Estimate state space without running TLC
|
|
8636
8641
|
|
|
8637
|
-
${color("
|
|
8642
|
+
${color("polly verify --help", COLORS.green)}
|
|
8638
8643
|
Show this help message
|
|
8639
8644
|
|
|
8640
8645
|
${color("Getting Started:", COLORS.blue)}
|
|
8641
8646
|
|
|
8642
|
-
1. Run ${color("
|
|
8647
|
+
1. Run ${color("polly verify --setup", COLORS.green)} to generate configuration
|
|
8643
8648
|
2. Review ${color("specs/verification.config.ts", COLORS.blue)} and fill in marked fields
|
|
8644
|
-
3. Run ${color("
|
|
8645
|
-
4. Run ${color("
|
|
8649
|
+
3. Run ${color("polly verify --validate", COLORS.green)} to check your configuration
|
|
8650
|
+
4. Run ${color("polly verify", COLORS.green)} to start verification
|
|
8646
8651
|
|
|
8647
8652
|
${color("Configuration Help:", COLORS.blue)}
|
|
8648
8653
|
|
|
@@ -8688,4 +8693,4 @@ main().catch((error) => {
|
|
|
8688
8693
|
process.exit(1);
|
|
8689
8694
|
});
|
|
8690
8695
|
|
|
8691
|
-
//# debugId=
|
|
8696
|
+
//# debugId=EBD97C743E15B6D964756E2164756E21
|