diff --git a/README.md b/README.md index ed86206..d8070e5 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ npm install -g @playwright/cli@latest playwright-cli --help ``` +Argument parsing and startup errors exit with code 1 and print an error message. With +`--json`, these errors are written to stdout as `{ "isError": true, "error": "..." }`. + ### Installing skills Claude Code, GitHub Copilot and others will use the locally installed skills. diff --git a/playwright-cli.js b/playwright-cli.js index 8e9d23f..28fa1f8 100755 --- a/playwright-cli.js +++ b/playwright-cli.js @@ -34,10 +34,24 @@ const UPDATE_CHECK_COMMANDS = new Set(['open', 'attach', 'list', 'install', 'ins main(); async function main() { - // Parse once and inspect the arguments before program() runs, as it may mutate them. - const args = parseArgs(process.argv.slice(2)); - await checkForUpdates(args).catch(() => {}); - await program({ args, embedderVersion: packageJson.version }); + const argv = process.argv.slice(2); + // Parsing can fail before the output mode is known, so start from the flag. + let json = argv.includes('--json'); + try { + // Parse once and inspect the arguments before program() runs, as it may mutate them. + const args = parseArgs(argv); + json = !!args.json; + await checkForUpdates(args).catch(() => {}); + await program({ args, embedderVersion: packageJson.version }); + } catch (error) { + // Report the failure the way program() reports its own errors. + const message = error instanceof Error ? error.message : String(error); + if (json) + console.log(JSON.stringify({ isError: true, error: message }, null, 2)); + else + console.error(message); + process.exit(1); + } } /** diff --git a/tests/integration.spec.ts b/tests/integration.spec.ts index 72e5831..94d4298 100644 --- a/tests/integration.spec.ts +++ b/tests/integration.spec.ts @@ -107,12 +107,31 @@ async function runCli(args: string[], env: Record = {}): Promise }); } -test('open data URL', async ({}) => { +test('open data URL', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-cli/issues/502' } }, async ({}) => { + const argumentError = "boolean option '--headed' should not be passed with '=value', use '--headed' or '--no-headed' instead"; + expect(await runCli(['open', '--headed=false'])).toEqual({ + output: '', + error: argumentError, + exitCode: 1, + }); + + expect(await runCli(['--json', 'open', '--headed=false'])).toEqual({ + output: JSON.stringify({ isError: true, error: argumentError }, null, 2), + error: '', + exitCode: 1, + }); + expect(await runCli(['open', 'data:text/html,hello', '--persistent'])).toEqual(expect.objectContaining({ output: expect.stringContaining('hello'), exitCode: 0, })); + expect(await runCli(['--json', 'screenshot', '--filename'])).toEqual({ + output: JSON.stringify({ isError: true, error: "error: '--filename' option: expected string, received boolean" }, null, 2), + error: '', + exitCode: 1, + }); + expect(await runCli(['delete-data'])).toEqual(expect.objectContaining({ output: expect.stringContaining('Deleted user data for'), exitCode: 0,