Skill: onlyloveher/playwright-automation-v1 (version 1.0.0)

--- name: playwright-browser-automation description: Browser automation using Playwright API directly. Navigate websites, interact with elements, extract data, take screenshots, generate PDFs, record videos, and automate complex workflows. More reliable than MCP approach. metadata: {"openclaw":{"emoji":"🎭","os":["linux","darwin","win32"],"requires":{"bins":["node","npx"]},"install":[{"id":"npm-playwright","kind":"npm","package":"playwright","bins":["playwright"],"label":"Install Playwright"}]}} --- # Playwright Browser Automation Direct Playwright API for reliable browser automation without MCP complexity. ## Installation ```bash # Install Playwright npm install -g playwright # Install browsers (one-time, ~100MB each) npx playwright install chromium # Optional: npx playwright install firefox npx playwright install webkit # For system dependencies on Ubuntu/Debian: sudo npx playwright install-deps chromium ``` ## Quick Start ```javascript const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); await page.goto('https://example.com'); await page.screenshot({ path: 'screenshot.png' }); await browser.close(); })(); ``` ## Best Practices ### 1. Use Locators (Auto-waiting) ```javascript // ✅ GOOD: Uses auto-waiting and retries await page.getByRole('button', { name: 'Submit' }).click(); await page.getByLabel('Username').fill('user'); await page.getByPlaceholder('Search').fill('query'); // ❌ BAD: May fail if element not ready await page.click('#submit'); ``` ### 2. Prefer User-Facing Attributes ```javascript // ✅ GOOD: Resilient to DOM changes await page.getByRole('heading', { name: 'Welcome' }); await page.getByText('Sign in'); await page.getByTestId('login-button'); // ❌ BAD: Brittle CSS selectors await page.click('.btn-primary > div:nth-child(2)'); ``` ### 3. Handle Dynamic Content ```javascript // Wait for network idle await page.goto('https://spa-app.com', { waitUntil: 'networkidle' }); // Wait for specific element await page.waitForSelector('.results-loaded'); await page.waitForFunction(() => document.querySelectorAll('.item').length > 0); ``` ### 4. Use Contexts for Isolation ```javascript // Each context = isolated session (cookies, storage) const context = await browser.newContext(); const page = await context.newPage(); // Multiple pages in one context const page2 = await context.newPage(); ``` ### 5. Network Interception ```javascript // Mock API responses await page.route('**/api/users', route => { route.fulfill({ status: 200, body: JSON.stringify({ users: [] }) }); }); // Block resources await page.route('**/*.{png,jpg,css}', route => route.abort()); ``` ## Common Patterns ### Form Automation ```javascript // Fill form await page.goto('https://example.com/login'); await page.getByLabel('Username').fill('myuser'); await page.getByLabel('Password').fill('mypass'); await page.getByRole('button', { name: 'Sign in' }).click(); // Wait for navigation/result await page.waitForURL('/dashboard'); await expect(page.getByText('Welcome')).toBeVisible(); ``` ### Data Extraction ```javascript // Extract table data const rows = await page.$$eval('table tr', rows => rows.map(row => ({ name: row.querySelector('td:nth-child(1)')?.textContent, price: row.querySelector('td:nth-child(2)')?.textContent })) ); // Extract with JavaScript evaluation const data = await page.evaluate(() => { return Array.from(document.querySelectorAll('.product')).map(p => ({ title: p.querySelector('.title')?.textContent, price: p.querySelector('.price')?.textContent })); }); ``` ### Screenshots & PDFs ```javascript // Full page screenshot await page.screenshot({ path: 'full.png', fullPage: true }); // Element screenshot await page.locator('.chart').screenshot({ path: 'chart.png' }); // PDF (Chromium only) await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true }); ``` ### Video Recording ```javascript const context = await browser.newContext({ recordVideo: { dir: './videos/', size: { width: 1920, height: 1080 } } }); const page = await context.newPage(); // ... do stuff ... await context.close(); // Video saved automatically ``` ### Mobile Emulation ```javascript const context = await browser.newContext({ viewport: { width: 375, height: 667 }, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0)', isMobile: true, hasTouch: true }); ``` ### Authentication ```javascript // Method 1: HTTP Basic Auth const context = await browser.newContext({ httpCredentials: { username: 'user', password: 'pass' } }); // Method 2: Cookies await context.addCookies([ { name: 'session', value: 'abc123', domain: '.example.com', path: '/' } ]); // Method 3: Local Storage await page.evaluate(() => { localStorage.setItem('token', 'xyz'); }); // Method 4: Reuse auth state await context.storageState({ path: 'auth.json' }); // Later: await browser.newContext({ storageState: 'auth.json' }); ``` ## Advanced Features ### File Upload/Download ```javascript // Upload await page.setInputFiles('input[type="file"]', '/path/to/file.pdf'); // Download const [download] = await Promise.all([ page.waitForEvent('download'), page.click('a[download]') ]); await download.saveAs('/path/to/save/' + download.suggestedFilename()); ``` ### Dialogs Handling ```javascript page.on('dialog', dialog => { if (dialog.type() === 'alert') dialog.accept(); if (dialog.type() === 'confirm') dialog.accept(); if (dialog.type() === 'prompt') dialog.accept('My answer'); }); ``` ### Frames & Shadow DOM ```javascript // Frame by name const frame = page.frame('frame-name'); await frame.click('button'); // Frame by locator const frame = page.frameLocator('iframe').first(); await frame.getByRole('button').click(); // Shadow DOM await page.locator('my-component').locator('button').click(); ``` ### Tracing (Debug) ```javascript await context.tracing.start({ screenshots: true, snapshots: true }); // ... run tests ... await context.tracing.stop({ path: 'trace.zip' }); // View at https://trace.playwright.dev ``` ## Configuration Options ```javascript const browser = await chromium.launch({ headless: true, // Run without UI slowMo: 50, // Slow down by 50ms (for debugging) devtools: false, // Open DevTools args: ['--no-sandbox', '--disable-setuid-sandbox'] // Docker/Ubuntu }); const context = await browser.newContext({ viewport: { width: 1920, height: 1080 }, locale: 'ru-RU', timezoneId: 'Europe/Moscow', geolocation: { latitude: 55.7558, longitude: 37.6173 }, permissions: ['geolocation'], userAgent: 'Custom Agent', bypassCSP: true, // Bypass Content Security Policy }); ``` ## Error Handling ```javascript // Retry with timeout try { await page.getByRole('button', { name: 'Load' }).click({ timeout: 10000 }); } catch (e) { console.log('Button not found or not clickable'); } // Check if element exists const hasButton = await page.getByRole('button').count() > 0; // Wait with custom condition await page.waitForFunction(() => document.querySelectorAll('.loaded').length >= 10 ); ``` ## Sudoers Setup For Playwright browser installation: ```bash # /etc/sudoers.d/playwright username ALL=(root) NOPASSWD: /usr/bin/npx playwright install-deps * username ALL=(root) NOPASSWD: /usr/bin/npx playwright install * ``` ## References - [Playwright Docs](https://playwright.dev) - [API Reference](https://playwright.dev/docs/api/class-playwright) - [Best Practices](https://playwright.dev/docs/best-practices) - [Locators Guide](https://playwright.dev/docs/locators)

