From d5907f4b13d7ca54b9d5ea69004af3c88e715d77 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 4 Apr 2024 11:04:51 -0700 Subject: [PATCH] feat(junit): includeProjectInTestName option (#30233) Fixes #30246. --- packages/playwright/src/reporters/junit.ts | 11 +++++--- packages/playwright/types/test.d.ts | 2 +- tests/playwright-test/reporter-junit.spec.ts | 27 ++++++++++++++++++++ utils/generate_types/overrides-test.d.ts | 2 +- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/playwright/src/reporters/junit.ts b/packages/playwright/src/reporters/junit.ts index 6adb7d4168..cc526abb93 100644 --- a/packages/playwright/src/reporters/junit.ts +++ b/packages/playwright/src/reporters/junit.ts @@ -33,11 +33,13 @@ class JUnitReporter extends EmptyReporter { private outputFile: string | undefined; private resolvedOutputFile: string | undefined; private stripANSIControlSequences = false; + private includeProjectInTestName = false; - constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean } = {}) { + constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean } = {}) { super(); this.outputFile = options.outputFile || reportOutputNameFromEnv(); this.stripANSIControlSequences = options.stripANSIControlSequences || false; + this.includeProjectInTestName = options.includeProjectInTestName || false; } override printsToStdio() { @@ -98,6 +100,7 @@ class JUnitReporter extends EmptyReporter { let failures = 0; let duration = 0; const children: XMLEntry[] = []; + const testCaseNamePrefix = projectName && this.includeProjectInTestName ? `[${projectName}] ` : ''; for (const test of suite.allTests()){ ++tests; @@ -107,7 +110,7 @@ class JUnitReporter extends EmptyReporter { ++failures; for (const result of test.results) duration += result.duration; - await this._addTestCase(suite.title, test, children); + await this._addTestCase(suite.title, testCaseNamePrefix, test, children); } this.totalTests += tests; @@ -132,12 +135,12 @@ class JUnitReporter extends EmptyReporter { return entry; } - private async _addTestCase(suiteName: string, test: TestCase, entries: XMLEntry[]) { + private async _addTestCase(suiteName: string, namePrefix: string, test: TestCase, entries: XMLEntry[]) { const entry = { name: 'testcase', attributes: { // Skip root, project, file - name: test.titlePath().slice(3).join(' › '), + name: namePrefix + test.titlePath().slice(3).join(' › '), // filename classname: suiteName, time: (test.results.reduce((acc, value) => acc + value.duration, 0)) / 1000 diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 9d065280ac..7883711476 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -24,7 +24,7 @@ export type ReporterDescription = ['line'] | ['list'] | ['list', { printSteps?: boolean }] | ['github'] | - ['junit'] | ['junit', { outputFile?: string, stripANSIControlSequences?: boolean }] | + ['junit'] | ['junit', { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean }] | ['json'] | ['json', { outputFile?: string }] | ['html'] | ['html', { outputFolder?: string, open?: 'always' | 'never' | 'on-failure', host?: string, port?: number, attachmentsBaseURL?: string }] | ['null'] | diff --git a/tests/playwright-test/reporter-junit.spec.ts b/tests/playwright-test/reporter-junit.spec.ts index cc4175219e..cade24f3b6 100644 --- a/tests/playwright-test/reporter-junit.spec.ts +++ b/tests/playwright-test/reporter-junit.spec.ts @@ -280,6 +280,33 @@ for (const useIntermediateMergeReport of [false, true] as const) { expect(result.exitCode).toBe(0); }); + test('should includeProjectInTestName', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + projects: [ { name: 'project1' }, { name: 'project2' } ], + reporter: [['junit', { includeProjectInTestName: true }]] + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test('one', async ({}) => { + expect(1).toBe(1); + }); + `, + }, { reporter: '' }); + expect(result.exitCode).toBe(0); + const xml = parseXML(result.output); + expect(xml['testsuites']['testsuite'][0]['$']['name']).toBe('a.test.js'); + expect(xml['testsuites']['testsuite'][0]['$']['hostname']).toBe('project1'); + expect(xml['testsuites']['testsuite'][0]['$']['tests']).toBe('1'); + expect(xml['testsuites']['testsuite'][0]['testcase'][0]['$']['name']).toBe('[project1] one'); + expect(xml['testsuites']['testsuite'][1]['$']['name']).toBe('a.test.js'); + expect(xml['testsuites']['testsuite'][1]['$']['hostname']).toBe('project2'); + expect(xml['testsuites']['testsuite'][1]['$']['tests']).toBe('1'); + expect(xml['testsuites']['testsuite'][1]['testcase'][0]['$']['name']).toBe('[project2] one'); + }); + test('should render existing attachments, but not missing ones', async ({ runInlineTest }) => { test.skip(useIntermediateMergeReport, 'Blob report hashes attachment paths'); const result = await runInlineTest({ diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 2d1ba769e6..72cdf3a7b3 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -23,7 +23,7 @@ export type ReporterDescription = ['line'] | ['list'] | ['list', { printSteps?: boolean }] | ['github'] | - ['junit'] | ['junit', { outputFile?: string, stripANSIControlSequences?: boolean }] | + ['junit'] | ['junit', { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean }] | ['json'] | ['json', { outputFile?: string }] | ['html'] | ['html', { outputFolder?: string, open?: 'always' | 'never' | 'on-failure', host?: string, port?: number, attachmentsBaseURL?: string }] | ['null'] |