diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts index d1c86fe159..cfe294a75c 100644 --- a/packages/playwright/src/common/config.ts +++ b/packages/playwright/src/common/config.ts @@ -58,11 +58,6 @@ export class FullConfigInternal { testIdMatcher?: Matcher; defineConfigWasUsed = false; - // TODO: when merging reports, there could be no internal config. This is very unfortunate. - static from(config: FullConfig): FullConfigInternal | undefined { - return (config as any)[configInternalSymbol]; - } - constructor(location: ConfigLocation, userConfig: Config, configCLIOverrides: ConfigCLIOverrides) { if (configCLIOverrides.projects && userConfig.projects) throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`); diff --git a/packages/playwright/src/isomorphic/teleReceiver.ts b/packages/playwright/src/isomorphic/teleReceiver.ts index 9624a76876..a7f9f412f1 100644 --- a/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/packages/playwright/src/isomorphic/teleReceiver.ts @@ -26,9 +26,7 @@ export type JsonStackFrame = { file: string, line: number, column: number }; export type JsonStdIOType = 'stdout' | 'stderr'; -export type JsonConfig = Pick & { - listOnly: boolean; -}; +export type JsonConfig = Pick; export type MergeReporterConfig = Pick; @@ -147,10 +145,11 @@ export class TeleReporterReceiver { this._reportConfig = reportConfig; } - dispatch(message: JsonEvent): Promise | void { + dispatch(mode: 'list' | 'test', message: JsonEvent): Promise | void { const { method, params } = message; if (method === 'onConfigure') { this._onConfigure(params.config); + this._listOnly = mode === 'list'; return; } if (method === 'onProject') { @@ -197,7 +196,6 @@ export class TeleReporterReceiver { private _onConfigure(config: JsonConfig) { this._rootDir = config.rootDir; - this._listOnly = config.listOnly; this._config = this._parseConfig(config); this._reporter.onConfigure?.(this._config); } diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index 4184273e1d..8c0adcd5c0 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -30,7 +30,6 @@ import type { ZipFile } from 'playwright-core/lib/zipBundle'; import { yazl } from 'playwright-core/lib/zipBundle'; import { mime } from 'playwright-core/lib/utilsBundle'; import type { HTMLReport, Stats, TestAttachment, TestCase, TestCaseSummary, TestFile, TestFileSummary, TestResult, TestStep } from '@html-reporter/types'; -import { FullConfigInternal } from '../common/config'; import EmptyReporter from './empty'; type TestEntry = { @@ -52,6 +51,7 @@ type HtmlReporterOptions = { host?: string, port?: number, attachmentsBaseURL?: string, + _mode?: string; }; class HtmlReporter extends EmptyReporter { @@ -124,12 +124,11 @@ class HtmlReporter extends EmptyReporter { override async onExit() { if (process.env.CI || !this._buildResult) return; - const { ok, singleTestId } = this._buildResult; const shouldOpen = this._open === 'always' || (!ok && this._open === 'on-failure'); if (shouldOpen) { await showHTMLReport(this._outputFolder, this._options.host, this._options.port, singleTestId); - } else if (!FullConfigInternal.from(this.config)?.cliListOnly) { + } else if (this._options._mode === 'run') { const packageManagerCommand = getPackageManagerExecCommand(); const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder); const hostArg = this._options.host ? ` --host ${this._options.host}` : ''; diff --git a/packages/playwright/src/reporters/merge.ts b/packages/playwright/src/reporters/merge.ts index 9e81f92e7a..096cc66c50 100644 --- a/packages/playwright/src/reporters/merge.ts +++ b/packages/playwright/src/reporters/merge.ts @@ -60,7 +60,7 @@ export async function createMergedReport(config: FullConfigInternal, dir: string for (const event of events) { if (event.method === 'onEnd') printStatus(`building final report`); - await receiver.dispatch(event); + await receiver.dispatch('test', event); if (event.method === 'onEnd') printStatus(`finished building report`); } @@ -248,7 +248,6 @@ function mergeConfigureEvents(configureEvents: JsonEvent[], rootDirOverride: str rootDir: '', version: '', workers: 0, - listOnly: false }; for (const event of configureEvents) config = mergeConfigs(config, event.params.config); diff --git a/packages/playwright/src/reporters/teleEmitter.ts b/packages/playwright/src/reporters/teleEmitter.ts index e6bc49ee3e..44a9908300 100644 --- a/packages/playwright/src/reporters/teleEmitter.ts +++ b/packages/playwright/src/reporters/teleEmitter.ts @@ -17,7 +17,7 @@ import path from 'path'; import { createGuid } from 'playwright-core/lib/utils'; import type { FullConfig, FullResult, Location, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter'; -import { FullConfigInternal, getProjectId } from '../common/config'; +import { getProjectId } from '../common/config'; import type { JsonAttachment, JsonConfig, JsonEvent, JsonFullResult, JsonProject, JsonStdIOType, JsonSuite, JsonTestCase, JsonTestEnd, JsonTestResultEnd, JsonTestResultStart, JsonTestStepEnd, JsonTestStepStart } from '../isomorphic/teleReceiver'; import { serializeRegexPatterns } from '../isomorphic/teleReceiver'; import type { ReporterV2 } from './reporterV2'; @@ -152,7 +152,6 @@ export class TeleReporterEmitter implements ReporterV2 { rootDir: config.rootDir, version: config.version, workers: config.workers, - listOnly: !!FullConfigInternal.from(config)?.cliListOnly, }; } diff --git a/packages/playwright/src/runner/reporters.ts b/packages/playwright/src/runner/reporters.ts index 2285e9bee2..033356e1bd 100644 --- a/packages/playwright/src/runner/reporters.ts +++ b/packages/playwright/src/runner/reporters.ts @@ -50,9 +50,10 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' | descriptions ??= config.config.reporter; if (config.configCLIOverrides.additionalReporters) descriptions = [...descriptions, ...config.configCLIOverrides.additionalReporters]; + const runOptions = { configDir: config.configDir, _mode: mode }; for (const r of descriptions) { const [name, arg] = r; - const options = { ...arg, configDir: config.configDir }; + const options = { ...runOptions, ...arg }; if (name in defaultReporters) { reporters.push(new defaultReporters[name as keyof typeof defaultReporters](options)); } else { @@ -62,7 +63,7 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' | } if (process.env.PW_TEST_REPORTER) { const reporterConstructor = await loadReporter(config, process.env.PW_TEST_REPORTER); - reporters.push(wrapReporterAsV2(new reporterConstructor())); + reporters.push(wrapReporterAsV2(new reporterConstructor(runOptions))); } const someReporterPrintsToStdio = reporters.some(r => r.printsToStdio()); diff --git a/packages/playwright/src/runner/uiMode.ts b/packages/playwright/src/runner/uiMode.ts index d0071e14f1..61a5aaddce 100644 --- a/packages/playwright/src/runner/uiMode.ts +++ b/packages/playwright/src/runner/uiMode.ts @@ -167,7 +167,7 @@ class UIMode { } private async _listTests() { - const reporter = new InternalReporter(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true)); + const reporter = new InternalReporter(new TeleReporterEmitter(e => this._dispatchEvent('listReport', e), true)); this._config.cliListOnly = true; this._config.testIdMatcher = undefined; const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false }); @@ -195,7 +195,7 @@ class UIMode { this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id); const reporters = await createReporters(this._config, 'ui'); - reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true)); + reporters.push(new TeleReporterEmitter(e => this._dispatchEvent('testReport', e), true)); const reporter = new InternalReporter(new Multiplexer(reporters)); const taskRunner = createTaskRunnerForWatch(this._config, reporter); const testRun = new TestRun(this._config, reporter); diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 1becb6645f..89e05f5b48 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -737,10 +737,15 @@ const dispatchEvent = (method: string, params?: any) => { return; } - // The order of receiver dispatches matters here, we want to assign `lastRunTestCount` - // before we use it. - lastRunReceiver?.dispatch({ method, params })?.catch(() => {}); - receiver?.dispatch({ method, params })?.catch(() => {}); + if (method === 'listReport') + receiver?.dispatch('list', params)?.catch(() => {}); + + if (method === 'testReport') { + // The order of receiver dispatches matters here, we want to assign `lastRunTestCount` + // before we use it. + lastRunReceiver?.dispatch('test', params)?.catch(() => {}); + receiver?.dispatch('test', params)?.catch(() => {}); + } }; const outputDirForTestCase = (testCase: TestCase): string | undefined => { diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index f336e916ff..15c17743e4 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -212,7 +212,7 @@ test('should merge into html with dependencies', async ({ runInlineTest, mergeRe const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); expect(exitCode).toBe(0); - expect(output).toContain('To open last HTML report run:'); + expect(output).not.toContain('To open last HTML report run:'); await showReport(); @@ -377,7 +377,7 @@ test('total time is from test run not from merge', async ({ runInlineTest, merge const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); expect(exitCode).toBe(0); - expect(output).toContain('To open last HTML report run:'); + expect(output).not.toContain('To open last HTML report run:'); await showReport(); @@ -1152,7 +1152,7 @@ test('preserve steps in html report', async ({ runInlineTest, mergeReports, show const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'], cwd: mergeCwd }); expect(exitCode).toBe(0); - expect(output).toContain('To open last HTML report run:'); + expect(output).not.toContain('To open last HTML report run:'); await showReport();