Playwright Skill
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check res...
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check res...
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
IMPORTANT - Path Resolution: This skill can be installed in different locations (plugin system, manual installation, global, or project-specific). Before executing any commands, determine the skill directory based on where you loaded this SKILL.md file, and use that path in all commands below. Replace
$SKILL_DIR with the actual discovered path.
Common installation paths:
~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill~/.claude/skills/playwright-skill<project>/.claude/skills/playwright-skillGeneral-purpose browser automation skill. I'll write custom Playwright code for any automation task you request and execute it via the universal executor.
CRITICAL WORKFLOW - Follow these steps in order:
Auto-detect dev servers - For localhost testing, ALWAYS run server detection FIRST:
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"
Write scripts to /tmp - NEVER write test files to skill directory; always use
/tmp/playwright-test-*.js
Use visible browser by default - Always use
headless: false unless user specifically requests headless mode
Parameterize URLs - Always make URLs configurable via environment variable or constant at top of script
/tmp/playwright-test-*.js (won't clutter your project)cd $SKILL_DIR && node run.js /tmp/playwright-test-*.jscd $SKILL_DIR npm run setup
This installs Playwright and Chromium browser. Only needed once.
Step 1: Detect dev servers (for localhost testing)
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"
Step 2: Write test script to /tmp with URL parameter
// /tmp/playwright-test-page.js const { chromium } = require('playwright');// Parameterized URL (detected or user-provided) const TARGET_URL = 'http://localhost:3001'; // <-- Auto-detected or from user
(async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage();
await page.goto(TARGET_URL); console.log('Page loaded:', await page.title());
await page.screenshot({ path: '/tmp/screenshot.png', fullPage: true }); console.log('📸 Screenshot saved to /tmp/screenshot.png');
await browser.close(); })();
Step 3: Execute from skill directory
cd $SKILL_DIR && node run.js /tmp/playwright-test-page.js
// /tmp/playwright-test-responsive.js const { chromium } = require('playwright');const TARGET_URL = 'http://localhost:3001'; // Auto-detected
(async () => { const browser = await chromium.launch({ headless: false, slowMo: 100 }); const page = await browser.newPage();
// Desktop test await page.setViewportSize({ width: 1920, height: 1080 }); await page.goto(TARGET_URL); console.log('Desktop - Title:', await page.title()); await page.screenshot({ path: '/tmp/desktop.png', fullPage: true });
// Mobile test await page.setViewportSize({ width: 375, height: 667 }); await page.screenshot({ path: '/tmp/mobile.png', fullPage: true });
await browser.close(); })();
// /tmp/playwright-test-login.js const { chromium } = require('playwright');const TARGET_URL = 'http://localhost:3001'; // Auto-detected
(async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage();
await page.goto(
);${TARGET_URL}/loginawait page.fill('input[name="email"]', 'test@example.com'); await page.fill('input[name="password"]', 'password123'); await page.click('button[type="submit"]');
// Wait for redirect await page.waitForURL('**/dashboard'); console.log('✅ Login successful, redirected to dashboard');
await browser.close(); })();
// /tmp/playwright-test-form.js const { chromium } = require('playwright');const TARGET_URL = 'http://localhost:3001'; // Auto-detected
(async () => { const browser = await chromium.launch({ headless: false, slowMo: 50 }); const page = await browser.newPage();
await page.goto(
);${TARGET_URL}/contactawait page.fill('input[name="name"]', 'John Doe'); await page.fill('input[name="email"]', 'john@example.com'); await page.fill('textarea[name="message"]', 'Test message'); await page.click('button[type="submit"]');
// Verify submission await page.waitForSelector('.success-message'); console.log('✅ Form submitted successfully');
await browser.close(); })();
const { chromium } = require('playwright');(async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage();
await page.goto('http://localhost:3000');
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(
); console.log(✅ Working links: ${results.working}, results.broken);❌ Broken links:await browser.close(); })();
const { chromium } = require('playwright');(async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage();
try { await page.goto('http://localhost:3000', { waitUntil: 'networkidle', timeout: 10000, });
await page.screenshot({ path: '/tmp/screenshot.png', fullPage: true, }); console.log('📸 Screenshot saved to /tmp/screenshot.png');} catch (error) { console.error('❌ Error:', error.message); } finally { await browser.close(); } })();
// /tmp/playwright-test-responsive-full.js const { chromium } = require('playwright');const TARGET_URL = 'http://localhost:3001'; // Auto-detected
(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 viewport of viewports) { console.log(
, );Testing ${viewport.name} (${viewport.width}x${viewport.height})await page.setViewportSize({ width: viewport.width, height: viewport.height, }); await page.goto(TARGET_URL); await page.waitForTimeout(1000); await page.screenshot({ path: `/tmp/${viewport.name.toLowerCase()}.png`, fullPage: true, });}
console.log('✅ All viewports tested'); await browser.close(); })();
For quick one-off tasks, you can execute code inline without creating files:
# Take a quick screenshot cd $SKILL_DIR && node run.js " const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); await page.goto('http://localhost:3001'); await page.screenshot({ path: '/tmp/quick-screenshot.png', fullPage: true }); console.log('Screenshot saved'); await browser.close(); "
When to use inline vs files:
Optional utility functions in
lib/helpers.js:
const helpers = require('./lib/helpers');// Detect running dev servers (CRITICAL - use this first!) const servers = await helpers.detectDevServers(); console.log('Found servers:', servers);
// Safe click with retry await helpers.safeClick(page, 'button.submit', { retries: 3 });
// Safe type with clear await helpers.safeType(page, '#username', 'testuser');
// Take timestamped screenshot await helpers.takeScreenshot(page, 'test-result');
// Handle cookie banners await helpers.handleCookieBanner(page);
// Extract table data const data = await helpers.extractTableData(page, 'table.results');
See
lib/helpers.js for full list.
Configure custom headers for all HTTP requests via environment variables. Useful for:
Single header (common case):
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \ cd $SKILL_DIR && node run.js /tmp/my-script.js
Multiple headers (JSON format):
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}' \ cd $SKILL_DIR && node run.js /tmp/my-script.js
Headers are automatically applied when using
helpers.createContext():
const context = await helpers.createContext(browser); const page = await context.newPage(); // All requests from this page include your custom headers
For scripts using raw Playwright API, use the injected
getContextOptionsWithHeaders():
const context = await browser.newContext( getContextOptionsWithHeaders({ viewport: { width: 1920, height: 1080 } }), );
For comprehensive Playwright API documentation, see API_REFERENCE.md:
detectDevServers() before writing test code for localhost testingPW_HEADER_NAME/PW_HEADER_VALUE env vars to identify automated traffic to your backend/tmp/playwright-test-*.js, never to skill directory or user's projectTARGET_URL constant at the top of every scriptheadless: false unless user explicitly asks for headless modeheadless: true when user specifically requests "headless" or "background" executionslowMo: 100 to make actions visible and easier to followwaitForURL, waitForSelector, waitForLoadState instead of fixed timeoutsconsole.log() to track progress and show what's happeningPlaywright not installed:
cd $SKILL_DIR && npm run setup
Module not found: Ensure running from skill directory via
run.js wrapper
Browser doesn't open: Check
headless: false and ensure display available
Element not found: Add wait:
await page.waitForSelector('.element', { timeout: 10000 })
User: "Test if the marketing page looks good"Claude: I'll test the marketing page across multiple viewports. Let me first detect running servers... [Runs: detectDevServers()] [Output: Found server on port 3001] I found your dev server running on http://localhost:3001
[Writes custom automation script to /tmp/playwright-test-marketing.js with URL parameterized] [Runs: cd $SKILL_DIR && node run.js /tmp/playwright-test-marketing.js] [Shows results with screenshots from /tmp/]
User: "Check if login redirects correctly" Claude: I'll test the login flow. First, let me check for running servers... [Runs: detectDevServers()] [Output: Found servers on ports 3000 and 3001] I found 2 dev servers. Which one should I test? - http://localhost:3000 - http://localhost:3001 User: "Use 3001" [Writes login automation to /tmp/playwright-test-login.js] [Runs: cd $SKILL_DIR && node run.js /tmp/playwright-test-login.js] [Reports: ✅ Login successful, redirected to /dashboard]
/tmp for automatic cleanup (no clutter)run.jsNo automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.