From b7d22b64e8c56eb83e0d631a46d93d5ee1c25544 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 18 Jan 2024 15:11:32 -0800 Subject: [PATCH] feat(merge): prepend bot name to global errors (#29055) This way one can figure out where does the error come from. An example merged report that exhibits the issue: https://mspwblobreport.z1.web.core.windows.net/run-7563628632-1-2328b83af75801ab76bb06c214fee483cf5bc07c/index.html#?q=s%3Afailed%20s%3Aflaky --- packages/playwright/src/reporters/merge.ts | 25 ++++++++++++++++++++- tests/playwright-test/reporter-blob.spec.ts | 3 ++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/playwright/src/reporters/merge.ts b/packages/playwright/src/reporters/merge.ts index f206c592e8..9e81f92e7a 100644 --- a/packages/playwright/src/reporters/merge.ts +++ b/packages/playwright/src/reporters/merge.ts @@ -26,12 +26,14 @@ import { Multiplexer } from './multiplexer'; import { ZipFile, calculateSha1 } from 'playwright-core/lib/utils'; import { currentBlobReportVersion, type BlobReportMetadata } from './blob'; import { relativeFilePath } from '../util'; +import type { TestError } from '../../types/testReporter'; type StatusCallback = (message: string) => void; type ReportData = { eventPatchers: JsonEventPatchers; reportFile: string; + metadata: BlobReportMetadata; }; export async function createMergedReport(config: FullConfigInternal, dir: string, reporterDescriptions: ReporterDescription[], rootDirOverride: string | undefined) { @@ -65,11 +67,13 @@ export async function createMergedReport(config: FullConfigInternal, dir: string }; await dispatchEvents(eventData.prologue); - for (const { reportFile, eventPatchers } of eventData.reports) { + for (const { reportFile, eventPatchers, metadata } of eventData.reports) { const reportJsonl = await fs.promises.readFile(reportFile); const events = parseTestEvents(reportJsonl); new JsonStringInternalizer(stringPool).traverse(events); eventPatchers.patchers.push(new AttachmentPathPatcher(dir)); + if (metadata.name) + eventPatchers.patchers.push(new GlobalErrorPatcher(metadata.name)); eventPatchers.patchEvents(events); await dispatchEvents(events); } @@ -213,6 +217,7 @@ async function mergeEvents(dir: string, shardReportFiles: string[], stringPool: reports.push({ eventPatchers, reportFile: localPath, + metadata, }); } @@ -465,6 +470,24 @@ class PathSeparatorPatcher { } } +class GlobalErrorPatcher { + private _prefix: string; + + constructor(botName: string) { + this._prefix = `(${botName}) `; + } + + patchEvent(event: JsonEvent) { + if (event.method !== 'onError') + return; + const error = event.params.error as TestError; + if (error.message !== undefined) + error.message = this._prefix + error.message; + if (error.stack !== undefined) + error.stack = this._prefix + error.stack; + } +} + interface JsonEventPatcher { patchEvent(event: JsonEvent): void; } diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index e69789fb45..63b6fd815b 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -904,7 +904,7 @@ test('onError in the report', async ({ runInlineTest, mergeReports, showReport, test.skip('skipped 3', async ({}) => {}); ` }; - const result = await runInlineTest(files, { shard: `1/3` }); + const result = await runInlineTest(files, { shard: `1/3` }, { PWTEST_BOT_NAME: 'macos-node16-ttest' }); expect(result.exitCode).toBe(1); const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); @@ -917,6 +917,7 @@ test('onError in the report', async ({ runInlineTest, mergeReports, showReport, await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('0'); await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('0'); await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('1'); + await expect(page.getByTestId('report-errors')).toContainText('(macos-node16-ttest) Error: Error in teardown'); }); test('preserve config fields', async ({ runInlineTest, mergeReports }) => {