mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: call onEnd(result)
on InternalReporter (#23972)
Drive-by: fix watch mode not running global teardown.
This commit is contained in:
parent
a07626b804
commit
e28312ba63
@ -22,7 +22,6 @@ import { Suite } from '../common/test';
|
|||||||
import type { FullConfigInternal } from '../common/config';
|
import type { FullConfigInternal } from '../common/config';
|
||||||
import { Multiplexer } from './multiplexer';
|
import { Multiplexer } from './multiplexer';
|
||||||
import { prepareErrorStack, relativeFilePath } from './base';
|
import { prepareErrorStack, relativeFilePath } from './base';
|
||||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
|
||||||
|
|
||||||
type StdIOChunk = {
|
type StdIOChunk = {
|
||||||
chunk: string | Buffer;
|
chunk: string | Buffer;
|
||||||
@ -34,7 +33,6 @@ export class InternalReporter {
|
|||||||
private _multiplexer: Multiplexer;
|
private _multiplexer: Multiplexer;
|
||||||
private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = [];
|
private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = [];
|
||||||
private _config!: FullConfigInternal;
|
private _config!: FullConfigInternal;
|
||||||
private _montonicStartTime: number = 0;
|
|
||||||
|
|
||||||
constructor(reporters: Reporter[]) {
|
constructor(reporters: Reporter[]) {
|
||||||
this._multiplexer = new Multiplexer(reporters);
|
this._multiplexer = new Multiplexer(reporters);
|
||||||
@ -45,7 +43,6 @@ export class InternalReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
onBegin(config: FullConfig, suite: Suite) {
|
||||||
this._montonicStartTime = monotonicTime();
|
|
||||||
this._multiplexer.onBegin(config, suite);
|
this._multiplexer.onBegin(config, suite);
|
||||||
|
|
||||||
const deferred = this._deferred!;
|
const deferred = this._deferred!;
|
||||||
@ -86,16 +83,15 @@ export class InternalReporter {
|
|||||||
this._multiplexer.onTestEnd(test, result);
|
this._multiplexer.onTestEnd(test, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnd() {
|
async onEnd(result: FullResult) {
|
||||||
this._config.config.metadata.totalTime = monotonicTime() - this._montonicStartTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onExit(result: FullResult) {
|
|
||||||
if (this._deferred) {
|
if (this._deferred) {
|
||||||
// onBegin was not reported, emit it.
|
// onBegin was not reported, emit it.
|
||||||
this.onBegin(this._config.config, new Suite('', 'root'));
|
this.onBegin(this._config.config, new Suite('', 'root'));
|
||||||
}
|
}
|
||||||
await this._multiplexer.onEnd(result);
|
await this._multiplexer.onEnd(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onExit() {
|
||||||
await this._multiplexer.onExit();
|
await this._multiplexer.onExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,8 @@ export class Runner {
|
|||||||
status = 'failed';
|
status = 'failed';
|
||||||
if (status === 'passed' && taskStatus !== 'passed')
|
if (status === 'passed' && taskStatus !== 'passed')
|
||||||
status = taskStatus;
|
status = taskStatus;
|
||||||
await reporter.onExit({ status });
|
await reporter.onEnd({ status });
|
||||||
|
await reporter.onExit();
|
||||||
|
|
||||||
// Calling process.exit() might truncate large stdout/stderr output.
|
// Calling process.exit() might truncate large stdout/stderr output.
|
||||||
// See https://github.com/nodejs/node/issues/6456.
|
// See https://github.com/nodejs/node/issues/6456.
|
||||||
|
@ -29,6 +29,7 @@ import { collectProjectsAndTestFiles, createRootSuite, loadFileSuites, loadGloba
|
|||||||
import type { Matcher } from '../util';
|
import type { Matcher } from '../util';
|
||||||
import type { Suite } from '../common/test';
|
import type { Suite } from '../common/test';
|
||||||
import { buildDependentProjects, buildTeardownToSetupsMap } from './projectUtils';
|
import { buildDependentProjects, buildTeardownToSetupsMap } from './projectUtils';
|
||||||
|
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
const removeFolderAsync = promisify(rimraf);
|
const removeFolderAsync = promisify(rimraf);
|
||||||
const readDirAsync = promisify(fs.readdir);
|
const readDirAsync = promisify(fs.readdir);
|
||||||
@ -91,10 +92,7 @@ function addGlobalSetupTasks(taskRunner: TaskRunner<TestRun>, config: FullConfig
|
|||||||
|
|
||||||
function addRunTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) {
|
function addRunTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) {
|
||||||
taskRunner.addTask('create phases', createPhasesTask());
|
taskRunner.addTask('create phases', createPhasesTask());
|
||||||
taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => {
|
taskRunner.addTask('report begin', createReportBeginTask());
|
||||||
reporter.onBegin(config.config, rootSuite!);
|
|
||||||
return () => reporter.onEnd();
|
|
||||||
});
|
|
||||||
for (const plugin of config.plugins)
|
for (const plugin of config.plugins)
|
||||||
taskRunner.addTask('plugin begin', createPluginBeginTask(plugin));
|
taskRunner.addTask('plugin begin', createPluginBeginTask(plugin));
|
||||||
taskRunner.addTask('start workers', createWorkersTask());
|
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> {
|
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);
|
const taskRunner = new TaskRunner<TestRun>(reporter, config.config.globalTimeout);
|
||||||
taskRunner.addTask('load tests', createLoadTask(mode, { ...options, filterOnly: false }));
|
taskRunner.addTask('load tests', createLoadTask(mode, { ...options, filterOnly: false }));
|
||||||
taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => {
|
taskRunner.addTask('report begin', createReportBeginTask());
|
||||||
reporter.onBegin(config.config, rootSuite!);
|
|
||||||
return () => reporter.onEnd();
|
|
||||||
});
|
|
||||||
return taskRunner;
|
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> {
|
function createPluginSetupTask(plugin: TestRunnerPluginRegistration): Task<TestRun> {
|
||||||
return async ({ config, reporter }) => {
|
return async ({ config, reporter }) => {
|
||||||
if (typeof plugin.factory === 'function')
|
if (typeof plugin.factory === 'function')
|
||||||
|
@ -72,7 +72,8 @@ class UIMode {
|
|||||||
reporter.onConfigure(this._config);
|
reporter.onConfigure(this._config);
|
||||||
const testRun = new TestRun(this._config, reporter);
|
const testRun = new TestRun(this._config, reporter);
|
||||||
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
||||||
await reporter.onExit({ status });
|
await reporter.onEnd({ status });
|
||||||
|
await reporter.onExit();
|
||||||
if (status !== 'passed') {
|
if (status !== 'passed') {
|
||||||
await globalCleanup();
|
await globalCleanup();
|
||||||
return status;
|
return status;
|
||||||
@ -168,7 +169,8 @@ class UIMode {
|
|||||||
clearCompilationCache();
|
clearCompilationCache();
|
||||||
reporter.onConfigure(this._config);
|
reporter.onConfigure(this._config);
|
||||||
const status = await taskRunner.run(testRun, 0);
|
const status = await taskRunner.run(testRun, 0);
|
||||||
await reporter.onExit({ status });
|
await reporter.onEnd({ status });
|
||||||
|
await reporter.onExit();
|
||||||
|
|
||||||
const projectDirs = new Set<string>();
|
const projectDirs = new Set<string>();
|
||||||
for (const p of this._config.projects)
|
for (const p of this._config.projects)
|
||||||
@ -192,7 +194,8 @@ class UIMode {
|
|||||||
reporter.onConfigure(this._config);
|
reporter.onConfigure(this._config);
|
||||||
const stop = new ManualPromise();
|
const stop = new ManualPromise();
|
||||||
const run = taskRunner.run(testRun, 0, stop).then(async status => {
|
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._testRun = undefined;
|
||||||
this._config.testIdMatcher = undefined;
|
this._config.testIdMatcher = undefined;
|
||||||
return status;
|
return status;
|
||||||
|
@ -118,7 +118,11 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise<Full
|
|||||||
reporter.onConfigure(config);
|
reporter.onConfigure(config);
|
||||||
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
||||||
if (status !== 'passed')
|
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.
|
// Prepare projects that will be watched, set up watcher.
|
||||||
const failedTestIdCollector = new Set<string>();
|
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) {
|
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';
|
status = 'failed';
|
||||||
if (status === 'passed' && taskStatus !== 'passed')
|
if (status === 'passed' && taskStatus !== 'passed')
|
||||||
status = taskStatus;
|
status = taskStatus;
|
||||||
await reporter.onExit({ status });
|
await reporter.onEnd({ status });
|
||||||
|
await reporter.onExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function affectedProjectsClosure(projectClosure: FullProjectInternal[], affected: FullProjectInternal[]): Set<FullProjectInternal> {
|
function affectedProjectsClosure(projectClosure: FullProjectInternal[], affected: FullProjectInternal[]): Set<FullProjectInternal> {
|
||||||
|
@ -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`);
|
expect(testProcess.output).not.toContain(`src${path.sep}link.spec.tsx`);
|
||||||
await testProcess.waitForOutput('Waiting for file changes.');
|
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');
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user