Skip to main content

browser-automation

Common Playwright automation patterns for web testing and scraping via MCP

Source facts

Repository
code-yeongyu/sisyphus-private
Last source activity
October 20, 2025 at 01:49
Detected SKILL.md language
English
Stars
17
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
browser-automation
description
Common Playwright automation patterns for web testing and scraping via MCP
# Browser Automation with Playwright MCP This skill provides comprehensive patterns for browser automation using the Playwright MCP server. The Playwright MCP uses the accessibility tree for element interaction, which is more reliable and semantic than screenshot-based approaches. ## Overview Playwright MCP enables: - Reliable web navigation and page interactions - Form filling and data extraction - Element interaction via accessibility tree - Cross-browser testing automation - Network request monitoring and mocking The MCP server runs via `npx @playwright/mcp@latest` and provides tools accessible through the Claude Code interface. ## Navigation Patterns ### Basic Navigation Navigate to URLs and wait for page load events: ```javascript // Navigate to a URL await page.goto('https://example.com'); // Navigate with specific wait conditions await page.goto('https://example.com', { waitUntil: 'networkidle' // Wait until network is idle }); // Navigate and wait for a specific element await page.goto('https://example.com'); await page.waitForSelector('#main-content'); ``` ### Handling Page Load States ```javascript // Wait for DOM to be ready await page.waitForLoadState('domcontentloaded'); // Wait for full page load including resources await page.waitForLoadState('load'); // Wait for network to be idle (no requests for 500ms) await page.waitForLoadState('networkidle'); ``` ### Navigation with Authentication ```javascript // Navigate with HTTP authentication await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 30000 }); // Set authentication state await context.addCookies([ { name: 'session', value: 'abc123', domain: 'example.com', path: '/' } ]); ``` ### Handling Redirects and Popups ```javascript // Wait for navigation after click (handles redirects) await Promise.all([ page.waitForNavigation(), page.click('a[href="/login"]') ]); // Handle popup windows const [popup] = await Promise.all([ page.waitForEvent('popup'), page.click('button[data-action="open-popup"]') ]); await popup.waitForLoadState(); ``` ## Form Filling ### Text Input Fields ```javascript // Fill text input await page.fill('input[name="username"]', 'john.doe@example.com'); // Type with realistic delays (simulates human typing) await page.type('input[name="password"]', 'SecurePass123', { delay: 100 // 100ms between keystrokes }); // Clear and fill await page.fill('input[name="search"]', ''); await page.fill('input[name="search"]', 'new search term'); ``` ### Select Dropdowns ```javascript // Select by value await page.selectOption('select[name="country"]', 'US'); // Select by label await page.selectOption('select[name="country"]', { label: 'United States' }); // Select multiple options await page.selectOption('select[name="interests"]', ['coding', 'testing', 'automation']); // Select by index await page.selectOption('select[name="priority"]', { index: 2 }); ``` ### Checkboxes and Radio Buttons ```javascript // Check a checkbox await page.check('input[type="checkbox"][name="terms"]'); // Uncheck a checkbox await page.uncheck('input[type="checkbox"][name="newsletter"]'); // Select radio button await page.check('input[type="radio"][value="premium"]'); // Verify checkbox state const isChecked = await page.isChecked('input[name="terms"]'); ``` ### Complex Form Interactions ```javascript // Fill complete form await page.fill('input[name="firstName"]', 'John'); await page.fill('input[name="lastName"]', 'Doe'); await page.fill('input[name="email"]', 'john.doe@example.com'); await page.selectOption('select[name="country"]', 'US'); await page.check('input[name="terms"]'); // Submit form await page.click('button[type="submit"]'); // Wait for form submission to complete await page.waitForURL('**/success'); ``` ### File Uploads ```javascript // Upload single file await page.setInputFiles('input[type="file"]', 'path/to/file.pdf'); // Upload multiple files await page.setInputFiles('input[type="file"][multiple]', [ 'path/to/file1.jpg', 'path/to/file2.jpg' ]); // Clear file input await page.setInputFiles('input[type="file"]', []); ``` ## Data Extraction ### Text Content Extraction ```javascript // Extract text content const title = await page.textContent('h1.page-title'); // Extract inner text (visible text only) const description = await page.innerText('.product-description'); // Extract from multiple elements const prices = await page.$$eval('.product-price', elements => elements.map(el => el.textContent.trim()) ); ``` ### Attribute Extraction ```javascript // Get single attribute const imageUrl = await page.getAttribute('img.product-image', 'src'); // Get multiple attributes const linkData = await page.$eval('a.download-link', el => ({ href: el.getAttribute('href'), text: el.textContent, target: el.getAttribute('target') })); // Extract data attributes const productId = await page.getAttribute('[data-product-id]', 'data-product-id'); ``` ### Structured Data Extraction ```javascript // Extract table data const tableData = await page.$$eval('table.data-table tbody tr', rows => rows.map(row => { const cells = row.querySelectorAll('td'); return { name: cells[0]?.textContent.trim(), email: cells[1]?.textContent.trim(), role: cells[2]?.textContent.trim() }; }) ); // Extract list items const items = await page.$$eval('ul.items li', elements => elements.map(el => ({ text: el.textContent.trim(), id: el.getAttribute('data-id') })) ); ``` ### Pagination and Infinite Scroll ```javascript // Handle pagination const allData = []; let hasNextPage = true; while (hasNextPage) { // Extract current page data const pageData = await page.$$eval('.item', items => items.map(item => item.textContent.trim()) ); allData.push(...pageData); // Check for next page button const nextButton = await page.$('button.next-page:not([disabled])'); if (nextButton) { await nextButton.click(); await page.waitForLoadState('networkidle'); } else { hasNextPage = false; } } // Handle infinite scroll await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; const distance = 100; const timer = setInterval(() => { const scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } }, 100); }); }); ``` ## Element Interaction ### Clicking Elements ```javascript // Simple click await page.click('button.submit'); // Click with options await page.click('button.menu', { button: 'right', // Right-click clickCount: 2 // Double-click }); // Force click (bypass actionability checks) await page.click('.hidden-element', { force: true }); // Click and wait for navigation await Promise.all([ page.waitForNavigation(), page.click('a.external-link') ]); ``` ### Hovering and Focus ```javascript // Hover over element await page.hover('.menu-item'); // Focus on input await page.focus('input[name="search"]'); // Hover and click submenu await page.hover('.dropdown-menu'); await page.click('.dropdown-menu .submenu-item'); ``` ### Keyboard Interactions ```javascript // Press single key await page.press('input[name="search"]', 'Enter'); // Press key combination await page.press('body', 'Control+A'); // Type special characters await page.keyboard.press('ArrowDown'); await page.keyboard.press('Tab'); // Keyboard shortcuts await page.keyboard.press('Control+C'); // Copy await page.keyboard.press('Control+V'); // Paste ``` ### Drag and Drop ```javascript // Drag and drop await page.dragAndDrop('#source-element', '#target-element'); // Manual drag and drop with more control await page.hover('#draggable'); await page.mouse.down(); await page.hover('#drop-zone'); await page.mouse.up(); ``` ### Waiting for Elements ```javascript // Wait for element to appear await page.waitForSelector('.dynamic-content'); // Wait for element to be visible await page.waitForSelector('.modal', { state: 'visible' }); // Wait for element to be hidden await page.waitForSelector('.loading-spinner', { state: 'hidden' }); // Wait for element with timeout await page.waitForSelector('.slow-element', { timeout: 10000 }); ``` ### Element State Checks ```javascript // Check if element is visible const isVisible = await page.isVisible('.element'); // Check if element is enabled const isEnabled = await page.isEnabled('button.submit'); // Check if element is editable const isEditable = await page.isEditable('input[name="email"]'); // Get element count const count = await page.locator('.item').count(); ``` ## Accessibility Tree Usage The Playwright MCP leverages the accessibility tree for more reliable element interaction: ### Using ARIA Roles ```javascript // Find by role await page.click('button[role="button"]'); await page.click('[role="menuitem"]'); // Find by accessible name await page.getByRole('button', { name: 'Submit' }).click(); await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com'); ``` ### Using Labels ```javascript // Find input by label text await page.getByLabel('Username').fill('john.doe'); await page.getByLabel('Password').fill('SecurePass123'); await page.getByLabel('Remember me').check(); ``` ### Using Accessible Names ```javascript // Find by accessible name (aria-label, title, etc.) await page.getByText('Welcome to our site').click(); await page.getByTitle('Close dialog').click(); await page.getByAltText('Company logo').click(); ``` ## Best Practices
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub