import { test as base } from '@playwright/test'; import { mkdirSync, writeFileSync } from 'fs'; import { randomUUID } from 'crypto'; import path from 'path'; // Re-exported so specs can import everything from here — expect, Page, // APIRequestContext — and get the coverage-collecting `test` at the same time. // The explicit `test` below wins over the star export. export * from '@playwright/test'; const NYC_OUTPUT = path.resolve(__dirname, '..', '..', '.nyc_output'); const collectingCoverage = process.env.COVERAGE === 'true'; // Set once any page reports instrumentation. Checked by the collection script so // an empty run fails loudly instead of publishing 0% — see the note below. const MARKER = path.join(NYC_OUTPUT, '.collected'); /** * Flushes istanbul's per-page counters after each test. * * Coverage lives in `window.__coverage__` on the page and dies with it, so it * has to be read before the page closes — one file per test, because the suite * runs fullyParallel and workers would otherwise overwrite each other. */ export const test = base.extend<{ collectCoverage: void }>({ collectCoverage: [ async ({ page }, use) => { await use(); if (!collectingCoverage) return; // The page may already be closed by a test that navigated away or crashed; // a missing sample is not worth failing a passing test over. The run-level // check catches the case that actually matters — no samples at all. const coverage = await page .evaluate(() => (window as unknown as { __coverage__?: unknown }).__coverage__) .catch(() => undefined); if (!coverage) return; mkdirSync(NYC_OUTPUT, { recursive: true }); writeFileSync(path.join(NYC_OUTPUT, `${randomUUID()}.json`), JSON.stringify(coverage)); writeFileSync(MARKER, 'ok'); }, { auto: true } ] });