diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index 0f362ead08..f0ea6c3a64 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -285,9 +285,8 @@ program program .command('show-trace [trace...]') .option('-b, --browser ', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium') - .option('-h, --host ', 'Host to serve trace on', 'localhost') - .option('-p, --port ', 'Port to serve trace on', '9322') - .option('--web', 'Open trace viewer as a PWA in a browser tab') + .option('-h, --host ', 'Host to serve trace on; specifying this option opens trace in a browser tab') + .option('-p, --port ', 'Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab') .option('--stdin', 'Accept trace URLs over stdin to update the viewer') .description('show trace viewer') .action(function(traces, options) { @@ -298,7 +297,13 @@ program if (options.browser === 'wk') options.browser = 'webkit'; - const openOptions = { headless: false, host: options.host, port: +options.port, isServer: !!options.stdin, openInBrowser: !!options.web }; + const openOptions = { + headless: false, + host: options.host, + port: +options.port, + isServer: !!options.stdin, + openInBrowser: options.port !== undefined || options.host !== undefined + }; showTraceViewer(traces, options.browser, openOptions).catch(logErrorAndExit); }).addHelpText('afterAll', ` Examples: diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 0d2398835c..3cc13c70a4 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -119,10 +119,18 @@ async function startTraceViewerServer(traceUrls: string[], options?: Options): P params.push('isUnderTest=true'); const { host, port } = options || {}; - const urlPrefix = await server.start({ preferredPort: port, host }); + const url = await server.start({ preferredPort: port, host }); const { app } = options || {}; const searchQuery = params.length ? '?' + params.join('&') : ''; - const url = urlPrefix + `/trace/${app || 'index.html'}${searchQuery}`; + const urlPath = `/trace/${app || 'index.html'}${searchQuery}`; + + server.routePath('/', (_, response) => { + response.statusCode = 301; + response.setHeader('Location', urlPath); + response.end(); + return true; + }); + return { server, url }; } @@ -175,6 +183,8 @@ export async function openTraceViewerApp(traceUrls: string[], browserName: strin async function openTraceInBrowser(traceUrls: string[], options?: Options) { const { url } = await startTraceViewerServer(traceUrls, options); // eslint-disable-next-line no-console + console.log('\nListening on ' + url); + // eslint-disable-next-line no-console await open(url, { wait: true }).catch(() => console.log(`Failed to open browser on ${url}`)); } diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 38914c17b9..b03f369d96 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -139,8 +139,8 @@ async function runTests(args: string[], opts: { [key: string]: any }) { const runner = new Runner(config); let status: FullResult['status']; - if (opts.ui || opts.uiWeb) - status = await runner.uiAllTests(!!opts.uiWeb); + if (opts.ui || opts.uiHost || opts.uiPort) + status = await runner.uiAllTests({ host: opts.uiHost, port: opts.uiPort ? +opts.uiPort : undefined }); else if (process.env.PWTEST_WATCH) status = await runner.watchAllTests(); else @@ -328,7 +328,8 @@ const testOptions: [string, string][] = [ ['--timeout ', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})`], ['--trace ', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`], ['--ui', `Run tests in interactive UI mode`], - ['--ui-web', `Open interactive UI mode in a browser tab`], + ['--ui-host ', 'Host to serve UI on; specifying this option opens UI in a browser tab'], + ['--ui-port ', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'], ['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`], ['-j, --workers ', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`], ['-x', `Stop after the first failure`], diff --git a/packages/playwright-test/src/runner/runner.ts b/packages/playwright-test/src/runner/runner.ts index 15caf4d59f..3ae448b902 100644 --- a/packages/playwright-test/src/runner/runner.ts +++ b/packages/playwright-test/src/runner/runner.ts @@ -107,9 +107,9 @@ export class Runner { return await runWatchModeLoop(config); } - async uiAllTests(openInBrowser: boolean): Promise { + async uiAllTests(options: { host?: string, port?: number }): Promise { const config = this._config; webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); - return await runUIMode(config, openInBrowser); + return await runUIMode(config, options); } } diff --git a/packages/playwright-test/src/runner/uiMode.ts b/packages/playwright-test/src/runner/uiMode.ts index d034ec4e8c..a689ada37b 100644 --- a/packages/playwright-test/src/runner/uiMode.ts +++ b/packages/playwright-test/src/runner/uiMode.ts @@ -81,7 +81,7 @@ class UIMode { return status; } - async showUI(openInBrowser: boolean) { + async showUI(options: { host?: string, port?: number }) { const exitPromise = new ManualPromise(); let queue = Promise.resolve(); @@ -120,7 +120,9 @@ class UIMode { app: 'uiMode.html', headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1', transport: this._transport, - openInBrowser, + host: options.host, + port: options.port, + openInBrowser: options.host !== undefined || options.port !== undefined, }); if (!process.env.PWTEST_DEBUG) { @@ -210,12 +212,12 @@ class UIMode { } } -export async function runUIMode(config: FullConfigInternal, openInBrowser: boolean): Promise { +export async function runUIMode(config: FullConfigInternal, options: { host?: string, port?: number }): Promise { const uiMode = new UIMode(config); const status = await uiMode.runGlobalSetup(); if (status !== 'passed') return status; - await uiMode.showUI(openInBrowser); + await uiMode.showUI(options); return await uiMode.globalCleanup?.() || 'passed'; }