diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index e2ded11098..48d809d127 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -629,9 +629,9 @@ export default defineConfig({ - `stdout` ?<["pipe"|"ignore"]> If `"pipe"`, it will pipe the stdout of the command to the process stdout. If `"ignore"`, it will ignore the stdout of the command. Default to `"ignore"`. - `stderr` ?<["pipe"|"ignore"]> Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. - - `kill` ?<[Object]> How to shut down the process gracefully. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ SIGINT: 500 }`, the process group is sent a `SIGINT` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGTERM` instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGINT` and `SIGTERM` signals, so this option is ignored. - - `SIGINT` ?<[int]> - - `SIGTERM` ?<[int]> + - `kill` ?<[Object]> How to shut down the process gracefully. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: "SIGINT", timeout: 500 }`, the process group is sent a `SIGINT` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGTERM` instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGINT` and `SIGTERM` signals, so this option is ignored. + - `signal` <["SIGINT"|"SIGTERM"]> + - `timeout` <[int]> - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. Launch a development web server (or multiple) during the tests. diff --git a/packages/playwright/src/plugins/webServerPlugin.ts b/packages/playwright/src/plugins/webServerPlugin.ts index d18287e296..a4a2943618 100644 --- a/packages/playwright/src/plugins/webServerPlugin.ts +++ b/packages/playwright/src/plugins/webServerPlugin.ts @@ -30,7 +30,7 @@ export type WebServerPluginOptions = { url?: string; ignoreHTTPSErrors?: boolean; timeout?: number; - kill?: { SIGINT?: number, SIGTERM?: number }; + kill?: { signal: 'SIGINT' | 'SIGTERM', timeout?: number }; reuseExistingServer?: boolean; cwd?: string; env?: { [key: string]: string; }; @@ -92,21 +92,6 @@ export class WebServerPlugin implements TestRunnerPlugin { throw new Error(`${this._options.url ?? `http://localhost${port ? ':' + port : ''}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`); } - let signal: 'SIGINT' | 'SIGTERM' | undefined = undefined; - let timeout = 0; - if (this._options.kill) { - if (typeof this._options.kill.SIGINT === 'number') { - signal = 'SIGINT'; - timeout = this._options.kill.SIGINT; - } - if (typeof this._options.kill.SIGTERM === 'number') { - if (signal) - throw new Error('Only one of SIGINT or SIGTERM can be specified in config.webServer.kill'); - signal = 'SIGTERM'; - timeout = this._options.kill.SIGTERM; - } - } - debugWebServer(`Starting WebServer process ${this._options.command}...`); const { launchedProcess, gracefullyClose } = await launchProcess({ command: this._options.command, @@ -121,9 +106,11 @@ export class WebServerPlugin implements TestRunnerPlugin { attemptToGracefullyClose: async () => { if (process.platform === 'win32') throw new Error('Graceful shutdown is not supported on Windows'); - if (!signal) + if (!this._options.kill) throw new Error('skip graceful shutdown'); + const { signal, timeout = 0 } = this._options.kill; + // proper usage of SIGINT is to send it to the entire process group, see https://www.cons.org/cracauer/sigint.html // there's no such convention for SIGTERM, so we decide what we want. signaling the process group for consistency. process.kill(-launchedProcess.pid!, signal); diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index b72fc52246..80c7b7d49c 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -9631,14 +9631,14 @@ interface TestConfigWebServer { /** * How to shut down the process gracefully. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ - * SIGINT: 500 }`, the process group is sent a `SIGINT` signal, followed by `SIGKILL` if it doesn't exit within 500ms. - * You can also use `SIGTERM` instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGINT` - * and `SIGTERM` signals, so this option is ignored. + * signal: "SIGINT", timeout: 500 }`, the process group is sent a `SIGINT` signal, followed by `SIGKILL` if it doesn't + * exit within 500ms. You can also use `SIGTERM` instead. A `0` timeout means no `SIGKILL` will be sent. Windows + * doesn't support `SIGINT` and `SIGTERM` signals, so this option is ignored. */ kill?: { - SIGINT?: number; + signal: "SIGINT"|"SIGTERM"; - SIGTERM?: number; + timeout: number; }; /** diff --git a/tests/playwright-test/web-server.spec.ts b/tests/playwright-test/web-server.spec.ts index 2d2a9ce76e..6474287a87 100644 --- a/tests/playwright-test/web-server.spec.ts +++ b/tests/playwright-test/web-server.spec.ts @@ -799,18 +799,12 @@ test.describe('kill option', () => { }); test('can be configured to send SIGTERM', async ({ runInlineTest }) => { - const result = await runInlineTest(files({ kill: { SIGTERM: 500 } }), { workers: 1 }); + const result = await runInlineTest(files({ kill: { signal: 'SIGTERM', timeout: 500 } }), { workers: 1 }); expect(parseOutputLines(result).sort()).toEqual(['childprocess received SIGTERM', 'webserver received SIGTERM but stubbornly refuses to wind down']); }); test('can be configured to send SIGINT', async ({ runInlineTest }) => { - const result = await runInlineTest(files({ kill: { SIGINT: 500 } }), { workers: 1 }); + const result = await runInlineTest(files({ kill: { signal: 'SIGINT', timeout: 500 } }), { workers: 1 }); expect(parseOutputLines(result).sort()).toEqual(['childprocess received SIGINT', 'webserver received SIGINT but stubbornly refuses to wind down']); }); - - test('throws when mixed', async ({ runInlineTest }) => { - const result = await runInlineTest(files({ kill: { SIGINT: 500, SIGTERM: 500 } }), { workers: 1 }); - expect(result.exitCode).toBe(1); - expect(result.output).toContain('Only one of SIGINT or SIGTERM can be specified in config.webServer.kill'); - }); });