chore: respect sigint in global setup (#14805)

This commit is contained in:
Pavel Feldman 2022-06-12 12:06:00 -08:00 committed by GitHub
parent c7b3f4646f
commit 41529bb1a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 149 additions and 17 deletions

View file

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

View file

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