From a511731f55e3cd31dc8b4937a538ac565b9a3e82 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 22 Mar 2024 10:02:00 -0700 Subject: [PATCH] fix(html): keep projects from different bots separate (#30051) --- packages/playwright/src/reporters/html.ts | 20 ++++++++++++- tests/playwright-test/reporter-blob.spec.ts | 33 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index e0262122d1..d32e581c61 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -213,6 +213,8 @@ class HtmlBuilder { private _dataZipFile: ZipFile; private _hasTraces = false; private _attachmentsBaseURL: string; + private _projectToId: Map = new Map(); + private _lastProjectId = 0; constructor(config: FullConfig, outputDir: string, attachmentsBaseURL: string) { this._config = config; @@ -353,7 +355,7 @@ class HtmlBuilder { path = path.slice(1); const [file, ...titles] = test.titlePath(); - const testIdExpression = `[project=${projectName}]${toPosixPath(file)}\x1e${titles.join('\x1e')} (repeat:${test.repeatEachIndex})`; + const testIdExpression = `[project=${this._projectId(test.parent)}]${toPosixPath(file)}\x1e${titles.join('\x1e')} (repeat:${test.repeatEachIndex})`; const testId = fileId + '-' + calculateSha1(testIdExpression).slice(0, 20); const results = test.results.map(r => this._createTestResult(test, r)); @@ -392,6 +394,16 @@ class HtmlBuilder { }; } + private _projectId(suite: Suite): number { + const project = projectSuite(suite); + let id = this._projectToId.get(project); + if (!id) { + id = ++this._lastProjectId; + this._projectToId.set(project, id); + } + return id; + } + private _serializeAttachments(attachments: JsonAttachment[]) { let lastAttachment: TestAttachment | undefined; return attachments.map(a => { @@ -629,4 +641,10 @@ function createSnippets(stepsInFile: MultiMap) { } } +function projectSuite(suite: Suite): Suite { + while (suite.parent?.parent) + suite = suite.parent; + return suite; +} + export default HtmlReporter; diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index 6e18651af9..e12ec3c069 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -1236,6 +1236,39 @@ test('preserve reportName on projects', async ({ runInlineTest, mergeReports }) expect(output).toContain(`botNames: first,second`); }); +test('keep projects with same name different bot name separate', async ({ runInlineTest, mergeReports, showReport, page }) => { + const files = (reportName: string) => ({ + 'playwright.config.ts': ` + module.exports = { + reporter: [['blob', { fileName: '${reportName}.zip' }]], + projects: [ + { name: 'foo' }, + ] + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test('test 1', async ({}) => { expect('${reportName}').toBe('second'); }); + `, + }); + + await runInlineTest(files('first'), undefined, { PWTEST_BOT_NAME: 'first' }); + await runInlineTest(files('second'), undefined, { PWTEST_BOT_NAME: 'second', PWTEST_BLOB_DO_NOT_REMOVE: '1' }); + + const reportDir = test.info().outputPath('blob-report'); + const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); + expect(exitCode).toBe(0); + await showReport(); + await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('1'); + await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('1'); + + await page.getByText(/test 1.*first/).getByRole('link', { name: 'test' }).click(); + await expect(page.getByText('Errors')).toBeVisible(); + await page.goBack(); + await page.getByText(/test 1.*second/).getByRole('link', { name: 'test' }).click(); + await expect(page.getByText('Errors')).not.toBeVisible(); +}); + test('no reports error', async ({ runInlineTest, mergeReports }) => { const reportDir = test.info().outputPath('blob-report'); fs.mkdirSync(reportDir, { recursive: true });