chore: respect sigint in global setup (#14805)
This commit is contained in:
parent
c7b3f4646f
commit
41529bb1a5
|
|
@ -397,11 +397,10 @@ export class Runner {
|
|||
}
|
||||
|
||||
// 13. Run Global setup.
|
||||
const globalTearDown = await this._performGlobalSetup(config, rootSuite);
|
||||
if (!globalTearDown)
|
||||
return { status: 'failed' };
|
||||
|
||||
const result: FullResult = { status: 'passed' };
|
||||
const globalTearDown = await this._performGlobalSetup(config, rootSuite, result);
|
||||
if (result.status !== 'passed')
|
||||
return result;
|
||||
|
||||
// 14. Run tests.
|
||||
try {
|
||||
|
|
@ -433,9 +432,10 @@ export class Runner {
|
|||
return result;
|
||||
}
|
||||
|
||||
private async _performGlobalSetup(config: FullConfigInternal, rootSuite: Suite): Promise<(() => Promise<void>) | undefined> {
|
||||
const result: FullResult = { status: 'passed' };
|
||||
private async _performGlobalSetup(config: FullConfigInternal, rootSuite: Suite, result: FullResult): Promise<(() => Promise<void>) | undefined> {
|
||||
let globalSetupResult: any;
|
||||
const pluginsThatWereSetUp: TestRunnerPlugin[] = [];
|
||||
const sigintWatcher = new SigIntWatcher();
|
||||
|
||||
const tearDown = async () => {
|
||||
// Reverse to setup.
|
||||
|
|
@ -445,34 +445,49 @@ export class Runner {
|
|||
}, result);
|
||||
|
||||
await this._runAndReportError(async () => {
|
||||
if (config.globalTeardown)
|
||||
if (globalSetupResult && config.globalTeardown)
|
||||
await (await this._loader.loadGlobalHook(config.globalTeardown, 'globalTeardown'))(this._loader.fullConfig());
|
||||
}, result);
|
||||
|
||||
for (const plugin of [...this._plugins].reverse()) {
|
||||
for (const plugin of pluginsThatWereSetUp.reverse()) {
|
||||
await this._runAndReportError(async () => {
|
||||
await plugin.teardown?.();
|
||||
}, result);
|
||||
}
|
||||
};
|
||||
|
||||
await this._runAndReportError(async () => {
|
||||
// Legacy webServer support.
|
||||
if (config.webServer)
|
||||
this._plugins.push(webServerPluginForConfig(config, this._reporter));
|
||||
// Legacy webServer support.
|
||||
if (config.webServer)
|
||||
this._plugins.push(webServerPluginForConfig(config, this._reporter));
|
||||
|
||||
await this._runAndReportError(async () => {
|
||||
// First run the plugins, if plugin is a web server we want it to run before the
|
||||
// config's global setup.
|
||||
for (const plugin of this._plugins)
|
||||
await plugin.setup?.(config, config._configDir, rootSuite);
|
||||
for (const plugin of this._plugins) {
|
||||
await Promise.race([
|
||||
plugin.setup?.(config, config._configDir, rootSuite),
|
||||
sigintWatcher.promise(),
|
||||
]);
|
||||
if (sigintWatcher.hadSignal())
|
||||
break;
|
||||
pluginsThatWereSetUp.push(plugin);
|
||||
}
|
||||
|
||||
// The do global setup.
|
||||
if (config.globalSetup)
|
||||
globalSetupResult = await (await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup'))(this._loader.fullConfig());
|
||||
if (!sigintWatcher.hadSignal() && config.globalSetup) {
|
||||
const hook = await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup');
|
||||
await Promise.race([
|
||||
Promise.resolve().then(() => hook(this._loader.fullConfig())).then((r: any) => globalSetupResult = r || '<noop>'),
|
||||
sigintWatcher.promise(),
|
||||
]);
|
||||
}
|
||||
}, result);
|
||||
|
||||
if (result.status !== 'passed') {
|
||||
sigintWatcher.disarm();
|
||||
|
||||
if (result.status !== 'passed' || sigintWatcher.hadSignal()) {
|
||||
await tearDown();
|
||||
result.status = sigintWatcher.hadSignal() ? 'interrupted' : 'failed';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,3 +306,120 @@ test('should not hang if test suites in worker are inconsistent with runner', as
|
|||
expect(result.report.suites[0].specs[1].tests[0].results[0].error.message).toBe('Unknown test(s) in worker:\nproject-name > a.spec.js > Test 1 - bar\nproject-name > a.spec.js > Test 2 - baz');
|
||||
});
|
||||
|
||||
test('sigint should stop global setup', async ({ runInlineTest }) => {
|
||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
globalSetup: './globalSetup',
|
||||
globalTeardown: './globalTeardown.ts',
|
||||
};
|
||||
`,
|
||||
'globalSetup.ts': `
|
||||
module.exports = () => {
|
||||
console.log('Global setup');
|
||||
console.log('%%SEND-SIGINT%%');
|
||||
return new Promise(f => setTimeout(f, 30000));
|
||||
};
|
||||
`,
|
||||
'globalTeardown.ts': `
|
||||
module.exports = () => {
|
||||
console.log('Global teardown');
|
||||
};
|
||||
`,
|
||||
'a.spec.js': `
|
||||
const { test } = pwt;
|
||||
test('test', async () => { });
|
||||
`,
|
||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.passed).toBe(0);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Global setup');
|
||||
expect(output).not.toContain('Global teardown');
|
||||
});
|
||||
|
||||
test('sigint should stop plugins', async ({ runInlineTest }) => {
|
||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
require('@playwright/test')._addRunnerPlugin(() => ({
|
||||
setup: async () => {
|
||||
console.log('Plugin1 setup');
|
||||
console.log('%%SEND-SIGINT%%');
|
||||
return new Promise(f => setTimeout(f, 30000));
|
||||
},
|
||||
teardown: async () => {
|
||||
console.log('Plugin1 teardown');
|
||||
}
|
||||
}));
|
||||
|
||||
require('@playwright/test')._addRunnerPlugin(() => ({
|
||||
setup: async () => {
|
||||
console.log('Plugin2 setup');
|
||||
},
|
||||
teardown: async () => {
|
||||
console.log('Plugin2 teardown');
|
||||
}
|
||||
}));
|
||||
`,
|
||||
'a.spec.js': `
|
||||
const { test } = pwt;
|
||||
test('test', async () => { });
|
||||
`,
|
||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.passed).toBe(0);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Plugin1 setup');
|
||||
expect(output).not.toContain('Plugin1 teardown');
|
||||
expect(output).not.toContain('Plugin2 setup');
|
||||
expect(output).not.toContain('Plugin2 teardown');
|
||||
});
|
||||
|
||||
test('sigint should stop plugins 2', async ({ runInlineTest }) => {
|
||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
require('@playwright/test')._addRunnerPlugin(() => ({
|
||||
setup: async () => {
|
||||
console.log('Plugin1 setup');
|
||||
},
|
||||
teardown: async () => {
|
||||
console.log('Plugin1 teardown');
|
||||
}
|
||||
}));
|
||||
|
||||
require('@playwright/test')._addRunnerPlugin(() => ({
|
||||
setup: async () => {
|
||||
console.log('Plugin2 setup');
|
||||
console.log('%%SEND-SIGINT%%');
|
||||
return new Promise(f => setTimeout(f, 30000));
|
||||
},
|
||||
teardown: async () => {
|
||||
console.log('Plugin2 teardown');
|
||||
}
|
||||
}));
|
||||
`,
|
||||
'a.spec.js': `
|
||||
const { test } = pwt;
|
||||
test('test', async () => { });
|
||||
`,
|
||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.passed).toBe(0);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Plugin1 setup');
|
||||
expect(output).toContain('Plugin2 setup');
|
||||
expect(output).toContain('Plugin1 teardown');
|
||||
expect(output).not.toContain('Plugin2 teardown');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue