Skip to main content

browser-automation

Common Playwright automation patterns for web testing and scraping via MCP

소스 정보

저장소
code-yeongyu/sisyphus-private
최근 소스 활동
2025년 10월 20일 01:49
감지된 SKILL.md 언어
영어
스타
17
포크
2

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기