diff --git a/packages/playwright-test/src/isomorphic/teleReceiver.ts b/packages/playwright-test/src/isomorphic/teleReceiver.ts index 39c25c09f8..e7ffd6ba53 100644 --- a/packages/playwright-test/src/isomorphic/teleReceiver.ts +++ b/packages/playwright-test/src/isomorphic/teleReceiver.ts @@ -147,8 +147,12 @@ export class TeleReporterReceiver { this._onConfigure(params.config); return; } + if (method === 'onProject') { + this._onProject(params.project); + return; + } if (method === 'onBegin') { - this._onBegin(params.projects); + this._onBegin(); return; } if (method === 'onTestBegin') { @@ -192,35 +196,36 @@ export class TeleReporterReceiver { this._reporter.onConfigure(this._config); } - private _onBegin(projects: JsonProject[]) { - for (const project of projects) { - let projectSuite = this._rootSuite.suites.find(suite => suite.project()!.id === project.id); - if (!projectSuite) { - projectSuite = new TeleSuite(project.name, 'project'); - this._rootSuite.suites.push(projectSuite); - projectSuite.parent = this._rootSuite; - } - const p = this._parseProject(project); - projectSuite.project = () => p; - this._mergeSuitesInto(project.suites, projectSuite); - - // Remove deleted tests when listing. Empty suites will be auto-filtered - // in the UI layer. - if (this._listOnly) { - const testIds = new Set(); - const collectIds = (suite: JsonSuite) => { - suite.tests.map(t => t.testId).forEach(testId => testIds.add(testId)); - suite.suites.forEach(collectIds); - }; - project.suites.forEach(collectIds); - - const filterTests = (suite: TeleSuite) => { - suite.tests = suite.tests.filter(t => testIds.has(t.id)); - suite.suites.forEach(filterTests); - }; - filterTests(projectSuite); - } + private _onProject(project: JsonProject) { + let projectSuite = this._rootSuite.suites.find(suite => suite.project()!.id === project.id); + if (!projectSuite) { + projectSuite = new TeleSuite(project.name, 'project'); + this._rootSuite.suites.push(projectSuite); + projectSuite.parent = this._rootSuite; } + const p = this._parseProject(project); + projectSuite.project = () => p; + this._mergeSuitesInto(project.suites, projectSuite); + + // Remove deleted tests when listing. Empty suites will be auto-filtered + // in the UI layer. + if (this._listOnly) { + const testIds = new Set(); + const collectIds = (suite: JsonSuite) => { + suite.tests.map(t => t.testId).forEach(testId => testIds.add(testId)); + suite.suites.forEach(collectIds); + }; + project.suites.forEach(collectIds); + + const filterTests = (suite: TeleSuite) => { + suite.tests = suite.tests.filter(t => testIds.has(t.id)); + suite.suites.forEach(filterTests); + }; + filterTests(projectSuite); + } + } + + private _onBegin() { this._reporter.onBegin?.(this._rootSuite); } diff --git a/packages/playwright-test/src/reporters/merge.ts b/packages/playwright-test/src/reporters/merge.ts index aa8bd1881a..25c48ebe87 100644 --- a/packages/playwright-test/src/reporters/merge.ts +++ b/packages/playwright-test/src/reporters/merge.ts @@ -121,7 +121,7 @@ async function mergeEvents(dir: string, shardReportFiles: string[], printStatus: const stringPool = new StringInternPool(); const events: JsonEvent[] = []; const configureEvents: JsonEvent[] = []; - const beginEvents: JsonEvent[] = []; + const projectEvents: JsonEvent[] = []; const endEvents: JsonEvent[] = []; const blobs = await extractAndParseReports(dir, shardReportFiles, stringPool, printStatus); @@ -158,15 +158,22 @@ async function mergeEvents(dir: string, shardReportFiles: string[], printStatus: for (const event of parsedEvents) { if (event.method === 'onConfigure') configureEvents.push(event); - else if (event.method === 'onBegin') - beginEvents.push(event); + else if (event.method === 'onProject') + projectEvents.push(event); else if (event.method === 'onEnd') endEvents.push(event); - else if (event.method !== 'onBlobReportMetadata') + else if (event.method !== 'onBlobReportMetadata' && event.method !== 'onBegin') events.push(event); } } - return [mergeConfigureEvents(configureEvents), mergeBeginEvents(beginEvents), ...events, mergeEndEvents(endEvents), { method: 'onExit', params: undefined }]; + return [ + mergeConfigureEvents(configureEvents), + ...projectEvents, + { method: 'onBegin', params: undefined }, + ...events, + mergeEndEvents(endEvents), + { method: 'onExit', params: undefined }, + ]; } function mergeConfigureEvents(configureEvents: JsonEvent[]): JsonEvent { @@ -194,28 +201,6 @@ function mergeConfigureEvents(configureEvents: JsonEvent[]): JsonEvent { }; } -function mergeBeginEvents(beginEvents: JsonEvent[]): JsonEvent { - if (!beginEvents.length) - throw new Error('No begin events found'); - const projects: JsonProject[] = []; - for (const event of beginEvents) { - const shardProjects: JsonProject[] = event.params.projects; - for (const shardProject of shardProjects) { - const mergedProject = projects.find(p => p.id === shardProject.id); - if (!mergedProject) - projects.push(shardProject); - else - mergeJsonSuites(shardProject.suites, mergedProject); - } - } - return { - method: 'onBegin', - params: { - projects, - } - }; -} - function mergeConfigs(to: JsonConfig, from: JsonConfig): JsonConfig { return { ...to, @@ -230,18 +215,6 @@ function mergeConfigs(to: JsonConfig, from: JsonConfig): JsonConfig { }; } -function mergeJsonSuites(jsonSuites: JsonSuite[], parent: JsonSuite | JsonProject) { - for (const jsonSuite of jsonSuites) { - const existingSuite = parent.suites.find(s => s.title === jsonSuite.title); - if (!existingSuite) { - parent.suites.push(jsonSuite); - } else { - mergeJsonSuites(jsonSuite.suites, existingSuite); - existingSuite.tests.push(...jsonSuite.tests); - } - } -} - function mergeEndEvents(endEvents: JsonEvent[]): JsonEvent { const result: FullResult = { status: 'passed' }; for (const event of endEvents) { @@ -278,8 +251,8 @@ class IdsPatcher { for (const event of events) { const { method, params } = event; switch (method) { - case 'onBegin': - this._onBegin(params.config, params.projects); + case 'onProject': + this._onProject(params.project); continue; case 'onTestBegin': case 'onStepBegin': @@ -294,22 +267,11 @@ class IdsPatcher { } } - private _onBegin(config: JsonConfig, projects: JsonProject[]) { - const usedNames = new Set(); - for (const project of projects) { - project.metadata = project.metadata ?? {}; - project.metadata.reportName = this._reportName; - for (let i = 0; i < projects.length; ++i) { - const candidate = (project.name + this._salt) + (i ? i : ''); - if (usedNames.has(candidate)) - continue; - project.id = candidate; - usedNames.add(candidate); - break; - } - } - for (const project of projects) - project.suites.forEach(suite => this._updateTestIds(suite)); + private _onProject(project: JsonProject) { + project.metadata = project.metadata ?? {}; + project.metadata.reportName = this._reportName; + project.id = this._stringPool.internString(project.id + this._salt); + project.suites.forEach(suite => this._updateTestIds(suite)); } private _updateTestIds(suite: JsonSuite) { diff --git a/packages/playwright-test/src/reporters/teleEmitter.ts b/packages/playwright-test/src/reporters/teleEmitter.ts index ed91644679..6a38f0d7f8 100644 --- a/packages/playwright-test/src/reporters/teleEmitter.ts +++ b/packages/playwright-test/src/reporters/teleEmitter.ts @@ -45,7 +45,9 @@ export class TeleReporterEmitter implements ReporterV2 { onBegin(suite: Suite) { const projects = suite.suites.map(projectSuite => this._serializeProject(projectSuite)); - this._messageSink({ method: 'onBegin', params: { projects } }); + for (const project of projects) + this._messageSink({ method: 'onProject', params: { project } }); + this._messageSink({ method: 'onBegin', params: undefined }); } onTestBegin(test: TestCase, result: TestResult): void {