Add CI coverage summaries and aggregate JaCoCo report (#6451)

This commit is contained in:
Anthony Stirling
2026-06-02 14:59:10 +01:00
committed by GitHub
parent 2c0ebc28a7
commit de9d6ad3f5
15 changed files with 1583 additions and 23 deletions
@@ -1,17 +1,45 @@
import { test as base, expect } from "@playwright/test";
import * as fs from "node:fs/promises";
import * as path from "node:path";
/**
* Custom test fixture that auto-dismisses the cookie consent banner
* before every test. The banner (#cc-main) overlays the page and
* intercepts pointer events, causing click timeouts across all tests.
* Custom test fixture that:
* 1. auto-dismisses the cookie consent banner (the banner overlays
* the page and intercepts pointer events, causing click timeouts
* across all tests);
* 2. optionally captures V8 JS coverage per test when PW_COVERAGE=1.
*
* Coverage notes:
* - Only chromium supports page.coverage.* - the try/catch silently
* skips collection on firefox/webkit so cross-browser runs don't
* fail when coverage is enabled.
* - Raw V8 dumps land under .test-state/playwright/coverage-pw/ as
* <test-title>-<testId>.json. CI post-processes them with
* scripts/playwright-coverage-summary.py into a vitest-shaped
* coverage-summary.json that the existing helper renders.
* - resetOnNavigation: false so navigation between pages inside the
* same test (login → tool page → preview) accumulates instead of
* starting fresh.
*
* Usage: import { test, expect } from '@app/tests/helpers/test-base';
*/
const COVERAGE_ENABLED = process.env.PW_COVERAGE === "1";
// Path is workspace-relative because Playwright cwd is frontend/editor/
// at runtime (defined by playwright.config.ts). Going up two levels
// lands at the repo root - the same `.test-state/playwright/` that
// `task e2e:live` already provisions.
const COVERAGE_DIR = path.resolve(
process.cwd(),
"..",
"..",
".test-state",
"playwright",
"coverage-pw",
);
export const test = base.extend({
page: async ({ page }, use) => {
// Set the cookie consent cookie before any navigation so the banner
// never appears. The cookieconsent library (orestbida/cookieconsent)
// reads this cookie on init and skips the banner if consent exists.
page: async ({ page }, use, testInfo) => {
await page.context().addCookies([
{
name: "cc_cookie",
@@ -28,7 +56,37 @@ export const test = base.extend({
},
]);
if (COVERAGE_ENABLED) {
try {
await page.coverage.startJSCoverage({ resetOnNavigation: false });
} catch {
// Browser doesn't support V8 coverage (firefox/webkit) - silently skip.
}
}
await use(page);
if (COVERAGE_ENABLED) {
try {
const entries = await page.coverage.stopJSCoverage();
if (entries.length > 0) {
await fs.mkdir(COVERAGE_DIR, { recursive: true });
// Sanitize the path so it survives every filesystem we run on.
const safeTitle = testInfo.titlePath
.join("-")
.replace(/[^A-Za-z0-9._-]/g, "_")
.slice(0, 120);
const outPath = path.join(
COVERAGE_DIR,
`${safeTitle}-${testInfo.testId}.json`,
);
await fs.writeFile(outPath, JSON.stringify(entries));
}
} catch {
// Browser closed or coverage unsupported - swallow so a coverage
// failure never fails the underlying test.
}
}
},
});