diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index c2d29d0eb4..12a7c91b2b 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -668,6 +668,7 @@ export default defineConfig({ - `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. + - `name` ?<[string]> Specifies a custom name for the web server. This name will be prefixed to log messages. Defaults to `[WebServer]`. Launch a development web server (or multiple) during the tests. @@ -720,12 +721,14 @@ export default defineConfig({ { command: 'npm run start', url: 'http://localhost:3000', + name: 'Frontend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, }, { command: 'npm run backend', url: 'http://localhost:3333', + name: 'Backend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, } diff --git a/docs/src/test-webserver-js.md b/docs/src/test-webserver-js.md index d4a7bbb19b..ed0c51875c 100644 --- a/docs/src/test-webserver-js.md +++ b/docs/src/test-webserver-js.md @@ -38,6 +38,7 @@ export default defineConfig({ | `stderr` | Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. | | `timeout` | How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. | | `gracefulShutdown` | How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting down a Docker container requires `SIGTERM`. | +| `name` | Specifies a custom name for the web server. This name will be prefixed to log messages. Defaults to `[WebServer]`. | ## Adding a server timeout @@ -107,12 +108,14 @@ export default defineConfig({ { command: 'npm run start', url: 'http://localhost:3000', + name: 'Frontend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, }, { command: 'npm run backend', url: 'http://localhost:3333', + name: 'Backend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, } diff --git a/packages/playwright/src/plugins/webServerPlugin.ts b/packages/playwright/src/plugins/webServerPlugin.ts index 24d9e4d3fd..afd067c855 100644 --- a/packages/playwright/src/plugins/webServerPlugin.ts +++ b/packages/playwright/src/plugins/webServerPlugin.ts @@ -37,6 +37,7 @@ export type WebServerPluginOptions = { env?: { [key: string]: string; }; stdout?: 'pipe' | 'ignore'; stderr?: 'pipe' | 'ignore'; + name?: string; }; const DEFAULT_ENVIRONMENT_VARIABLES = { @@ -136,11 +137,11 @@ export class WebServerPlugin implements TestRunnerPlugin { launchedProcess.stderr!.on('data', data => { if (debugWebServer.enabled || (this._options.stderr === 'pipe' || !this._options.stderr)) - this._reporter!.onStdErr?.(prefixOutputLines(data.toString())); + this._reporter!.onStdErr?.(prefixOutputLines(data.toString(), this._options.name)); }); launchedProcess.stdout!.on('data', data => { if (debugWebServer.enabled || this._options.stdout === 'pipe') - this._reporter!.onStdOut?.(prefixOutputLines(data.toString())); + this._reporter!.onStdOut?.(prefixOutputLines(data.toString(), this._options.name)); }); } @@ -223,12 +224,12 @@ export const webServerPluginsForConfig = (config: FullConfigInternal): TestRunne return webServerPlugins; }; -function prefixOutputLines(output: string) { +function prefixOutputLines(output: string, prefixName: string = 'WebServer'): string { const lastIsNewLine = output[output.length - 1] === '\n'; let lines = output.split('\n'); if (lastIsNewLine) lines.pop(); - lines = lines.map(line => colors.dim('[WebServer] ') + line); + lines = lines.map(line => colors.dim(`[${prefixName}] `) + line); if (lastIsNewLine) lines.push(''); return lines.join('\n'); diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 32f3494955..ce639a1cd8 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -957,12 +957,14 @@ interface TestConfig { * { * command: 'npm run start', * url: 'http://localhost:3000', + * name: 'Frontend', * timeout: 120 * 1000, * reuseExistingServer: !process.env.CI, * }, * { * command: 'npm run backend', * url: 'http://localhost:3333', + * name: 'Backend', * timeout: 120 * 1000, * reuseExistingServer: !process.env.CI, * } @@ -9973,5 +9975,10 @@ interface TestConfigWebServer { * checked. Either `port` or `url` should be specified. */ url?: string; + + /** + * Specifies a custom name for the web server. This name will be prefixed to log messages. Defaults to `[WebServer]`. + */ + name?: string; } diff --git a/tests/playwright-test/web-server.spec.ts b/tests/playwright-test/web-server.spec.ts index b50203048e..9260012105 100644 --- a/tests/playwright-test/web-server.spec.ts +++ b/tests/playwright-test/web-server.spec.ts @@ -808,3 +808,59 @@ test.describe('gracefulShutdown option', () => { expect(parseOutputLines(result).sort()).toEqual(['childprocess received SIGINT', 'webserver received SIGINT but stubbornly refuses to wind down']); }); }); + +test.describe('name option', () => { + test('should use custom prefix', async ({ runInlineTest }, { workerIndex }) => { + const port = workerIndex * 2 + 10500; + const name1 = 'CustomName1'; + const name2 = 'CustomName2'; + const defaultPrefix = 'WebServer'; + const result = await runInlineTest({ + 'test.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => {}); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: [ + { + command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port}', + port: ${port}, + name: '${name1}', + }, + { + command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port + 1}', + port: ${port + 1}, + name: '${name2}', + } + ], + }; + `, + }, undefined); + expect(result.exitCode).toBe(0); + expect(result.output).toContain(`[${name1}]`); + expect(result.output).toContain(`[${name2}]`); + expect(result.output).not.toContain(`[${defaultPrefix}]`); + }); + + test('should use default prefix when name option is not set', async ({ runInlineTest }, { workerIndex }) => { + const port = workerIndex * 2 + 10500; + const defaultPrefix = 'WebServer'; + const result = await runInlineTest({ + 'test.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => {}); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: { + command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port}', + port: ${port}, + }, + }; + `, + }, undefined); + expect(result.exitCode).toBe(0); + expect(result.output).toContain(`[${defaultPrefix}]`); + }); +});