Skip to content

Commit 424f2e6

Browse files
authored
chore(mcp): support config.ini (#39267)
1 parent 0ffb1d0 commit 424f2e6

File tree

15 files changed

+1365
-90
lines changed

15 files changed

+1365
-90
lines changed

.claude/skills/playwright-api/SKILL.md

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
name: playwright-dev
3+
description: Explains how to develop Playwright - add APIs, MCP tools, CLI commands, and vendor dependencies.
4+
---
5+
6+
# Playwright Development Guide
7+
8+
## Table of Contents
9+
10+
- [Adding and Modifying APIs](api.md) — define API docs, implement client/server, add tests
11+
- [MCP Tools and CLI Commands](mcp-dev.md) — add MCP tools, CLI commands, config options
12+
- [Vendoring Dependencies](vendor.md) — bundle third-party npm packages into playwright-core or playwright
13+
14+
## Build
15+
- Assume watch is running and everything is up to date.
16+
- If not, run `npm run build`.
17+
18+
## Lint
19+
- Run `npm run flint` to lint everything before commit.
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# Adding and Modifying APIs
2+
3+
- Before performing the implementation, go over the steps to understand and plan the work ahead. It is important to follow the steps in order, as some of them are prerequisites for others.
4+
5+
## Step 1: Define API in Documentation
6+
7+
Define (or update) API in `docs/src/api/class-xxx.md`. For the new methods, params and options use the version from package.json (without `-next`).
8+
9+
### Documentation Format
10+
11+
**Method definition:**
12+
```markdown
13+
## async method: Page.methodName
14+
* since: v1.XX
15+
- returns: <[null]|[Response]>
16+
17+
Description of the method.
18+
19+
### param: Page.methodName.paramName
20+
* since: v1.XX
21+
- `paramName` <[string]>
22+
23+
Description of the parameter.
24+
25+
### option: Page.methodName.optionName
26+
* since: v1.XX
27+
- `optionName` <[string]>
28+
29+
Description of the option.
30+
```
31+
32+
**Key syntax rules:**
33+
- `* since: v1.XX` — version from package.json (without -next)
34+
- `* langs: js, python` — language filter (optional)
35+
- `* langs: alias-java: navigate` — language-specific method name
36+
- `* deprecated: v1.XX` — deprecation marker
37+
- `<[TypeName]>` — type annotation: `<[string]>`, `<[int]>`, `<[float]>`, `<[boolean]>`
38+
- `<[null]|[Response]>` — union type
39+
- `<[Array]<[Locator]>>` — array type
40+
- `<[Object]>` with indented `- \`field\` <[type]>` — object type
41+
- `### param:` — required parameter
42+
- `### option:` — optional parameter
43+
- `= %%-placeholder-name-%%` — reuse shared param definition from `docs/src/api/params.md`
44+
45+
**Property definition:**
46+
```markdown
47+
## property: Page.propName
48+
* since: v1.XX
49+
- type: <[string]>
50+
51+
Description.
52+
```
53+
54+
**Event definition:**
55+
```markdown
56+
## event: Page.eventName
57+
* since: v1.XX
58+
- argument: <[Dialog]>
59+
60+
Description.
61+
```
62+
63+
Watch will kick in and auto-generate:
64+
- `packages/playwright-core/types/types.d.ts` — public API types
65+
- `packages/playwright/types/test.d.ts` — test API types
66+
67+
## Step 2: Implement Client API
68+
69+
Implement the new API in `packages/playwright-core/src/client/xxx.ts`.
70+
71+
### Client Implementation Pattern
72+
73+
Client classes extend `ChannelOwner<XxxChannel>` and call through `this._channel`:
74+
75+
```typescript
76+
// Direct channel call (most common)
77+
async methodName(param: string, options: channels.FrameMethodNameOptions = {}): Promise<void> {
78+
await this._channel.methodName({ param, ...options, timeout: this._timeout(options) });
79+
}
80+
81+
// Channel call with response wrapping
82+
async goto(url: string, options: channels.FrameGotoOptions = {}): Promise<network.Response | null> {
83+
return network.Response.fromNullable(
84+
(await this._channel.goto({ url, ...options, timeout: this._timeout(options) })).response
85+
);
86+
}
87+
```
88+
89+
**Key patterns:**
90+
- Parameters are assembled into a single object for the channel call
91+
- Timeout is processed through `this._timeout(options)` or `this._navigationTimeout(options)`
92+
- Return values from channel are unwrapped/converted: `Response.fromNullable()`, `ElementHandle.from()`, etc.
93+
- Locator methods delegate to Frame: `return await this._frame.click(this._selector, { strict: true, ...options })`
94+
- Page methods often delegate to `this._mainFrame`
95+
96+
## Step 3: Define Protocol Channel
97+
98+
Define (or update) channel for the API in `packages/protocol/src/protocol.yml` as needed.
99+
100+
### Protocol YAML Format
101+
102+
Methods are defined under `commands:` in the interface section:
103+
104+
```yaml
105+
Page:
106+
type: interface
107+
extends: EventTarget
108+
109+
commands:
110+
methodName:
111+
title: Short description for tracing
112+
parameters:
113+
url: string # required string
114+
timeout: float # required float
115+
referer: string? # optional string (? suffix)
116+
waitUntil: LifecycleEvent? # optional reference to another type
117+
button: # optional enum
118+
type: enum?
119+
literals:
120+
- left
121+
- right
122+
- middle
123+
modifiers: # optional array of enums
124+
type: array?
125+
items:
126+
type: enum
127+
literals:
128+
- Alt
129+
- Control
130+
- Meta
131+
- Shift
132+
position: Point? # optional reference type
133+
viewportSize: # required inline object
134+
type: object
135+
properties:
136+
width: int
137+
height: int
138+
returns:
139+
response: Response? # optional return value
140+
flags:
141+
slowMo: true
142+
snapshot: true
143+
pausesBeforeAction: true
144+
```
145+
146+
**Type primitives:** `string`, `int`, `float`, `boolean`, `binary`, `json`
147+
**Optional:** append `?` to any type: `string?`, `int?`, `object?`
148+
**Arrays:** `type: array` with `items:` (or `type: array?` for optional)
149+
**Enums:** `type: enum` with `literals:` list
150+
**References:** use type name directly: `Response`, `Frame`, `Point`
151+
**Flags:** `slowMo`, `snapshot`, `pausesBeforeAction`, `pausesBeforeInput`
152+
153+
Watch will kick in and auto-generate:
154+
- `packages/protocol/src/channels.d.ts` — channel TypeScript interfaces
155+
- `packages/playwright-core/src/protocol/validator.ts` — runtime validators
156+
- `packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts` — method metadata
157+
158+
## Step 4: Implement Dispatcher
159+
160+
Implement dispatcher handler in `packages/playwright-core/src/server/dispatchers/xxxDispatcher.ts` as needed.
161+
162+
### Dispatcher Pattern
163+
164+
Dispatchers receive validated params and route to server objects:
165+
166+
```typescript
167+
// Simple pass-through (most common)
168+
async methodName(params: channels.PageMethodNameParams, progress: Progress): Promise<void> {
169+
await this._page.methodName(progress, params.value);
170+
}
171+
172+
// With response wrapping
173+
async goto(params: channels.FrameGotoParams, progress: Progress): Promise<channels.FrameGotoResult> {
174+
return { response: ResponseDispatcher.fromNullable(this._browserContextDispatcher,
175+
await this._frame.goto(progress, params.url, params)) };
176+
}
177+
178+
// With dispatcher extraction (when params contain dispatcher references)
179+
async expectScreenshot(params: channels.PageExpectScreenshotParams, progress: Progress): Promise<channels.PageExpectScreenshotResult> {
180+
const mask = (params.mask || []).map(({ frame, selector }) => ({
181+
frame: (frame as FrameDispatcher)._object,
182+
selector,
183+
}));
184+
return await this._page.expectScreenshot(progress, { ...params, mask });
185+
}
186+
187+
// With array result wrapping
188+
async querySelectorAll(params: channels.FrameQuerySelectorAllParams, progress: Progress): Promise<channels.FrameQuerySelectorAllResult> {
189+
const elements = await progress.race(this._frame.querySelectorAll(params.selector));
190+
return { elements: elements.map(e => ElementHandleDispatcher.from(this, e)) };
191+
}
192+
```
193+
194+
**Key patterns:**
195+
- Method signature: `async method(params: channels.XxxMethodParams, progress: Progress): Promise<channels.XxxMethodResult>`
196+
- Extract params: `params.url`, `params.selector`, etc.
197+
- Convert dispatcher refs to server objects: `(params.frame as FrameDispatcher)._object`
198+
- Wrap server objects as dispatchers in results: `ResponseDispatcher.fromNullable()`, `ElementHandleDispatcher.from()`
199+
- All methods receive `Progress` for timeout/cancellation
200+
201+
## Step 5: Implement Server Logic
202+
203+
Handler should route the call into the corresponding method in `packages/playwright-core/src/server/xxx.ts`.
204+
205+
Server methods implement the actual browser interaction:
206+
207+
```typescript
208+
// In packages/playwright-core/src/server/frames.ts
209+
async goto(progress: Progress, url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
210+
// ... validation, URL construction ...
211+
// Delegates to browser-specific implementation:
212+
const result = await this._page.delegate.navigateFrame(this, url, referer);
213+
// ... wait for lifecycle events ...
214+
return response;
215+
}
216+
```
217+
218+
Browser-specific implementations live in:
219+
- `packages/playwright-core/src/server/chromium/crPage.ts` — Chromium (uses CDP: `this._client.send('Page.navigate', { ... })`)
220+
- `packages/playwright-core/src/server/firefox/ffPage.ts` — Firefox
221+
- `packages/playwright-core/src/server/webkit/wkPage.ts` — WebKit
222+
223+
## Step 6: Write Tests
224+
225+
### Test Location
226+
- Page-only tests: `tests/page/xxx.spec.ts` — use `page` fixture
227+
- Context tests: `tests/library/xxx.spec.ts` — use `context` fixture
228+
229+
### Test Patterns
230+
231+
**Page test:**
232+
```typescript
233+
import { test as it, expect } from './pageTest';
234+
235+
it('should do something @smoke', async ({ page, server }) => {
236+
await page.goto(server.EMPTY_PAGE);
237+
// ... assertions ...
238+
expect(page.url()).toBe(server.EMPTY_PAGE);
239+
});
240+
241+
it('should handle options', async ({ page, server, browserName, isAndroid }) => {
242+
it.skip(isAndroid, 'Not supported on Android');
243+
it.info().annotations.push({ type: 'issue', description: 'https://github.com/user/repo/issues/123' });
244+
// ...
245+
});
246+
```
247+
248+
**Library/context test:**
249+
```typescript
250+
import { contextTest as it, expect } from '../config/browserTest';
251+
252+
it('should work with context', async ({ context, server }) => {
253+
const page = await context.newPage();
254+
await page.goto(server.EMPTY_PAGE);
255+
// ...
256+
});
257+
```
258+
259+
### Available Fixtures
260+
- `page` — isolated page instance
261+
- `context` — browser context (library tests)
262+
- `server` — HTTP test server (`server.EMPTY_PAGE`, `server.PREFIX`, `server.CROSS_PROCESS_PREFIX`)
263+
- `httpsServer` — HTTPS test server
264+
- `asset(name)` — path to test asset file
265+
- `browserName` — `'chromium' | 'firefox' | 'webkit'`
266+
- `channel` — browser channel string
267+
- `isAndroid`, `isBidi`, `isElectron` — platform booleans
268+
- `isWindows`, `isMac`, `isLinux` — OS booleans
269+
- `mode` — test mode (`'default'`, `'service'`, etc.)
270+
271+
### Running Tests
272+
```bash
273+
npm run ctest tests/page/xxx.spec.ts # Chromium only
274+
npm run test tests/page/xxx.spec.ts # All browsers
275+
npm run ctest -- --grep "should do something" # Filter by name
276+
```
277+
278+
## Architecture Overview
279+
280+
```
281+
docs/src/api/class-xxx.md (API documentation — source of truth for public types)
282+
→ auto-generates → types.d.ts, test.d.ts
283+
284+
packages/protocol/src/protocol.yml (RPC protocol definition)
285+
→ auto-generates → channels.d.ts, validator.ts, protocolMetainfo.ts
286+
287+
Client call chain:
288+
user code → Page.method() → Frame.method() → this._channel.method(params)
289+
→ Proxy validates & sends → Connection.sendMessageToServer()
290+
[wire]
291+
DispatcherConnection.dispatch() → XxxDispatcher.method(params, progress)
292+
→ ServerObject.method(progress, ...) → BrowserDelegate (CDP/Firefox/WebKit)
293+
```

0 commit comments

Comments
 (0)