Browser automation with Playwright. For simple tasks (navigate, click, screenshot, check elements), use the Playwright MCP tools directly. For complex automation (multi-viewport testing, loops, network interception, data extraction), write scripts. Use when the user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based task.
From frontend-designnpx claudepluginhub aeriondyseti/aeriondyseti-plugins --plugin frontend-designThis skill uses the workspace's default tool permissions.
API_REFERENCE.mdlib/helpers.jspackage.jsonrun.jsDesigns and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
This skill provides two approaches to browser automation. Choose the right one for the task.
MCP tools are available as mcp__playwright__* tool calls. Key tools:
browser_navigate — go to a URLbrowser_click — click an element by refbrowser_type — type into a fieldbrowser_snapshot — get accessibility tree (structured page content with element refs)browser_take_screenshot — capture the page visuallybrowser_tab_list / browser_tab_new / browser_tab_select — manage tabsbrowser_select_option — interact with dropdownsbrowser_hover — hover over elementsbrowser_press_key — keyboard inputbrowser_wait — wait for a conditionMCP workflow:
browser_navigate to the URLbrowser_snapshot to see the page structure and get element refsbrowser_click, browser_type, etc.browser_take_screenshot to capture resultsScript workflow — see Script Execution below.
1. browser_navigate → http://localhost:3000
2. browser_snapshot → read the page structure
3. Report what you see
1. browser_navigate → http://localhost:3000/contact
2. browser_snapshot → find form fields and their refs
3. browser_click → [ref for name input]
4. browser_type → "John Doe"
5. browser_click → [ref for email input]
6. browser_type → "john@example.com"
7. browser_click → [ref for submit button]
8. browser_snapshot → verify success message
1. browser_navigate → http://localhost:3000
2. browser_take_screenshot → saves to file, view the result
Path Resolution: Determine $SKILL_DIR from where this SKILL.md was loaded.
cd $SKILL_DIR && npm run setup
For localhost testing, detect servers first:
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"
/tmp/playwright-test-*.js — never to skill directory or user's projectTARGET_URL constant at topheadless: false by default (visible browser)cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js// /tmp/playwright-test-responsive.js
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001';
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const viewports = [
{ name: 'Desktop', width: 1920, height: 1080 },
{ name: 'Tablet', width: 768, height: 1024 },
{ name: 'Mobile', width: 375, height: 667 },
];
for (const vp of viewports) {
await page.setViewportSize({ width: vp.width, height: vp.height });
await page.goto(TARGET_URL);
await page.waitForTimeout(1000);
await page.screenshot({ path: `/tmp/${vp.name.toLowerCase()}.png`, fullPage: true });
console.log(`${vp.name} screenshot saved`);
}
await browser.close();
})();
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001';
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
const links = await page.locator('a[href^="http"]').all();
const results = { working: 0, broken: [] };
for (const link of links) {
const href = await link.getAttribute('href');
try {
const response = await page.request.head(href);
if (response.ok()) results.working++;
else results.broken.push({ url: href, status: response.status() });
} catch (e) {
results.broken.push({ url: href, error: e.message });
}
}
console.log(`Working: ${results.working}, Broken: ${results.broken.length}`);
if (results.broken.length) console.log('Broken:', JSON.stringify(results.broken, null, 2));
await browser.close();
})();
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3001';
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/login`);
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForURL('**/dashboard');
console.log('Login successful, redirected to dashboard');
await browser.close();
})();
lib/helpers.js provides utilities for scripts:
const helpers = require('./lib/helpers');
await helpers.detectDevServers(); // Find running dev servers
await helpers.safeClick(page, selector); // Click with retry
await helpers.safeType(page, sel, text); // Type with clear
await helpers.takeScreenshot(page, name); // Timestamped screenshot
await helpers.handleCookieBanner(page); // Dismiss cookie banners
await helpers.extractTableData(page, sel); // Extract table as JSON
await helpers.createContext(browser); // Context with env headers
Set headers for all requests via environment variables:
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
cd $SKILL_DIR && node run.js /tmp/my-script.js
Or multiple headers: PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}'
For comprehensive Playwright API docs (selectors, network interception, auth, visual testing, mobile emulation, debugging, CI/CD), see API_REFERENCE.md.
cd $SKILL_DIR && npm run setuprun.jsheadless: false and display availabilityawait page.waitForSelector('.el', { timeout: 10000 })playwright MCP server is running (loaded from plugin's .mcp.json)