From 44dba4302380dbd7204a5ced10c15e70911cc12e Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Thu, 6 Aug 2026 19:37:16 +0200 Subject: [PATCH 1/4] fix(html-reporter): use h2 for non-expandable chip headers (#42151) --- packages/html-reporter/src/chip.css | 2 +- packages/html-reporter/src/chip.spec.ts | 7 +++++-- packages/html-reporter/src/chip.tsx | 4 ++-- tests/playwright-test/reporter-html.spec.ts | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/html-reporter/src/chip.css b/packages/html-reporter/src/chip.css index f37aff953b642..513fe4ff40c7f 100644 --- a/packages/html-reporter/src/chip.css +++ b/packages/html-reporter/src/chip.css @@ -23,7 +23,7 @@ background-color: var(--color-canvas-subtle); padding: 0 8px; border-bottom: none; - margin-top: 12px; + margin: 12px 0 0; color: inherit; font: inherit; font-weight: 600; diff --git a/packages/html-reporter/src/chip.spec.ts b/packages/html-reporter/src/chip.spec.ts index 69d6444dbfb57..c48a9b79b635d 100644 --- a/packages/html-reporter/src/chip.spec.ts +++ b/packages/html-reporter/src/chip.spec.ts @@ -50,10 +50,13 @@ test('body render prop is rendered', async ({ mount }) => { await expect(component.getByText('Chip children')).toBeVisible(); }); -test('chip without setExpanded is not a button', async ({ mount }) => { +test('chip without setExpanded is a heading', async ({ mount }) => { const component = await mount('chip/NotExpandable'); await expect(component.getByRole('button')).toHaveCount(0); - await expect(component.getByText('Body')).toBeVisible(); + await expect(component).toMatchAriaSnapshot(` + - heading "Title" [level=2] + - region: Body + `); }); test('expand collapse with the keyboard', async ({ mount, page }) => { diff --git a/packages/html-reporter/src/chip.tsx b/packages/html-reporter/src/chip.tsx index 1a6926e36c28b..b206188cab1fe 100644 --- a/packages/html-reporter/src/chip.tsx +++ b/packages/html-reporter/src/chip.tsx @@ -47,9 +47,9 @@ export const Chip: React.FC<{ onClick={() => setExpanded(!expanded)} title={title}> {headerContent} - :
+ :

{headerContent} -

} + } {(!setExpanded || expanded) &&
{children} {body && body()} diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 9ce7ac74a2569..3c0a31e733247 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -3431,7 +3431,7 @@ for (const useIntermediateMergeReport of [true, false] as const) { await expect(page.getByRole('link', { name: 'Speedboard' })).toHaveAttribute('aria-selected', 'true'); await expect(page).toMatchAriaSnapshot(` - - text: /Slowest Tests/ + - heading "Slowest Tests" [level=2] - region: - list: - listitem: @@ -3455,7 +3455,7 @@ for (const useIntermediateMergeReport of [true, false] as const) { `); await page.getByText('foo').first().click(); await expect(page).toMatchAriaSnapshot(` - - text: /Slowest Tests/ + - heading "Slowest Tests" [level=2] `); await page.getByRole('link', { name: 'Failed' }).click(); From 75dbf177180a64313cfbb55d86667a6ef0228d65 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Thu, 6 Aug 2026 19:37:22 +0200 Subject: [PATCH 2/4] feat(mcp): default codegen language from PW_LANG_NAME (#42150) --- packages/playwright-core/src/tools/mcp/config.ts | 8 ++++++++ packages/playwright-core/src/tools/mcp/program.ts | 4 ++-- tests/mcp/codegen.spec.ts | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/tools/mcp/config.ts b/packages/playwright-core/src/tools/mcp/config.ts index 8125fd348d0f0..0fa8802d2be5a 100644 --- a/packages/playwright-core/src/tools/mcp/config.ts +++ b/packages/playwright-core/src/tools/mcp/config.ts @@ -81,11 +81,17 @@ export type CLIOptions = { viewportSize?: ViewportSize; }; +export const defaultCodegenLanguage: 'typescript' | 'python' | 'java' | 'csharp' = + process.env.PW_LANG_NAME === 'python' || process.env.PW_LANG_NAME === 'java' || process.env.PW_LANG_NAME === 'csharp' + ? process.env.PW_LANG_NAME + : 'typescript'; + const defaultConfig: MergedConfig = { browser: { launchOptions: {}, contextOptions: {}, }, + codegen: defaultCodegenLanguage, timeouts: { action: 5000, navigation: 60000, @@ -406,6 +412,8 @@ export function configFromEnv(env?: NodeJS.ProcessEnv): Config & { configFile?: options.cdpEndpoint = envToString(e.PLAYWRIGHT_MCP_CDP_ENDPOINT); options.cdpHeader = headerParser(envToString(e.PLAYWRIGHT_MCP_CDP_HEADERS)); options.cdpTimeout = numberParser(e.PLAYWRIGHT_MCP_CDP_TIMEOUT); + if (e.PLAYWRIGHT_MCP_CODEGEN) + options.codegen = enumParser<'typescript' | 'python' | 'java' | 'csharp' | 'none'>('--codegen', ['none', 'typescript', 'python', 'java', 'csharp'], e.PLAYWRIGHT_MCP_CODEGEN); options.config = envToString(e.PLAYWRIGHT_MCP_CONFIG); if (e.PLAYWRIGHT_MCP_CONSOLE_LEVEL) options.consoleLevel = enumParser<'error' | 'warning' | 'info' | 'debug'>('--console-level', ['error', 'warning', 'info', 'debug'], e.PLAYWRIGHT_MCP_CONSOLE_LEVEL); diff --git a/packages/playwright-core/src/tools/mcp/program.ts b/packages/playwright-core/src/tools/mcp/program.ts index cc7de4f504d3a..4fb5d4302d47b 100644 --- a/packages/playwright-core/src/tools/mcp/program.ts +++ b/packages/playwright-core/src/tools/mcp/program.ts @@ -16,7 +16,7 @@ import { Option as ProgramOption } from 'commander'; import * as mcpServer from '../utils/mcp/server'; -import { commaSeparatedList, dotenvFileLoader, enumParser, headerParser, numberParser, resolutionParser, resolveCLIConfigForMCP, semicolonSeparatedList } from './config'; +import { commaSeparatedList, defaultCodegenLanguage, dotenvFileLoader, enumParser, headerParser, numberParser, resolutionParser, resolveCLIConfigForMCP, semicolonSeparatedList } from './config'; import { setupExitWatchdog } from './watchdog'; import { createBrowserWithInfo } from './browserFactory'; import { BrowserBackend } from '../backend/browserBackend'; @@ -42,7 +42,7 @@ export function decorateMCPCommand(command: Command) { .option('--cdp-endpoint ', 'CDP endpoint to connect to.') .option('--cdp-header ', 'CDP headers to send with the connect request, multiple can be specified.', headerParser) .option('--cdp-timeout ', 'timeout in milliseconds for connecting to CDP endpoint, defaults to 30000ms', numberParser) - .option('--codegen ', 'specify the language to use for code generation, possible values: "typescript", "python", "java", "csharp", "none". Default is "typescript".', enumParser.bind(null, '--codegen', ['none', 'typescript', 'python', 'java', 'csharp'])) + .option('--codegen ', `specify the language to use for code generation, possible values: "typescript", "python", "java", "csharp", "none". Default is "${defaultCodegenLanguage}".`, enumParser.bind(null, '--codegen', ['none', 'typescript', 'python', 'java', 'csharp'])) .option('--config ', 'path to the configuration file.') .option('--console-level ', 'level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.', enumParser.bind(null, '--console-level', ['error', 'warning', 'info', 'debug'])) .option('--device ', 'device to emulate, for example: "iPhone 15"') diff --git a/tests/mcp/codegen.spec.ts b/tests/mcp/codegen.spec.ts index 6fa811393c18d..c4c689133d516 100644 --- a/tests/mcp/codegen.spec.ts +++ b/tests/mcp/codegen.spec.ts @@ -31,6 +31,20 @@ async function navigateToForm(client: Client, server: any) { }); } +test('codegen defaults to PW_LANG_NAME', async ({ startClient, server }) => { + const { client } = await startClient({ env: { PW_LANG_NAME: 'python' } }); + expect(await navigateToForm(client, server)).toHaveResponse({ + code: `page.goto("${server.PREFIX}")`, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { element: 'Submit button', target: 'e2' }, + })).toHaveResponse({ + code: `page.get_by_role("button", name="Submit").click()`, + }); +}); + test('codegen python', async ({ startClient, server }) => { const { client } = await startClient({ args: ['--codegen=python'] }); expect(await navigateToForm(client, server)).toHaveResponse({ From a6c2a481196ea07e2e29e86cf944893d0818d39a Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Thu, 6 Aug 2026 19:37:36 +0200 Subject: [PATCH 3/4] fix(cli): print -s instead of --s after attach (#42138) --- packages/playwright-core/src/tools/cli-client/output.ts | 2 +- tests/mcp/cli-session.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/tools/cli-client/output.ts b/packages/playwright-core/src/tools/cli-client/output.ts index 7dc38f04b52da..afcac0e728239 100644 --- a/packages/playwright-core/src/tools/cli-client/output.ts +++ b/packages/playwright-core/src/tools/cli-client/output.ts @@ -224,7 +224,7 @@ export class TextOutput implements Output { attach(session: string, pid: number | undefined, endpoint: string | undefined, toolResult: string): void { if (endpoint) { console.log(`### Session \`${session}\` created, attached to \`${endpoint}\`.`); - console.log(`Run commands with: playwright-cli --s=${session} `); + console.log(`Run commands with: playwright-cli -s=${session} `); console.log(''); } else { console.log(`### Browser \`${session}\` opened with pid ${pid}.`); diff --git a/tests/mcp/cli-session.spec.ts b/tests/mcp/cli-session.spec.ts index 30bd50abd3eb3..08a2b13125469 100644 --- a/tests/mcp/cli-session.spec.ts +++ b/tests/mcp/cli-session.spec.ts @@ -293,7 +293,7 @@ workspace1: await page.setContent('My Page'); const { output: openOutput } = await cli('attach', 'foobar'); expect(openOutput).toContain('### Session `foobar` created, attached to `foobar`.'); - expect(openOutput).toContain('Run commands with: playwright-cli --s=foobar '); + expect(openOutput).toContain('Run commands with: playwright-cli -s=foobar '); const { output: listOutput } = await cli('list', '--all'); expect(listOutput).toBe(`### Browsers /: @@ -326,7 +326,7 @@ workspace1: await page.setContent('Alias Page'); const { output: openOutput } = await cli('attach', 'foobar', '--session=mybrowser'); expect(openOutput).toContain('### Session `mybrowser` created, attached to `foobar`.'); - expect(openOutput).toContain('Run commands with: playwright-cli --s=mybrowser '); + expect(openOutput).toContain('Run commands with: playwright-cli -s=mybrowser '); await cli('-s', 'mybrowser', 'close'); }); From 3afd6ceebbde57f294cfb1dea87c59d587bb1ee3 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 6 Aug 2026 14:48:00 -0700 Subject: [PATCH 4/4] chore(extension): mark 0.3.0 (#42158) --- package-lock.json | 2 +- packages/extension/manifest.json | 2 +- packages/extension/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2428dea4eb701..677337971f910 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9740,7 +9740,7 @@ }, "packages/extension": { "name": "@playwright/extension", - "version": "0.2.1", + "version": "0.3.0", "license": "Apache-2.0", "engines": { "node": ">=20" diff --git a/packages/extension/manifest.json b/packages/extension/manifest.json index dec34378cfd07..59c9234aae1ad 100644 --- a/packages/extension/manifest.json +++ b/packages/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Playwright Extension", - "version": "0.2.1", + "version": "0.3.0", "description": "Connect your browser to AI agents through Playwright MCP server and CLI. Enables AI-driven web testing, debugging, and automation.", "permissions": [ "debugger", diff --git a/packages/extension/package.json b/packages/extension/package.json index 420ad8b3fea1f..562cff9277ae2 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/extension", - "version": "0.2.1", + "version": "0.3.0", "description": "Playwright Browser Extension", "private": true, "repository": {