From 47a71731f4ad44724fb4b7886683515f97c22a79 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 9 Mar 2023 08:04:02 -0800 Subject: [PATCH] chore: add explicit command for gui runner (#21509) --- packages/playwright-test/src/cli.ts | 89 +++++++++++++------ .../playwright-test/src/runner/reporters.ts | 5 +- packages/playwright-test/src/runner/uiMode.ts | 5 +- packages/trace-viewer/src/ui/watchMode.tsx | 2 +- 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 296077b064..91730482d3 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -30,6 +30,7 @@ import type { FullResult } from '../reporter'; export function addTestCommands(program: Command) { addTestCommand(program); + addOpenCommand(program); addShowReportCommand(program); addListFilesCommand(program); } @@ -37,32 +38,8 @@ export function addTestCommands(program: Command) { function addTestCommand(program: Command) { const command = program.command('test [test-filter...]'); command.description('run tests with Playwright Test'); - command.option('--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`); - command.option('--headed', `Run tests in headed browsers (default: headless)`); - command.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`); - command.option('-c, --config ', `Configuration file, or a test directory with optional ${kDefaultConfigFiles.map(file => `"${file}"`).join('/')}`); - command.option('--forbid-only', `Fail if test.only is called (default: false)`); - command.option('--fully-parallel', `Run all tests in parallel (default: false)`); - command.option('-g, --grep ', `Only run tests matching this regular expression (default: ".*")`); - command.option('-gv, --grep-invert ', `Only run tests that do not match this regular expression`); - command.option('--global-timeout ', `Maximum time this test suite can run in milliseconds (default: unlimited)`); - command.option('--ignore-snapshots', `Ignore screenshot and snapshot expectations`); - command.option('-j, --workers ', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`); - command.option('--list', `Collect all the tests and report them, but do not run`); - command.option('--max-failures ', `Stop after the first N failures`); - command.option('--no-deps', 'Do not run project dependencies'); - command.option('--output ', `Folder for output artifacts (default: "test-results")`); - command.option('--pass-with-no-tests', `Makes test run succeed even if no tests were found`); - command.option('--quiet', `Suppress stdio`); - command.option('--repeat-each ', `Run each test N times (default: 1)`); - command.option('--reporter ', `Reporter to use, comma-separated, can be ${builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${baseFullConfig.reporter[0]}")`); - command.option('--retries ', `Maximum retry count for flaky tests, zero for no retries (default: no retries)`); - command.option('--shard ', `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"`); - command.option('--project ', `Only run tests from the specified list of projects (default: run all projects)`); - command.option('--timeout ', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})`); - command.option('--trace ', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`); - command.option('-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`); - command.option('-x', `Stop after the first failure`); + const options = [...sharedOptions, ...testOnlyOptions].sort((a, b) => a[0].replace(/-/g, '').localeCompare(b[0].replace(/-/g, ''))); + options.forEach(([name, description]) => command.option(name, description)); command.action(async (args, opts) => { try { await runTests(args, opts); @@ -79,7 +56,31 @@ Examples: $ npx playwright test my.spec.ts $ npx playwright test some.spec.ts:42 $ npx playwright test --headed - $ npx playwright test --browser=webkit`); + $ npx playwright test --project=webkit`); +} + +function addOpenCommand(program: Command) { + const command = program.command('gui [test-filter...]'); + command.description('open Playwright Test UI'); + sharedOptions.forEach(([name, description]) => command.option(name, description)); + command.action(async (args, opts) => { + try { + opts.ui = true; + await runTests(args, opts); + } catch (e) { + console.error(e); + process.exit(1); + } + }); + command.addHelpText('afterAll', ` +Arguments [test-filter...]: + Pass arguments to filter test files. Each argument is treated as a regular expression. Matching is performed against the absolute file paths. + +Examples: + $ npx playwright gui my.spec.ts + $ npx playwright gui some.spec.ts:42 + $ npx playwright gui --headed + $ npx playwright gui --project=webkit`); } function addListFilesCommand(program: Command) { @@ -168,7 +169,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) { const runner = new Runner(config); let status: FullResult['status']; - if (process.env.PWTEST_UI) + if (opts.ui) status = await runner.uiAllTests(); else if (process.env.PWTEST_WATCH) status = await runner.watchAllTests(); @@ -257,3 +258,35 @@ function restartWithExperimentalTsEsm(configFile: string | null): boolean { } const kTraceModes: TraceMode[] = ['on', 'off', 'on-first-retry', 'retain-on-failure']; + +const sharedOptions: [string, string][] = [ + ['--headed', `Run tests in headed browsers (default: headless)`], + ['-c, --config ', `Configuration file, or a test directory with optional ${kDefaultConfigFiles.map(file => `"${file}"`).join('/')}`], + ['--fully-parallel', `Run all tests in parallel (default: false)`], + ['--ignore-snapshots', `Ignore screenshot and snapshot expectations`], + ['-j, --workers ', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`], + ['--max-failures ', `Stop after the first N failures`], + ['--no-deps', 'Do not run project dependencies'], + ['--output ', `Folder for output artifacts (default: "test-results")`], + ['--quiet', `Suppress stdio`], + ['--reporter ', `Reporter to use, comma-separated, can be ${builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${baseFullConfig.reporter[0]}")`], + ['--project ', `Only run tests from the specified list of projects (default: run all projects)`], + ['--timeout ', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})`], + ['-x', `Stop after the first failure`], +]; + +const testOnlyOptions: [string, string][] = [ + ['--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`], + ['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`], + ['--forbid-only', `Fail if test.only is called (default: false)`], + ['--global-timeout ', `Maximum time this test suite can run in milliseconds (default: unlimited)`], + ['-g, --grep ', `Only run tests matching this regular expression (default: ".*")`], + ['-gv, --grep-invert ', `Only run tests that do not match this regular expression`], + ['--list', `Collect all the tests and report them, but do not run`], + ['--pass-with-no-tests', `Makes test run succeed even if no tests were found`], + ['--repeat-each ', `Run each test N times (default: 1)`], + ['--retries ', `Maximum retry count for flaky tests, zero for no retries (default: no retries)`], + ['--shard ', `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"`], + ['--trace ', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`], + ['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`], +]; diff --git a/packages/playwright-test/src/runner/reporters.ts b/packages/playwright-test/src/runner/reporters.ts index 24d80fda12..cb8ee06c0e 100644 --- a/packages/playwright-test/src/runner/reporters.ts +++ b/packages/playwright-test/src/runner/reporters.ts @@ -31,7 +31,7 @@ import type { FullConfigInternal } from '../common/types'; import { loadReporter } from './loadUtils'; import type { BuiltInReporter } from '../common/configLoader'; -export async function createReporter(config: FullConfigInternal, mode: 'list' | 'watch' | 'run') { +export async function createReporter(config: FullConfigInternal, mode: 'list' | 'watch' | 'run' | 'ui', additionalReporters: Reporter[] = []): Promise { const defaultReporters: {[key in BuiltInReporter]: new(arg: any) => Reporter} = { dot: mode === 'list' ? ListModeReporter : DotReporter, line: mode === 'list' ? ListModeReporter : LineReporter, @@ -40,7 +40,7 @@ export async function createReporter(config: FullConfigInternal, mode: 'list' | json: JSONReporter, junit: JUnitReporter, null: EmptyReporter, - html: HtmlReporter, + html: mode === 'ui' ? LineReporter : HtmlReporter, }; const reporters: Reporter[] = []; if (mode === 'watch') { @@ -55,6 +55,7 @@ export async function createReporter(config: FullConfigInternal, mode: 'list' | reporters.push(new reporterConstructor(arg)); } } + reporters.push(...additionalReporters); if (process.env.PW_TEST_REPORTER) { const reporterConstructor = await loadReporter(config, process.env.PW_TEST_REPORTER); reporters.push(new reporterConstructor()); diff --git a/packages/playwright-test/src/runner/uiMode.ts b/packages/playwright-test/src/runner/uiMode.ts index bc0c4b5c78..ddb8201bfc 100644 --- a/packages/playwright-test/src/runner/uiMode.ts +++ b/packages/playwright-test/src/runner/uiMode.ts @@ -20,7 +20,6 @@ import { ManualPromise } from 'playwright-core/lib/utils'; import type { FullResult } from '../../reporter'; import { clearCompilationCache, dependenciesForTestFile } from '../common/compilationCache'; import type { FullConfigInternal } from '../common/types'; -import ListReporter from '../reporters/list'; import { Multiplexer } from '../reporters/multiplexer'; import { TeleReporterEmitter } from '../reporters/teleEmitter'; import { createReporter } from './reporters'; @@ -111,7 +110,7 @@ class UIMode { return; } if (method === 'open' && params.location) { - open.openApp('code', { arguments: ['--goto', params.location] }).catch(() => {}); + open('vscode://file/' + params.location).catch(e => this._originalStderr(String(e))); return; } if (method === 'resizeTerminal') { @@ -163,7 +162,7 @@ class UIMode { this._config._internal.testIdMatcher = id => !testIdSet || testIdSet.has(id); const runReporter = new TeleReporterEmitter(e => this._dispatchEvent(e)); - const reporter = new Multiplexer([new ListReporter(), runReporter]); + const reporter = await createReporter(this._config, 'ui', [runReporter]); const taskRunner = createTaskRunnerForWatch(this._config, reporter); const context: TaskRunnerState = { config: this._config, reporter, phases: [] }; clearCompilationCache(); diff --git a/packages/trace-viewer/src/ui/watchMode.tsx b/packages/trace-viewer/src/ui/watchMode.tsx index c9bb002419..eac4073bc9 100644 --- a/packages/trace-viewer/src/ui/watchMode.tsx +++ b/packages/trace-viewer/src/ui/watchMode.tsx @@ -232,7 +232,7 @@ export const SettingsView: React.FC<{ {[...projects.entries()].map(([projectName, value]) => { - return
+ return
{ const copy = new Map(projects); copy.set(projectName, !copy.get(projectName));