From e28312ba63b7e4499c0de6f79b86ad6aacab78d6 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 29 Jun 2023 17:03:10 -0700 Subject: [PATCH] chore: call `onEnd(result)` on InternalReporter (#23972) Drive-by: fix watch mode not running global teardown. --- .../src/reporters/internalReporter.ts | 12 ++++------ packages/playwright-test/src/runner/runner.ts | 3 ++- packages/playwright-test/src/runner/tasks.ts | 21 +++++++++------- packages/playwright-test/src/runner/uiMode.ts | 9 ++++--- .../playwright-test/src/runner/watchMode.ts | 12 +++++++--- tests/playwright-test/watch.spec.ts | 24 +++++++++++++++++++ 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/packages/playwright-test/src/reporters/internalReporter.ts b/packages/playwright-test/src/reporters/internalReporter.ts index 330984adae..485e01d05b 100644 --- a/packages/playwright-test/src/reporters/internalReporter.ts +++ b/packages/playwright-test/src/reporters/internalReporter.ts @@ -22,7 +22,6 @@ import { Suite } from '../common/test'; import type { FullConfigInternal } from '../common/config'; import { Multiplexer } from './multiplexer'; import { prepareErrorStack, relativeFilePath } from './base'; -import { monotonicTime } from 'playwright-core/lib/utils'; type StdIOChunk = { chunk: string | Buffer; @@ -34,7 +33,6 @@ export class InternalReporter { private _multiplexer: Multiplexer; private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = []; private _config!: FullConfigInternal; - private _montonicStartTime: number = 0; constructor(reporters: Reporter[]) { this._multiplexer = new Multiplexer(reporters); @@ -45,7 +43,6 @@ export class InternalReporter { } onBegin(config: FullConfig, suite: Suite) { - this._montonicStartTime = monotonicTime(); this._multiplexer.onBegin(config, suite); const deferred = this._deferred!; @@ -86,16 +83,15 @@ export class InternalReporter { this._multiplexer.onTestEnd(test, result); } - async onEnd() { - this._config.config.metadata.totalTime = monotonicTime() - this._montonicStartTime; - } - - async onExit(result: FullResult) { + async onEnd(result: FullResult) { if (this._deferred) { // onBegin was not reported, emit it. this.onBegin(this._config.config, new Suite('', 'root')); } await this._multiplexer.onEnd(result); + } + + async onExit() { await this._multiplexer.onExit(); } diff --git a/packages/playwright-test/src/runner/runner.ts b/packages/playwright-test/src/runner/runner.ts index 3ae448b902..23f6d72b9f 100644 --- a/packages/playwright-test/src/runner/runner.ts +++ b/packages/playwright-test/src/runner/runner.ts @@ -91,7 +91,8 @@ export class Runner { status = 'failed'; if (status === 'passed' && taskStatus !== 'passed') status = taskStatus; - await reporter.onExit({ status }); + await reporter.onEnd({ status }); + await reporter.onExit(); // Calling process.exit() might truncate large stdout/stderr output. // See https://github.com/nodejs/node/issues/6456. diff --git a/packages/playwright-test/src/runner/tasks.ts b/packages/playwright-test/src/runner/tasks.ts index 3739dc96f2..d2ba6286aa 100644 --- a/packages/playwright-test/src/runner/tasks.ts +++ b/packages/playwright-test/src/runner/tasks.ts @@ -29,6 +29,7 @@ import { collectProjectsAndTestFiles, createRootSuite, loadFileSuites, loadGloba import type { Matcher } from '../util'; import type { Suite } from '../common/test'; import { buildDependentProjects, buildTeardownToSetupsMap } from './projectUtils'; +import { monotonicTime } from 'playwright-core/lib/utils'; const removeFolderAsync = promisify(rimraf); const readDirAsync = promisify(fs.readdir); @@ -91,10 +92,7 @@ function addGlobalSetupTasks(taskRunner: TaskRunner, config: FullConfig function addRunTasks(taskRunner: TaskRunner, config: FullConfigInternal) { taskRunner.addTask('create phases', createPhasesTask()); - taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => { - reporter.onBegin(config.config, rootSuite!); - return () => reporter.onEnd(); - }); + taskRunner.addTask('report begin', createReportBeginTask()); for (const plugin of config.plugins) taskRunner.addTask('plugin begin', createPluginBeginTask(plugin)); taskRunner.addTask('start workers', createWorkersTask()); @@ -105,13 +103,20 @@ function addRunTasks(taskRunner: TaskRunner, config: FullConfigInternal export function createTaskRunnerForList(config: FullConfigInternal, reporter: InternalReporter, mode: 'in-process' | 'out-of-process', options: { failOnLoadErrors: boolean }): TaskRunner { const taskRunner = new TaskRunner(reporter, config.config.globalTimeout); taskRunner.addTask('load tests', createLoadTask(mode, { ...options, filterOnly: false })); - taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => { - reporter.onBegin(config.config, rootSuite!); - return () => reporter.onEnd(); - }); + taskRunner.addTask('report begin', createReportBeginTask()); return taskRunner; } +function createReportBeginTask(): Task { + return async ({ config, reporter, rootSuite }) => { + const montonicStartTime = monotonicTime(); + reporter.onBegin(config.config, rootSuite!); + return async () => { + config.config.metadata.totalTime = monotonicTime() - montonicStartTime; + }; + }; +} + function createPluginSetupTask(plugin: TestRunnerPluginRegistration): Task { return async ({ config, reporter }) => { if (typeof plugin.factory === 'function') diff --git a/packages/playwright-test/src/runner/uiMode.ts b/packages/playwright-test/src/runner/uiMode.ts index 88b5648651..834cc2b0ec 100644 --- a/packages/playwright-test/src/runner/uiMode.ts +++ b/packages/playwright-test/src/runner/uiMode.ts @@ -72,7 +72,8 @@ class UIMode { reporter.onConfigure(this._config); const testRun = new TestRun(this._config, reporter); const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0); - await reporter.onExit({ status }); + await reporter.onEnd({ status }); + await reporter.onExit(); if (status !== 'passed') { await globalCleanup(); return status; @@ -168,7 +169,8 @@ class UIMode { clearCompilationCache(); reporter.onConfigure(this._config); const status = await taskRunner.run(testRun, 0); - await reporter.onExit({ status }); + await reporter.onEnd({ status }); + await reporter.onExit(); const projectDirs = new Set(); for (const p of this._config.projects) @@ -192,7 +194,8 @@ class UIMode { reporter.onConfigure(this._config); const stop = new ManualPromise(); const run = taskRunner.run(testRun, 0, stop).then(async status => { - await reporter.onExit({ status }); + await reporter.onEnd({ status }); + await reporter.onExit(); this._testRun = undefined; this._config.testIdMatcher = undefined; return status; diff --git a/packages/playwright-test/src/runner/watchMode.ts b/packages/playwright-test/src/runner/watchMode.ts index 1bb79174d2..11d633019e 100644 --- a/packages/playwright-test/src/runner/watchMode.ts +++ b/packages/playwright-test/src/runner/watchMode.ts @@ -118,7 +118,11 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise(); @@ -248,7 +252,8 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise, filesByProject: Map>, title?: string) { @@ -297,7 +302,8 @@ async function runTests(config: FullConfigInternal, failedTestIdCollector: Set { diff --git a/tests/playwright-test/watch.spec.ts b/tests/playwright-test/watch.spec.ts index 6d717a8075..0db5abe4e7 100644 --- a/tests/playwright-test/watch.spec.ts +++ b/tests/playwright-test/watch.spec.ts @@ -696,3 +696,27 @@ test('should run CT on indirect deps change ESM mode', async ({ runWatchTest, wr expect(testProcess.output).not.toContain(`src${path.sep}link.spec.tsx`); await testProcess.waitForOutput('Waiting for file changes.'); }); + +test('should run global teardown before exiting', async ({ runWatchTest }) => { + const testProcess = await runWatchTest({ + 'playwright.config.ts': ` + export default { + globalTeardown: './global-teardown.ts', + }; + `, + 'global-teardown.ts': ` + export default async function() { + console.log('running teardown'); + }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', async () => { + }); + `, + }, {}); + await testProcess.waitForOutput('a.test.ts:3:11 › passes'); + await testProcess.waitForOutput('Waiting for file changes.'); + testProcess.write('\x1B'); + await testProcess.waitForOutput('running teardown'); +});