path: _meta.json
content: ownerId: kn73rfyfbb2nt729a6f911fx2980czrm
slug: playwright-browser-automation
version: 2.0.0
publishedAt: 1770569278871

path: examples.py
content: #!/usr/bin/env python3
"""Example script for using Playwright MCP server with OpenClaw.

This script demonstrates how to programmatically interact with
the Playwright MCP server for browser automation.
"""

import subprocess
import json
import sys


def run_mcp_command(tool_name: str, params: dict) -> dict:
    """Run a single MCP tool command via playwright-mcp.
    
    Note: In real usage with OpenClaw, the MCP server runs continuously
    and tools are called via the MCP protocol. This script shows the
    conceptual flow.
    """
    # Build MCP request
    request = {
        "jsonrpc": "2.0",
        "method": "tools/call",
        "params": {
            "name": tool_name,
            "arguments": params
        },
        "id": 1
    }
    
    # In real implementation, this would be sent to running MCP server
    # For now, we just print what would happen
    print(f"MCP Call: {tool_name}")
    print(f"Params: {json.dumps(params, indent=2)}")
    return {"status": "example", "tool": tool_name}


def example_navigate_and_click():
    """Example: Navigate to a page and click a button."""
    print("=== Example: Navigate and Click ===\n")
    
    # Step 1: Navigate
    run_mcp_command("browser_navigate", {
        "url": "https://example.com",
        "waitUntil": "networkidle"
    })
    
    # Step 2: Click element
    run_mcp_command("browser_click", {
        "selector": "button#submit",
        "timeout": 5000
    })
    
    # Step 3: Get text to verify
    run_mcp_command("browser_get_text", {
        "selector": ".result-message"
    })


def example_fill_form():
    """Example: Fill and submit a form."""
    print("\n=== Example: Fill Form ===\n")
    
    steps = [
        ("browser_navigate", {"url": "https://example.com/login"}),
        ("browser_type", {"selector": "#username", "text": "myuser"}),
        ("browser_type", {"selector": "#password", "text": "mypass"}),
        ("browser_click", {"selector": "button[type=submit]"}),
    ]
    
    for tool, params in steps:
        run_mcp_command(tool, params)


def example_extract_data():
    """Example: Extract data using JavaScript."""
    print("\n=== Example: Extract Data ===\n")
    
    run_mcp_command("browser_navigate", {
        "url": "https://example.com/products"
    })
    
    # Extract product data
    run_mcp_command("browser_evaluate", {
        "script": """
            () => {
                return Array.from(document.querySelectorAll('.product')).map(p => ({
                    name: p.querySelector('.name')?.textContent,
                    price: p.querySelector('.price')?.textContent
                }));
            }
        """
    })


def main():
    """Run examples."""
    print("Playwright MCP Usage Examples")
    print("=" * 50)
    print()
    print("Note: These are conceptual examples showing MCP tool calls.")
    print("In practice, OpenClaw manages the MCP server lifecycle.")
    print()
    
    example_navigate_and_click()
    example_fill_form()
    example_extract_data()
    
    print("\n" + "=" * 50)
    print("For actual usage, configure MCP server in OpenClaw config.")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)