diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 3f982d22d5..ec173143cb 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -98,7 +98,7 @@ function addMergeReportsCommand(program: Command) { } }); command.option('-c, --config ', `Configuration file. Can be used to specify additional configuration for the output report.`); - command.option('--reporter ', 'Output report type', 'list'); + command.option('--reporter ', 'Output report type', 'list'); command.addHelpText('afterAll', ` Arguments [dir]: Directory containing blob reports. @@ -178,10 +178,10 @@ async function mergeReports(reportDir: string | undefined, opts: { [key: string] const configLoader = new ConfigLoader(); const config = await (configFile ? configLoader.loadConfigFile(configFile) : configLoader.loadEmptyConfig(process.cwd())); - const dir = path.resolve(process.cwd(), reportDir || 'playwright-report'); + const dir = path.resolve(process.cwd(), reportDir || ''); if (!(await fs.promises.stat(dir)).isDirectory()) throw new Error('Directory does not exist: ' + dir); - await createMergedReport(config, dir, opts.reporter || 'list'); + await createMergedReport(config, dir, opts.reporter || ['list']); } function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrides { diff --git a/packages/playwright-test/src/reporters/blob.ts b/packages/playwright-test/src/reporters/blob.ts index e303b524c2..721356d748 100644 --- a/packages/playwright-test/src/reporters/blob.ts +++ b/packages/playwright-test/src/reporters/blob.ts @@ -29,6 +29,7 @@ import { TeleReporterReceiver, type JsonEvent, type JsonProject, type JsonSuite, import { createReporters } from '../runner/reporters'; import { defaultReportFolder } from './html'; import { TeleReporterEmitter } from './teleEmitter'; +import { Multiplexer } from './multiplexer'; type BlobReporterOptions = { @@ -104,7 +105,7 @@ export class BlobReporter extends TeleReporterEmitter { } } -export async function createMergedReport(config: FullConfigInternal, dir: string, reporterName?: string) { +export async function createMergedReport(config: FullConfigInternal, dir: string, reporterNames: string[]) { const shardFiles = await sortedShardFiles(dir); const resourceDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-report-')); await fs.promises.mkdir(resourceDir, { recursive: true }); @@ -113,10 +114,8 @@ export async function createMergedReport(config: FullConfigInternal, dir: string const events = mergeEvents(shardReports); patchAttachmentPaths(events, resourceDir); - reporterName ??= 'list'; - - const reporters = await createReporters(config, 'merge', [reporterName]); - const receiver = new TeleReporterReceiver(path.sep, reporters[0]); + const reporters = await createReporters(config, 'merge', reporterNames); + const receiver = new TeleReporterReceiver(path.sep, new Multiplexer(reporters)); for (const event of events) await receiver.dispatch(event); } finally { diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index 2c1e94e062..d8a2efc100 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -310,4 +310,60 @@ test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, p await page.getByText('failing 1').click(); await expect(page.getByText('\'text-attachment\', { body: \'hi!\'')).toBeVisible(); +}); + +test('multiple output reports', async ({ runInlineTest, mergeReports, showReport, page }) => { + test.slow(); + const reportDir = test.info().outputPath('blob-report'); + const files = { + 'playwright.config.ts': ` + module.exports = { + retries: 1, + reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + import fs from 'fs'; + + test('first', async ({}) => { + const attachmentPath = test.info().outputPath('foo.txt'); + fs.writeFileSync(attachmentPath, 'hello!'); + await test.info().attach('file-attachment', {path: attachmentPath}); + + console.log('console info'); + console.error('console error'); + }); + test('failing 1', async ({}) => { + await test.info().attach('text-attachment', { body: 'hi!' }); + expect(1).toBe(2); + }); + test.skip('skipped 1', async ({}) => {}); + `, + 'b.test.js': ` + import { test, expect } from '@playwright/test'; + test('math 2', async ({}) => { }); + test('failing 2', async ({}) => { + expect(1).toBe(2); + }); + test.skip('skipped 2', async ({}) => {}); + ` + }; + await runInlineTest(files, { shard: `1/2` }); + + const reportFiles = await fs.promises.readdir(reportDir); + reportFiles.sort(); + expect(reportFiles).toEqual(['report-1-of-2.zip']); + const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_DEBUG_REPORTERS': '1' }, { additionalArgs: ['--reporter', 'html', '--reporter', 'line'] }); + expect(exitCode).toBe(0); + + // Check that line reporter was called. + const text = stripAnsi(output); + expect(text).toContain('Running 3 tests using 1 worker'); + expect(text).toContain('[1/3] a.test.js:5:11 › first'); + expect(text).toContain('a.test.js:13:11 › failing 1 (retry #1)'); + + // Check html report presence. + await showReport(); + await expect(page.getByText('first')).toBeVisible(); }); \ No newline at end of file