diff --git a/.gitignore b/.gitignore index d695893a270e2..834eff0437594 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ drivers/ nohup.out .trace .tmp +.playwright-cli allure* blob-report playwright-report diff --git a/package.json b/package.json index 9cb6bd88d7078..d57041c30dee6 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,10 @@ "roll": "node utils/roll_browser.js", "check-deps": "node utils/check_deps.js", "build-android-driver": "./utils/build_android_driver.sh", - "innerloop": "playwright run-server --reuse-browser" + "innerloop": "playwright run-server --reuse-browser", + "playwright-cli": "node packages/playwright/lib/mcp/terminal/cli.js", + "test-playwright-cli": "playwright test --config=tests/mcp/playwright.config.ts --project=chrome cli.spec.ts", + "playwright-cli-readme": "node utils/generate_cli_help.js --readme" }, "workspaces": [ "packages/*" diff --git a/packages/playwright/src/mcp/terminal/help.json b/packages/playwright/src/mcp/terminal/help.json deleted file mode 100644 index b049024a00334..0000000000000 --- a/packages/playwright/src/mcp/terminal/help.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "global": "Usage: playwright-cli [args] [options]\n\nCore:\n open open url\n close close the page\n type type text into editable element\n click [button] perform click on a web page\n dblclick [button] perform double click on a web page\n fill fill text into editable element\n drag perform drag and drop between two elements\n hover hover over element on page\n select select an option in a dropdown\n upload upload one or multiple files\n check check a checkbox or radio button\n uncheck uncheck a checkbox or radio button\n snapshot capture page snapshot to obtain element ref\n eval [ref] evaluate javascript expression on page or element\n dialog-accept [prompt] accept a dialog\n dialog-dismiss dismiss a dialog\n resize resize the browser window\n\nNavigation:\n go-back go back to the previous page\n go-forward go forward to the next page\n reload reload the current page\n\nKeyboard:\n press press a key on the keyboard, `a`, `arrowleft`\n keydown press a key down on the keyboard\n keyup press a key up on the keyboard\n\nMouse:\n mousemove move mouse to a given position\n mousedown [button] press mouse down\n mouseup [button] press mouse up\n mousewheel scroll mouse wheel\n\nSave as:\n screenshot [ref] screenshot of the current page or element\n pdf save page as pdf\n\nTabs:\n tab-list list all tabs\n tab-new [url] create a new tab\n tab-close [index] close a browser tab\n tab-select select a browser tab\n\nDevTools:\n console [min-level] list console messages\n network list all network requests since loading the page\n run-code run playwright code snippet\n tracing-start start trace recording\n tracing-stop stop trace recording\n\nSessions:\n session-list list all sessions\n session-stop [name] stop session\n session-stop-all stop all sessions\n session-delete [name] delete session data", - "commands": { - "open": "playwright-cli open \n\nOpen URL\n\nArguments:\n the url to navigate to\nOptions:\n --headed run browser in headed mode", - "close": "playwright-cli close \n\nClose the page\n", - "type": "playwright-cli type \n\nType text into editable element\n\nArguments:\n text to type into the element\nOptions:\n --submit whether to submit entered text (press enter after)", - "click": "playwright-cli click [button]\n\nPerform click on a web page\n\nArguments:\n exact target element reference from the page snapshot\n [button] button to click, defaults to left\nOptions:\n --modifiers modifier keys to press", - "dblclick": "playwright-cli dblclick [button]\n\nPerform double click on a web page\n\nArguments:\n exact target element reference from the page snapshot\n [button] button to click, defaults to left\nOptions:\n --modifiers modifier keys to press", - "fill": "playwright-cli fill \n\nFill text into editable element\n\nArguments:\n exact target element reference from the page snapshot\n text to fill into the element\nOptions:\n --submit whether to submit entered text (press enter after)", - "drag": "playwright-cli drag \n\nPerform drag and drop between two elements\n\nArguments:\n exact source element reference from the page snapshot\n exact target element reference from the page snapshot\nOptions:\n --headed run browser in headed mode", - "hover": "playwright-cli hover \n\nHover over element on page\n\nArguments:\n exact target element reference from the page snapshot", - "select": "playwright-cli select \n\nSelect an option in a dropdown\n\nArguments:\n exact target element reference from the page snapshot\n value to select in the dropdown", - "upload": "playwright-cli upload \n\nUpload one or multiple files\n\nArguments:\n the absolute paths to the files to upload", - "check": "playwright-cli check \n\nCheck a checkbox or radio button\n\nArguments:\n exact target element reference from the page snapshot", - "uncheck": "playwright-cli uncheck \n\nUncheck a checkbox or radio button\n\nArguments:\n exact target element reference from the page snapshot", - "snapshot": "playwright-cli snapshot \n\nCapture page snapshot to obtain element ref\n\nOptions:\n --filename save snapshot to markdown file instead of returning it in the response.", - "eval": "playwright-cli eval [ref]\n\nEvaluate JavaScript expression on page or element\n\nArguments:\n () => { /* code */ } or (element) => { /* code */ } when element is provided\n [ref] exact target element reference from the page snapshot", - "console": "playwright-cli console [min-level]\n\nList console messages\n\nArguments:\n [min-level] level of the console messages to return. each level includes the messages of more severe levels. defaults to \"info\".\nOptions:\n --clear whether to clear the console list", - "dialog-accept": "playwright-cli dialog-accept [prompt]\n\nAccept a dialog\n\nArguments:\n [prompt] the text of the prompt in case of a prompt dialog.", - "dialog-dismiss": "playwright-cli dialog-dismiss \n\nDismiss a dialog\n", - "resize": "playwright-cli resize \n\nResize the browser window\n\nArguments:\n width of the browser window\n height of the browser window", - "go-back": "playwright-cli go-back \n\nGo back to the previous page\n", - "go-forward": "playwright-cli go-forward \n\nGo forward to the next page\n", - "reload": "playwright-cli reload \n\nReload the current page\n", - "press": "playwright-cli press \n\nPress a key on the keyboard, `a`, `ArrowLeft`\n\nArguments:\n name of the key to press or a character to generate, such as `arrowleft` or `a`", - "keydown": "playwright-cli keydown \n\nPress a key down on the keyboard\n\nArguments:\n name of the key to press or a character to generate, such as `arrowleft` or `a`", - "keyup": "playwright-cli keyup \n\nPress a key up on the keyboard\n\nArguments:\n name of the key to press or a character to generate, such as `arrowleft` or `a`", - "mousemove": "playwright-cli mousemove \n\nMove mouse to a given position\n\nArguments:\n x coordinate\n y coordinate", - "mousedown": "playwright-cli mousedown [button]\n\nPress mouse down\n\nArguments:\n [button] button to press, defaults to left", - "mouseup": "playwright-cli mouseup [button]\n\nPress mouse up\n\nArguments:\n [button] button to press, defaults to left", - "mousewheel": "playwright-cli mousewheel \n\nScroll mouse wheel\n\nArguments:\n y delta\n x delta", - "screenshot": "playwright-cli screenshot [ref]\n\nscreenshot of the current page or element\n\nArguments:\n [ref] exact target element reference from the page snapshot.\nOptions:\n --filename file name to save the screenshot to. defaults to `page-{timestamp}.{png|jpeg}` if not specified.\n --full-page when true, takes a screenshot of the full scrollable page, instead of the currently visible viewport.", - "pdf": "playwright-cli pdf \n\nSave page as PDF\n\nOptions:\n --filename file name to save the pdf to. defaults to `page-{timestamp}.pdf` if not specified.", - "tab-list": "playwright-cli tab-list \n\nList all tabs\n", - "tab-new": "playwright-cli tab-new [url]\n\nCreate a new tab\n\nArguments:\n [url] the url to navigate to in the new tab. if omitted, the new tab will be blank.", - "tab-close": "playwright-cli tab-close [index]\n\nClose a browser tab\n\nArguments:\n [index] tab index. if omitted, current tab is closed.", - "tab-select": "playwright-cli tab-select \n\nSelect a browser tab\n\nArguments:\n tab index", - "network": "playwright-cli network \n\nList all network requests since loading the page\n\nOptions:\n --static whether to include successful static resources like images, fonts, scripts, etc. defaults to false.\n --clear whether to clear the network list", - "run-code": "playwright-cli run-code \n\nRun Playwright code snippet\n\nArguments:\n a javascript function containing playwright code to execute. it will be invoked with a single argument, page, which you can use for any page interaction.", - "tracing-start": "playwright-cli tracing-start \n\nStart trace recording\n", - "tracing-stop": "playwright-cli tracing-stop \n\nStop trace recording\n", - "session-list": "playwright-cli session-list \n\nList all sessions\n", - "session-stop": "playwright-cli session-stop [name]\n\nStop session\n\nArguments:\n [name] name of the session to stop. if omitted, current session is stopped.", - "session-stop-all": "playwright-cli session-stop-all \n\nStop all sessions\n", - "session-delete": "playwright-cli session-delete [name]\n\nDelete session data\n\nArguments:\n [name] name of the session to delete. if omitted, current session is deleted." - } -} \ No newline at end of file diff --git a/packages/playwright/src/mcp/terminal/helpGenerator.ts b/packages/playwright/src/mcp/terminal/helpGenerator.ts index 59ad7934ee04f..0062d8a6285de 100644 --- a/packages/playwright/src/mcp/terminal/helpGenerator.ts +++ b/packages/playwright/src/mcp/terminal/helpGenerator.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import fs from 'fs'; -import path from 'path'; import { commands } from './commands'; import type zodType from 'zod'; @@ -77,7 +75,7 @@ const categories: { name: Category, title: string }[] = [ { name: 'session', title: 'Sessions' }, ] as const; -function generateHelp() { +export function generateHelp() { const lines: string[] = []; lines.push('Usage: playwright-cli [args] [options]'); @@ -99,7 +97,7 @@ function generateHelp() { } -function generateReadme() { +export function generateReadme() { const lines: string[] = []; lines.push('\n## Commands'); @@ -136,27 +134,17 @@ function generateReadmeEntry(command: AnyCommandSchema): string { return formatWithGap(prefix, suffix, 40); } -async function main() { +export function generateHelpJSON() { const help = { global: generateHelp(), commands: Object.fromEntries( Object.entries(commands).map(([name, command]) => [name, generateCommandHelp(command)]) ), }; - const readme = generateReadme(); - const fileName = path.resolve(__dirname, 'help.json').replace('lib', 'src'); - // eslint-disable-next-line no-console - console.log('Writing ', path.relative(process.cwd(), fileName)); - await fs.promises.writeFile(fileName, JSON.stringify(help, null, 2)); - // eslint-disable-next-line no-console - console.log(help.global); - // eslint-disable-next-line no-console - console.log(readme); + return help; } function formatWithGap(prefix: string, text: string, threshold: number = 30) { const indent = Math.max(1, threshold - prefix.length); return prefix + ' '.repeat(indent) + text; } - -void main(); diff --git a/utils/build/build.js b/utils/build/build.js index 00802b7cbd12f..67ec71c8b6f2f 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -551,6 +551,15 @@ for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer']) { })); } +// Generate CLI help. +onChanges.push({ + inputs: [ + 'packages/playwright/src/mcp/terminal/commands.ts', + 'utils/generate_cli_help.js', + ], + script: 'utils/generate_cli_help.js', +}); + // Generate injected. onChanges.push({ inputs: [ @@ -636,12 +645,6 @@ copyFiles.push({ to: 'packages/playwright/lib', }); -copyFiles.push({ - files: 'packages/playwright/src/mcp/terminal/*.json', - from: 'packages/playwright/src', - to: 'packages/playwright/lib', -}); - copyFiles.push({ files: 'packages/playwright/src/mcp/terminal/*.md', from: 'packages/playwright/src', diff --git a/utils/generate_cli_commands.js b/utils/generate_cli_commands.js deleted file mode 100644 index 46da7c5aa3ca8..0000000000000 --- a/utils/generate_cli_commands.js +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env node -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// @ts-check - -const fs = require('fs') -const path = require('path') - -const { commands } = require('../packages/playwright/lib/mcp/terminal/commands.js'); - -/** - * - * @param {import('../packages/playwright/src/mcp/terminal/command').AnyCommandSchema} command - */ -function generateCommandHelp(command: AnyCommandSchema) { - const args: { name: string, description: string }[] = []; - - const shape = command.args ? (command.args as zodType.ZodObject).shape : {}; - for (const [name, schema] of Object.entries(shape)) { - const zodSchema = schema as zodType.ZodTypeAny; - const description = zodSchema.description ?? ''; - args.push({ name, description}) - } - - const lines: string[] = [ - `playwright-cli ${command.name} ${Object.keys(shape).map(k => `<${k}>`).join(' ')}`, - '', - command.description, - '', - ]; - - if (args.length) { - lines.push('Arguments:'); - for (const arg of args) - lines.push(...args.map(({ name, description }) => ` <${name}>\t${description}`)); - } - - if (command.options) { - lines.push('Options:'); - const optionsShape = (command.options as zodType.ZodObject).shape; - for (const [name, schema] of Object.entries(optionsShape)) { - const zodSchema = schema as zodType.ZodTypeAny; - const description = (zodSchema.description ?? '').toLowerCase(); - lines.push(` --${name}\t${description}`); - } - } - - console.log(lines.join('\n')); -} - -export function printHelp(commands: AnyCommandSchema[]) { - console.log('Usage: playwright-cli [options]'); - console.log('Commands:'); - for (const command of commands) - console.log(' ' + commandHelpEntry(command)); -} - -function commandHelpEntry(command: AnyCommandSchema): string { - const args: { name: string, description: string }[] = []; - - const shape = (command.args as zodType.ZodObject).shape; - for (const [name, schema] of Object.entries(shape)) { - const zodSchema = schema as zodType.ZodTypeAny; - const description = zodSchema.description ?? ''; - args.push({ name, description}) - } - - const lines: string[] = [ - `${command.name} ${Object.keys(shape).map(k => `<${k}>`).join(' ')}`, - command.description.toLowerCase(), - ]; - return lines.join('\t'); -} diff --git a/utils/generate_cli_help.js b/utils/generate_cli_help.js new file mode 100644 index 0000000000000..4097f96e548fe --- /dev/null +++ b/utils/generate_cli_help.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// @ts-check + +const fs = require('fs') +const path = require('path') + +const { generateHelp, generateReadme, generateHelpJSON } = require('../packages/playwright/lib/mcp/terminal/helpGenerator.js'); + +if (process.argv[2] === '--readme') { + console.log(generateReadme()); + process.exit(0); +} + +if (process.argv[2] === '--print') { + console.log(generateHelp()); + process.exit(0); +} + +const fileName = path.resolve(__dirname, '../packages/playwright/lib/mcp/terminal/help.json'); +console.log('Writing ', path.relative(process.cwd(), fileName)); +fs.writeFileSync(fileName, JSON.stringify(generateHelpJSON(), null, 2));