feat(cli): add --add-reporter to append reporters without replacing existing config (#41993)

This commit is contained in:
himasree424
2026-07-28 13:09:25 +01:00
committed by GitHub
parent 748f7bc9e4
commit 75689d1899
8 changed files with 25 additions and 5 deletions
+1
View File
@@ -79,6 +79,7 @@ npx playwright test --ui
| Option | Description |
| :--- | :--- |
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
| `--add-reporter <reporter>` | Reporter to add on top of the reporters configured in the config file, comma-separated. Can be a built-in reporter name or a path to a custom reporter file. Unlike `--reporter`, this keeps the configured reporters instead of replacing them. |
| `-c <file>` or `--config <file>` | Configuration file, or a test directory with optional "playwright.config.&#123;m,c&#125;?&#123;js,ts&#125;". Defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. |
| `--debug` | Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options. |
| `--fail-on-flaky-tests` | Fail if any test is flagged as flaky (default: false). |
@@ -126,6 +126,7 @@ function overridesFromOptions(options: { [key: string]: any }): ipc.ConfigCLIOve
repeatEach: options.repeatEach ? parseInt(options.repeatEach, 10) : undefined,
retries: options.retries ? parseInt(options.retries, 10) : undefined,
reporter: resolveReporterOption(options.reporter),
additionalReporters: resolveReporterOption(options.addReporter),
shard: resolveShardOption(options.shard),
timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
tsconfig: options.tsconfig ? path.resolve(process.cwd(), options.tsconfig) : undefined,
+1 -1
View File
@@ -100,7 +100,7 @@ export class FullConfigInternal {
preserveOutput: takeFirst(userConfig.preserveOutput, 'always'),
projects: [],
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
reporter: [...takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]), ...(configCLIOverrides.additionalReporters ?? [])],
reportSlowTests: takeFirst(userConfig.reportSlowTests, { max: 5, threshold: 300_000 /* 5 minutes */ }),
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
tags: globalTags,
+1
View File
@@ -36,6 +36,7 @@ export type ConfigCLIOverrides = {
repeatEach?: number;
retries?: number;
reporter?: ReporterDescription[];
additionalReporters?: ReporterDescription[];
shard?: { current: number, total: number };
timeout?: number;
tsconfig?: string;
+1
View File
@@ -206,6 +206,7 @@ const kTraceModes: TraceMode[] = ['on', 'off', 'on-first-retry', 'on-all-retries
// Note: update docs/src/test-cli-js.md when you update this, program is the source of truth.
const testOptions: [string, { description: string, choices?: string[], preset?: string }][] = [
['--add-reporter <reporter>', { description: `Reporter to add on top of the configured reporters, comma-separated, can be ${builtInReporters.map(name => `"${name}"`).join(', ')} or a path to a reporter module` }],
/* deprecated */ ['--browser <browser>', { description: `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")` }],
['-c, --config <file>', { description: `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"` }],
['--debug [mode]', { description: `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`, choices: ['inspector', 'cli'], preset: 'inspector' }],
@@ -51,8 +51,6 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' |
};
const reporters: ReporterV2[] = [];
descriptions ??= config.config.reporter;
if (runOptions?.additionalReporters)
descriptions = [...descriptions, ...runOptions.additionalReporters];
const reportOptions = reporterCommandOptions(config, mode, runOptions);
for (const r of descriptions) {
const [name, arg] = r;
-2
View File
@@ -37,7 +37,6 @@ import type { TestGroup } from '../runner/testGroups';
import type { EnvByProjectId } from './dispatcher';
import type { TestRunnerPluginRegistration } from '../plugins';
import type { Task } from './taskRunner';
import type { ReporterDescription } from '../../types/test';
import type { FullResult, TestError } from '../../types/testReporter';
import type { Matcher, TestCaseFilter } from '../util';
import type { InternalReporter } from '../reporters/internalReporter';
@@ -72,7 +71,6 @@ export type TestRunOptions = {
pauseAtEnd?: boolean;
onTestPaused?: (params: TestPausedParams) => void;
preserveOutputDir?: boolean;
additionalReporters?: ReporterDescription[];
shardWeights?: number[];
};
+20
View File
@@ -987,3 +987,23 @@ test('AggregateError sub-errors are spread into testInfo.errors', async ({ runIn
expect.stringMatching(/^FRAME Error: sub b: at .*a\.spec\.ts:19:/),
]);
});
test('--add-reporter should append to configured reporters instead of replacing them', async ({ runInlineTest }) => {
const result = await runInlineTest({
'configured-reporter.js': `
module.exports = class { onBegin() { console.log('FROM_CONFIGURED_REPORTER'); } };
`,
'added-reporter.js': `
module.exports = class { onBegin() { console.log('FROM_ADDED_REPORTER'); } };
`,
'playwright.config.ts': `module.exports = { reporter: [['./configured-reporter.js']] };`,
'a.spec.js': `
const { test } = require('@playwright/test');
test('test', () => {});
`,
}, { 'workers': 1 }, undefined, { additionalArgs: ['--add-reporter=./added-reporter.js'] });
expect(result.exitCode).toBe(0);
expect(result.output).toContain('FROM_CONFIGURED_REPORTER');
expect(result.output).toContain('FROM_ADDED_REPORTER');
});