Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
268 changes: 268 additions & 0 deletions plugins/core/skills/playwright-browser/API_REFERENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
# Playwright Advanced Patterns

Patterns beyond basics. For standard operations (click, fill, screenshot, selectors), use Playwright knowledge from training.

## Network Interception

### Mock API Responses

```javascript
await page.route('**/api/users', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Mock User' }])
});
});

// Modify requests
await page.route('**/api/**', route => {
route.continue({
headers: { ...route.request().headers(), 'X-Test': 'true' }
});
});

// Block resources
await page.route('**/*.{png,jpg,gif}', route => route.abort());
```

### Capture Network Traffic

```javascript
const requests = [];
page.on('request', req => requests.push({ url: req.url(), method: req.method() }));
page.on('response', res => console.log(res.status(), res.url()));

// Wait for specific response
const responsePromise = page.waitForResponse('**/api/data');
await page.click('button');
const response = await responsePromise;
const data = await response.json();
```

## Authentication State Persistence

```javascript
// Save auth state after login
await page.context().storageState({ path: '/tmp/auth.json' });

// Reuse in new context
const context = await browser.newContext({
storageState: '/tmp/auth.json'
});
```

## Multi-Tab Handling

```javascript
// Wait for popup
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.click('a[target="_blank"]')
]);
await popup.waitForLoadState();
console.log(await popup.title());

// Open new tab manually
const newPage = await context.newPage();
await newPage.goto('https://example.com');
```

## File Downloads

```javascript
const [download] = await Promise.all([
page.waitForEvent('download'),
page.click('button.download')
]);

const filePath = `/tmp/${download.suggestedFilename()}`;
await download.saveAs(filePath);
console.log('Downloaded to:', filePath);
```

## File Uploads

```javascript
// Single file
await page.setInputFiles('input[type="file"]', '/tmp/test.pdf');

// Multiple files
await page.setInputFiles('input[type="file"]', ['/tmp/a.pdf', '/tmp/b.pdf']);

// Clear files
await page.setInputFiles('input[type="file"]', []);
```

## Video Recording

```javascript
const context = await browser.newContext({
recordVideo: {
dir: '/tmp/videos/',
size: { width: 1280, height: 720 }
}
});

const page = await context.newPage();
// ... do things ...
await page.close();

const videoPath = await page.video().path();
console.log('Video saved:', videoPath);
```

## Trace Recording (Debugging)

```javascript
// Start trace
await context.tracing.start({ screenshots: true, snapshots: true });

// ... do things ...

// Save trace
await context.tracing.stop({ path: '/tmp/trace.zip' });
// View with: npx playwright show-trace /tmp/trace.zip
```

## iFrames

```javascript
const frame = page.frameLocator('#my-iframe');
await frame.locator('button').click();
await frame.locator('input').fill('text');
```

## Device Emulation

```javascript
const { devices } = require('playwright');

// Use predefined device
const context = await browser.newContext({
...devices['iPhone 14']
});

// Or custom viewport
const context = await browser.newContext({
viewport: { width: 375, height: 667 },
isMobile: true,
hasTouch: true
});
```

## Geolocation

```javascript
const context = await browser.newContext({
geolocation: { latitude: 37.7749, longitude: -122.4194 },
permissions: ['geolocation']
});
```

## Console and Error Capture

```javascript
page.on('console', msg => {
console.log(`[${msg.type()}] ${msg.text()}`);
});

page.on('pageerror', error => {
console.error('Page error:', error.message);
});
```

## Custom Headers (Global)

```javascript
const context = await browser.newContext({
extraHTTPHeaders: {
'X-Automated-By': 'playwright-skill',
'Authorization': 'Bearer token'
}
});
```

## Cookies

```javascript
// Set cookie
await context.addCookies([{
name: 'session',
value: 'abc123',
domain: 'example.com',
path: '/'
}]);

// Get cookies
const cookies = await context.cookies();

// Clear cookies
await context.clearCookies();
```

## localStorage/sessionStorage

