Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ Never add "Generated with" in commit message.
Never add test plan to PR description. Keep PR description short — a few bullet points at most.
Branch naming for issue fixes: `fix-<issue-number>`

**Never `git push` without an explicit instruction to push.** Applies even when a PR is already open for the branch — additional commits are immediately visible to reviewers. Commit locally, report what was committed, and wait. Only push when the user's message contains "push", "upload", "create PR", "ship it", or equivalent.

## Development Guides

Detailed guides for common development tasks:
Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ Connect to your existing browser tabs instead of launching a new browser:
playwright-cli attach --extension
```

This requires the [Playwright MCP Bridge browser extension](https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md) to be installed.
This requires the [Playwright Extension](https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md) to be installed.

## Quick Reference

Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started-mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Playwright MCP supports three profile modes:

- **Persistent (default)**: Login state and cookies are preserved between sessions. The profile is stored in `ms-playwright/mcp-{channel}-{workspace-hash}` in your platform's cache directory, so different projects get separate profiles automatically. Override with `--user-data-dir`.
- **Isolated**: Each session starts fresh. Pass `--isolated` to enable. You can load initial state with `--storage-state`.
- **Browser extension**: Connect to your existing browser tabs with the [Playwright MCP Bridge extension](https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md). Pass `--extension` to enable.
- **Browser extension**: Connect to your existing browser tabs with the [Playwright Extension](https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md). Pass `--extension` to enable.

### Configuration file

Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/mcp/cdpRelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class CDPRelayServer {
await Promise.race([
this._extensionConnectionPromise,
new Promise((_, reject) => setTimeout(() => {
reject(new Error(`Extension connection timeout. Make sure the "Playwright MCP Bridge" extension is installed. See https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md for installation instructions.`));
reject(new Error(`Extension connection timeout. Make sure the "Playwright Extension" is installed. See https://github.com/microsoft/playwright-mcp/blob/main/packages/extension/README.md for installation instructions.`));
}, process.env.PWMCP_TEST_CONNECTION_TIMEOUT ? parseInt(process.env.PWMCP_TEST_CONNECTION_TIMEOUT, 10) : 5_000)),
]);
debugLogger('Extension connection established');
Expand Down
3 changes: 3 additions & 0 deletions packages/playwright-core/src/tools/mcp/cdpRelayV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export class ExtensionProtocolV1 implements ExtensionProtocolHandler {
case 'Target.getTargetInfo': {
return { result: this._connectedTabInfo?.targetInfo };
}
case 'Target.createTarget': {
throw new Error('Tab creation is not supported yet.');
}
}
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/mcp/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export type Config = {
/**
* Connect to a running browser instance (Edge/Chrome only). If specified, `browser`
* config is ignored.
* Requires the "Playwright MCP Bridge" browser extension to be installed.
* Requires the "Playwright Extension" to be installed.
*/
extension?: boolean;

Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/mcp/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function decorateMCPCommand(command: Command) {
.option('--console-level <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>', 'device to emulate, for example: "iPhone 15"')
.option('--executable-path <path>', 'path to the browser executable.')
.option('--extension', 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.')
.option('--extension', 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright Extension" to be installed.')
.option('--endpoint <endpoint>', 'Bound browser endpoint to connect to.')
.option('--grant-permissions <permissions...>', 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', commaSeparatedList)
.option('--headless', 'run browser in headless mode, headed by default')
Expand Down
34 changes: 34 additions & 0 deletions tests/page/page-network-response.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,37 @@ it('should return http version', async ({ page, server }) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(await response.httpVersion()).toBe('HTTP/1.1');
});

it('Response.formData() should parse multipart/form-data in page context', async ({ page, server, browserName }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/40244' });
it.fail(browserName === 'webkit', 'WebKit 26.4 upstream regression: rejects multipart body without trailing CRLF after closing boundary');
await page.goto(server.EMPTY_PAGE);
const result = await page.evaluate(async () => {
const boundary = '----WebKitFormBoundary1234';
const body = [
`--${boundary}`,
'Content-Disposition: form-data; name="field1"',
'',
'value1',
`--${boundary}`,
'Content-Disposition: form-data; name="file1"; filename="test.txt"',
'Content-Type: text/plain',
'',
'hello',
`--${boundary}--`,
].join('\r\n');
const response = new Response(body, {
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
});
const fd = await response.formData();
const file = fd.get('file1') as File;
return {
field1: fd.get('field1'),
filename: file instanceof File ? file.name : null,
fileContent: file instanceof File ? await file.text() : null,
};
});
expect(result.field1).toBe('value1');
expect(result.filename).toBe('test.txt');
expect(result.fileContent).toBe('hello');
});
Loading