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.
|
// 13. Run Global setup.
|
||||||
const globalTearDown = await this._performGlobalSetup(config, rootSuite);
|
|
||||||
if (!globalTearDown)
|
|
||||||
return { status: 'failed' };
|
|
||||||
|
|
||||||
const result: FullResult = { status: 'passed' };
|
const result: FullResult = { status: 'passed' };
|
||||||
|
const globalTearDown = await this._performGlobalSetup(config, rootSuite, result);
|
||||||
|
if (result.status !== 'passed')
|
||||||
|
return result;
|
||||||
|
|
||||||
// 14. Run tests.
|
// 14. Run tests.
|
||||||
try {
|
try {
|
||||||
|
|
@ -433,9 +432,10 @@ export class Runner {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _performGlobalSetup(config: FullConfigInternal, rootSuite: Suite): Promise<(() => Promise<void>) | undefined> {
|
private async _performGlobalSetup(config: FullConfigInternal, rootSuite: Suite, result: FullResult): Promise<(() => Promise<void>) | undefined> {
|
||||||
const result: FullResult = { status: 'passed' };
|
|
||||||
let globalSetupResult: any;
|
let globalSetupResult: any;
|
||||||
|
const pluginsThatWereSetUp: TestRunnerPlugin[] = [];
|
||||||
|
const sigintWatcher = new SigIntWatcher();
|
||||||
|
|
||||||
const tearDown = async () => {
|
const tearDown = async () => {
|
||||||
// Reverse to setup.
|
// Reverse to setup.
|
||||||
|
|
@ -445,34 +445,49 @@ export class Runner {
|
||||||
}, result);
|
}, result);
|
||||||
|
|
||||||
await this._runAndReportError(async () => {
|
await this._runAndReportError(async () => {
|
||||||
if (config.globalTeardown)
|
if (globalSetupResult && config.globalTeardown)
|
||||||
await (await this._loader.loadGlobalHook(config.globalTeardown, 'globalTeardown'))(this._loader.fullConfig());
|
await (await this._loader.loadGlobalHook(config.globalTeardown, 'globalTeardown'))(this._loader.fullConfig());
|
||||||
}, result);
|
}, result);
|
||||||
|
|
||||||
for (const plugin of [...this._plugins].reverse()) {
|
for (const plugin of pluginsThatWereSetUp.reverse()) {
|
||||||
await this._runAndReportError(async () => {
|
await this._runAndReportError(async () => {
|
||||||
await plugin.teardown?.();
|
await plugin.teardown?.();
|
||||||
}, result);
|
}, result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await this._runAndReportError(async () => {
|
// Legacy webServer support.
|
||||||
// Legacy webServer support.
|
if (config.webServer)
|
||||||
if (config.webServer)
|
this._plugins.push(webServerPluginForConfig(config, this._reporter));
|
||||||
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
|
// First run the plugins, if plugin is a web server we want it to run before the
|
||||||
// config's global setup.
|
// config's global setup.
|
||||||
for (const plugin of this._plugins)
|
for (const plugin of this._plugins) {
|
||||||
await plugin.setup?.(config, config._configDir, rootSuite);
|
await Promise.race([
|
||||||
|
plugin.setup?.(config, config._configDir, rootSuite),
|
||||||
|
sigintWatcher.promise(),
|
||||||
|
]);
|
||||||
|
if (sigintWatcher.hadSignal())
|
||||||
|
break;
|
||||||
|
pluginsThatWereSetUp.push(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
// The do global setup.
|
// The do global setup.
|
||||||
if (config.globalSetup)
|
if (!sigintWatcher.hadSignal() && config.globalSetup) {
|
||||||
globalSetupResult = await (await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup'))(this._loader.fullConfig());
|
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);
|
}, result);
|
||||||
|
|
||||||
if (result.status !== 'passed') {
|
sigintWatcher.disarm();
|
||||||
|
|
||||||
|
if (result.status !== 'passed' || sigintWatcher.hadSignal()) {
|
||||||
await tearDown();
|
await tearDown();
|
||||||
|
result.status = sigintWatcher.hadSignal() ? 'interrupted' : 'failed';
|
||||||
return;
|
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');
|
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