```javascript
// Evaluate in page context
await page.evaluate(() => {
localStorage.setItem('key', 'value');
});

const value = await page.evaluate(() => localStorage.getItem('key'));
```

## Wait Strategies

```javascript
// Wait for function to return true
await page.waitForFunction(() => document.querySelector('.loaded') !== null);

// Wait for specific response
await page.waitForResponse(res =>
res.url().includes('/api/') && res.status() === 200
);

// Wait for navigation
await Promise.all([
page.waitForNavigation(),
page.click('a.nav-link')
]);
```

## Parallel Browser Contexts

```javascript
// Run multiple isolated sessions
const [context1, context2] = await Promise.all([
browser.newContext(),
browser.newContext()
]);

const [page1, page2] = await Promise.all([
context1.newPage(),
context2.newPage()
]);

// Each has separate cookies, storage, etc.
```

## PDF Generation

```javascript
await page.pdf({
path: '/tmp/page.pdf',
format: 'A4',
printBackground: true
});
```

## Dialogs (alert, confirm, prompt)

```javascript
page.on('dialog', async dialog => {
console.log('Dialog:', dialog.message());
await dialog.accept('input for prompt');
// or: await dialog.dismiss();
});
```
78 changes: 78 additions & 0 deletions plugins/core/skills/playwright-browser/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: playwright-browser
description: "Use for browser automation, testing pages, screenshots, UX validation"
version: 1.0.0
category: testing
triggers:
- "browser"
- "playwright"
- "screenshot"
- "test the page"
- "check the UI"
- "login flow"
- "fill form"
- "responsive"
- "viewport"
---

<objective>
Browser automation via Playwright. Write scripts, execute via run.js.
</objective>

<execution>
Write Playwright code to /tmp, execute from skill directory:

```bash
node $SKILL_DIR/run.js /tmp/playwright-task.js
```

For inline code (variables are auto-injected, see below):

```bash
node $SKILL_DIR/run.js "const b = await chromium.launch(); const p = await b.newPage(); await p.goto('http://localhost:3000'); console.log(await p.title()); await b.close();"
```

$SKILL_DIR is where you loaded this file from.
</execution>

<headless-vs-headed>
Default: headless (invisible, less intrusive).

Use headed when user wants to see the browser. You know when that is.

For headed: `PLAYWRIGHT_HEADED=true` env var or `{ headless: false }` in script.
</headless-vs-headed>

<defaults>
Screenshots to /tmp. Use `slowMo: 100` for debugging.
</defaults>

<injected-variables>
For inline code, these are available:

- `BASE_URL` - from PLAYWRIGHT_BASE_URL env var
- `HEADLESS` - true by default (set PLAYWRIGHT_HEADED=true for visible)
- `CI_ARGS` - browser args for CI (`['--no-sandbox', '--disable-setuid-sandbox']`)
- `EXTRA_HEADERS` - from PW_HEADER_NAME/VALUE or PW_EXTRA_HEADERS
- `chromium`, `firefox`, `webkit`, `devices` - from playwright

Example:
```bash
node $SKILL_DIR/run.js "
const browser = await chromium.launch({ headless: HEADLESS, args: CI_ARGS });
const page = await browser.newPage();
await page.goto(BASE_URL || 'http://localhost:3000');
console.log(await page.title());
await browser.close();
"
```
</injected-variables>

<auto-install>
run.js auto-installs Playwright on first use. No manual setup needed.
</auto-install>

<advanced-patterns>
For network mocking, auth persistence, multi-tab, downloads, video, traces:
[API_REFERENCE.md](API_REFERENCE.md)
</advanced-patterns>
12 changes: 12 additions & 0 deletions plugins/core/skills/playwright-browser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "playwright-browser-skill",
"version": "1.0.0",
"description": "Playwright browser automation skill for Claude Code",
"private": true,
"scripts": {
"setup": "npm install && npx playwright install chromium"
},
"dependencies": {
"playwright": "^1.49.0"
}
}
Loading
Loading