From 49240858297936f951ace7c9ff60e235eccc1b59 Mon Sep 17 00:00:00 2001 From: quality Date: Sun, 4 Oct 2026 20:45:46 -0400 Subject: [PATCH] test: name the run in e2e coverage tooling-error artifacts main() in tests/tools/e2e-coverage-report.mjs built its failure report from error.runId / error.runStatus, which only CoverageReportError carries. Every throw raised after the manifest is read and validated is a plain Error, so the published artifact header read E2E coverage: tooling-error (run null; status unknown) even for a run sealed passed. CI cats that text straight into the step summary, so a reader of a red End-to-end coverage job could not tell whether the Playwright run failed or only the reporting step broke. Fall back to the manifest, which readCoverageRun can re-read from --input. An unreadable or absent manifest still yields null, exactly as before. Closes #1069 Signed-off-by: quality --- tests/e2e-coverage-report.test.mjs | 63 +++++++++++++++++++++++++++++ tests/tools/e2e-coverage-report.mjs | 8 +++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/tests/e2e-coverage-report.test.mjs b/tests/e2e-coverage-report.test.mjs index 90efd320..c9d5cec2 100644 --- a/tests/e2e-coverage-report.test.mjs +++ b/tests/e2e-coverage-report.test.mjs @@ -1909,3 +1909,66 @@ test('collectE2ECoverage reports a script recorded with no functions as unattrib await rm(fixture.root, { recursive: true, force: true }); } }); + +test('a tooling-error artifact still names the run the manifest records', async () => { + const fixture = await fixtureRun(); + try { + await writeCoverageArtifact(fixture.runDir, 'worker-0-page-0', { + schemaVersion: 1, + kind: 'endusers.playwright.v8-coverage', + runId: 'run-1', + result: [], + }); + await sealCoverageRun(fixture.runDir, 'passed'); + const jsonPath = join(fixture.root, 'identified-error.json'); + const textPath = join(fixture.root, 'identified-error.txt'); + + await assert.rejects( + () => + main([ + '--input', + fixture.runDir, + '--root', + fixture.root, + '--build', + fixture.buildDir, + '--json', + jsonPath, + '--text', + textPath, + ]), + /No src\/\*\* coverage was attributable/, + ); + + const report = JSON.parse(await readFile(jsonPath, 'utf8')); + assert.equal(report.status, 'tooling-error'); + assert.equal(report.runId, 'run-1'); + assert.equal(report.runStatus, 'passed'); + assert.match( + await readFile(textPath, 'utf8'), + /^E2E coverage: tooling-error \(run run-1; status passed\)$/mu, + ); + } finally { + await rm(fixture.root, { recursive: true, force: true }); + } +}); + +test('a tooling-error artifact reports no run when the manifest is unreadable', async () => { + const root = await mkdtemp(join(tmpdir(), 'endusers-e2e-report-')); + try { + const runDir = join(root, 'coverage'); + await mkdir(runDir, { recursive: true }); + const jsonPath = join(root, 'unreadable-error.json'); + + await assert.rejects(() => + main(['--input', runDir, '--root', root, '--json', jsonPath]), + ); + + const report = JSON.parse(await readFile(jsonPath, 'utf8')); + assert.equal(report.status, 'tooling-error'); + assert.equal(report.runId, null); + assert.equal(report.runStatus, null); + } finally { + await rm(root, { recursive: true, force: true }); + } +}); diff --git a/tests/tools/e2e-coverage-report.mjs b/tests/tools/e2e-coverage-report.mjs index e9efcfc3..247025bb 100644 --- a/tests/tools/e2e-coverage-report.mjs +++ b/tests/tools/e2e-coverage-report.mjs @@ -684,6 +684,10 @@ export async function main(argv = process.argv.slice(2)) { }); if (!options.text) process.stdout.write(text); } catch (error) { + // Only the run-status guards raise CoverageReportError; every later failure + // is a plain Error, so re-read the manifest rather than publish a report + // that cannot say whether the run passed or the tooling broke. + const run = await readCoverageRun(resolve(options.input)).catch(() => null); const failure = { schemaVersion: 2, kind: ERROR_KIND, @@ -691,8 +695,8 @@ export async function main(argv = process.argv.slice(2)) { error.runStatus === 'failed' || error.runStatus === 'cancelled' ? error.runStatus : 'tooling-error', - runId: error.runId ?? null, - runStatus: error.runStatus ?? null, + runId: error.runId ?? run?.runId ?? null, + runStatus: error.runStatus ?? run?.status ?? null, scripts: { captured: 0, converted: 0, ignored: 0 }, sources: [], summary: {