chore: call onEnd(result) on InternalReporter (#23972)

Drive-by: fix watch mode not running global teardown.
This commit is contained in:
Dmitry Gozman 2023-06-29 17:03:10 -07:00 committed by GitHub
parent a07626b804
commit e28312ba63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 23 deletions

View File

@ -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();
}

View File

@ -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.

View File

@ -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<TestRun>, config: FullConfig
function addRunTasks(taskRunner: TaskRunner<TestRun>, 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<TestRun>, config: FullConfigInternal
export function createTaskRunnerForList(config: FullConfigInternal, reporter: InternalReporter, mode: 'in-process' | 'out-of-process', options: { failOnLoadErrors: boolean }): TaskRunner<TestRun> {
const taskRunner = new TaskRunner<TestRun>(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<TestRun> {
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<TestRun> {
return async ({ config, reporter }) => {
if (typeof plugin.factory === 'function')

View File

@ -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<string>();
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;

View File

@ -118,7 +118,11 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise<Full
reporter.onConfigure(config);
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
if (status !== 'passed')
return await globalCleanup();
await globalCleanup();
await reporter.onEnd({ status });
await reporter.onExit();
if (status !== 'passed')
return status;
// Prepare projects that will be watched, set up watcher.
const failedTestIdCollector = new Set<string>();
@ -248,7 +252,8 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise<Full
}
}
return result === 'passed' ? await globalCleanup() : result;
const cleanupStatus = await globalCleanup();
return result === 'passed' ? cleanupStatus : result;
}
async function runChangedTests(config: FullConfigInternal, failedTestIdCollector: Set<string>, filesByProject: Map<FullProjectInternal, Set<string>>, title?: string) {
@ -297,7 +302,8 @@ async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<s
status = 'failed';
if (status === 'passed' && taskStatus !== 'passed')
status = taskStatus;
await reporter.onExit({ status });
await reporter.onEnd({ status });
await reporter.onExit();
}
function affectedProjectsClosure(projectClosure: FullProjectInternal[], affected: FullProjectInternal[]): Set<FullProjectInternal> {

View File

@ -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');
});