Browser automation with Playwright — testing, scraping, UI validation, form filling, screenshot capture. Use when automating browser interactions, testing web applications, or capturing screenshots.
npx claudepluginhub francoisbgdw/claude-skillsThis skill is limited to using the following tools:
Generate and execute Playwright scripts on-the-fly for browser automation, testing, and validation.
Automates browser tasks via Playwright CLI wrapper: navigate sites, snapshot elements, fill forms, click/type, screenshot, trace, extract data, debug UI flows from terminal.
Automates browser tasks via Playwright CLI for AI agents: navigate pages, take snapshots/screenshots, fill forms, click elements from command line. Use with shell access.
Automates browsers via Playwright CLI shell commands: navigate pages, interact with elements (click, fill, type), capture screenshots/snapshots/PDFs, manage tabs for web testing.
Share bugs, ideas, or general feedback.
Generate and execute Playwright scripts on-the-fly for browser automation, testing, and validation.
Before first use, install dependencies from the skill directory:
cd ${CLAUDE_SKILL_DIR} && npm run setup
This installs Playwright and the Chromium browser.
node ${CLAUDE_SKILL_DIR}/run.js <script-path>
The executor handles module resolution, browser lifecycle, and cleanup.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 100 });
const page = await browser.newPage();
try {
// Navigate
await page.goto('https://example.com');
// Interact
await page.fill('#username', 'user@example.com');
await page.fill('#password', 'password');
await page.click('button[type="submit"]');
// Wait for result
await page.waitForSelector('.dashboard');
// Screenshot
await page.screenshot({ path: '/tmp/result.png', fullPage: true });
console.log('SUCCESS: Page loaded and screenshot captured');
// Extract data
const title = await page.title();
console.log(`Page title: ${title}`);
} catch (error) {
await page.screenshot({ path: '/tmp/error.png' });
console.error(`FAILED: ${error.message}`);
} finally {
await browser.close();
}
})();
Instead of guessing fragile CSS selectors, use the accessibility tree to discover elements and interact via role-based locators. This is more robust against DOM changes, React hydration, and Shadow DOM.
// Get the full accessibility tree with roles and names
const snapshot = await page.accessibility.snapshot();
console.log(JSON.stringify(snapshot, null, 2));
This returns a tree of elements with their roles (button, link, textbox, heading, etc.) and accessible names.
// Instead of: page.click('button.submit-btn')
// Use:
await page.getByRole('button', { name: 'Submit' }).click();
// Instead of: page.fill('input#email')
// Use:
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
// Instead of: page.click('a.nav-link')
// Use:
await page.getByRole('link', { name: 'Dashboard' }).click();
// Instead of: page.click('input[type="checkbox"]')
// Use:
await page.getByRole('checkbox', { name: 'Remember me' }).check();
getByRole — uses ARIA roles and accessible names. Most resilient to DOM changes.getByLabel — finds form inputs by their associated label text.getByPlaceholder — finds inputs by placeholder text.getByText — finds elements by visible text content.getByTestId — finds elements by data-testid attribute (requires app support).When you don't know what elements are on the page, take a snapshot first:
// Discover all interactive elements
const snapshot = await page.accessibility.snapshot();
function listInteractive(node, depth = 0) {
const interactive = ['button', 'link', 'textbox', 'checkbox', 'radio',
'combobox', 'menuitem', 'tab', 'switch'];
if (interactive.includes(node.role)) {
console.log(`${' '.repeat(depth)}[${node.role}] "${node.name}"`);
}
if (node.children) {
for (const child of node.children) {
listInteractive(child, depth + 1);
}
}
}
listInteractive(snapshot);
Output example:
[link] "Home"
[link] "Dashboard"
[textbox] "Search"
[button] "Submit"
[checkbox] "Remember me"
Then interact using the discovered roles and names:
await page.getByRole('link', { name: 'Dashboard' }).click();
await page.goto(url);
await page.waitForLoadState('networkidle');
await page.screenshot({ path: '/tmp/screenshot.png', fullPage: true });
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
await page.getByRole('combobox', { name: 'Country' }).selectOption('US');
await page.getByRole('checkbox', { name: 'Terms' }).check();
await page.getByRole('button', { name: 'Submit' }).click();
await page.waitForSelector('.result', { state: 'visible', timeout: 10000 });
await page.waitForURL('**/dashboard');
await page.waitForResponse(resp => resp.url().includes('/api/data'));
const text = await page.textContent('.selector');
const items = await page.$$eval('.list-item', els => els.map(el => el.textContent));
const attr = await page.getAttribute('a.link', 'href');
// Basic auth
const context = await browser.newContext({
httpCredentials: { username: 'user', password: 'pass' }
});
// Cookie-based (set cookies before navigation)
await context.addCookies([{ name: 'session', value: 'token', domain: '.example.com', path: '/' }]);
const [newPage] = await Promise.all([
context.waitForEvent('page'),
page.click('a[target="_blank"]')
]);
await newPage.waitForLoadState();
For multi-step workflows that require many browser interactions, keep the browser alive across commands instead of cold-starting each time:
const { chromium } = require('playwright');
// Launch once, reuse across interactions
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
// ... perform multiple interactions without closing ...
// Only close when fully done
await browser.close();
When writing multi-step automation scripts, structure them as a single script with sequential steps rather than launching/closing the browser for each step. This is faster and preserves state (cookies, localStorage, session).
Use Playwright's codegen to record interactions:
npx playwright codegen https://example.com
This opens a browser and records your actions as Playwright code.
getByRole, getByLabel) over CSS selectors for resilienceheadless: false during development to see what's happeningslowMo: 100 to slow down actions for debuggingpage.waitForLoadState('networkidle') after navigation/tmp/playwright-* files after use