diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3eb1065d..00822577 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -25,6 +25,7 @@ jobs: E2E_TEST_PASSWORD: visual-test-password steps: - uses: actions/checkout@v7 + - run: node scripts/test-e2e-visual-snapshot-files.mjs - uses: actions/setup-node@v7 with: node-version: "24" diff --git a/package.json b/package.json index c987b00c..be49c90d 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "test:collector-runtime": "node scripts/test-local-collector-runtime.mjs", "test:cf-token-map": "bash scripts/test-cf-token-map.sh", "test:replica-status-probe": "bash scripts/test-replica-status-probe.sh", + "test:e2e-visual-snapshot-files": "node scripts/test-e2e-visual-snapshot-files.mjs", "test:audit-ci": "node scripts/test-audit-ci.mjs", "test:sentry-ci-report": "python3 scripts/test-sentry-ci-report.py", "verify": "npm run test:sentry-ci-report && npm run lint && npm run typecheck && npm test && npm run test:receipt-inbox-worker && npm run test:migrate-safe && npm run test:sqlite-backup && npm run test:r2-archive && npm run test:startup-config && npm run test:oracle-deploy && npm run test:apple-projects && npm run test:antigravity-collector && npm run test:session-token-collectors && npm run test:collector-runtime && npm run test:cf-token-map && npm run test:replica-status-probe && npm run build" diff --git a/scripts/test-e2e-visual-snapshot-files.mjs b/scripts/test-e2e-visual-snapshot-files.mjs new file mode 100644 index 00000000..36e2a439 --- /dev/null +++ b/scripts/test-e2e-visual-snapshot-files.mjs @@ -0,0 +1,31 @@ +#!/usr/bin/env node +/** + * Fail fast when visual.spec.ts references a screenshot baseline that is not + * committed under tests/e2e/visual.spec.ts-snapshots/. Playwright only + * reports missing files at assertion time (#1615 landed fleet-budget shots + * without PNGs). + */ +import { readFileSync, existsSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const specPath = join(root, "tests/e2e/visual.spec.ts"); +const snapDir = join(root, "tests/e2e/visual.spec.ts-snapshots"); +const spec = readFileSync(specPath, "utf8"); +const re = /toHaveScreenshot\(\s*['"]([^'"]+)['"]/g; +const names = [...spec.matchAll(re)].map((match) => match[1]); +const missing = names.filter((name) => { + const base = name.replace(/\.png$/i, ""); + return !existsSync(join(snapDir, `${base}-chromium-linux.png`)); +}); + +if (missing.length > 0) { + console.error("Missing committed Playwright visual baselines:"); + for (const name of missing) { + console.error(` ${name} -> ${snapDir}/${name.replace(/\.png$/i, "")}-chromium-linux.png`); + } + process.exit(1); +} + +console.log(`ok: ${names.length} visual baseline PNG(s) present`);