Browser Automation Skill
Expert in browser automation using Playwright, Puppeteer, and Selenium. Specializes in UI testing, web scraping, form automation, and automated workflows.
Expertise Areas
- Playwright Automation
-
Browser Control: Launch, navigate, interact with pages
-
Element Selection: CSS selectors, XPath, text-based, data-testid
-
Actions: Click, fill, select, hover, drag-and-drop
-
Waiting Strategies: waitForSelector, waitForNavigation, waitForTimeout
-
Network Interception: Mock APIs, block resources, modify requests
-
Screenshots & Videos: Full page, element-specific, video recording
- Testing Frameworks
-
End-to-End Testing: Playwright Test, Cypress-like workflows
-
Visual Regression: Screenshot comparison, pixel diff analysis
-
Accessibility Testing: ARIA validation, keyboard navigation
-
Performance Testing: Page load times, Core Web Vitals
-
Mobile Testing: Emulate devices, touch gestures
- Web Scraping
-
Data Extraction: Parse HTML, extract structured data
-
Pagination: Navigate through multi-page results
-
Dynamic Content: Handle lazy loading, infinite scroll
-
Authentication: Login flows, session management
-
Rate Limiting: Throttle requests, respect robots.txt
- Form Automation
-
Input Fields: Text, email, password, number inputs
-
Selections: Dropdowns, radio buttons, checkboxes
-
File Uploads: Single and multiple file uploads
-
Date Pickers: Custom date widgets
-
Multi-Step Forms: Wizard-style form flows
Code Examples
Basic Page Navigation
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); await page.goto('https://example.com', { waitUntil: 'networkidle' }); await page.screenshot({ path: 'screenshot.png', fullPage: true }); await browser.close();
Form Automation with Validation
// Fill and submit form await page.fill('input[name="email"]', 'test@example.com'); await page.fill('input[name="password"]', 'SecurePass123!'); await page.click('button[type="submit"]');
// Wait for success message const success = await page.waitForSelector('.success-message', { timeout: 5000 }); const message = await success.textContent(); console.log('Success:', message);
Data Extraction from Multiple Pages
const products = []; let page = 1;
while (page <= 10) {
await browser.goto(https://example.com/products?page=${page});
const items = await browser.$$eval('.product-item', (elements) => elements.map((el) => ({ title: el.querySelector('.title')?.textContent, price: el.querySelector('.price')?.textContent, image: el.querySelector('img')?.src, })) );
products.push(...items); page++; }
Network Interception
await page.route('/api/analytics', (route) => route.abort()); await page.route('/api/user', (route) => route.fulfill({ status: 200, body: JSON.stringify({ id: 1, name: 'Test User' }), }) );
Visual Regression Testing
import { expect } from '@playwright/test';
// Capture baseline await page.screenshot({ path: 'baseline.png' });
// After changes, compare const screenshot = await page.screenshot(); expect(screenshot).toMatchSnapshot('homepage.png');
Selector Strategies
- CSS Selectors (Preferred)
// ID selector (most reliable) await page.click('#submit-button');
// Data attribute (best practice) await page.click('[data-testid="login-button"]');
// Class selector await page.click('.btn-primary');
// Combined selector await page.click('button.submit[type="submit"]');
- XPath (When CSS isn't enough)
// Text-based selection await page.click('//button[contains(text(), "Submit")]');
// Complex hierarchy await page.click('//div[@class="form"]//input[@name="email"]');
- Playwright-Specific
// Text-based await page.getByText('Submit').click();
// Role-based (accessibility) await page.getByRole('button', { name: 'Submit' }).click();
// Label-based await page.getByLabel('Email address').fill('test@example.com');
// Placeholder-based await page.getByPlaceholder('Enter your email').fill('test@example.com');
Best Practices
- Use Stable Selectors
❌ Bad: .css-4j6h2k-button (auto-generated class) ✅ Good: [data-testid="submit-button"]
- Add Explicit Waits
❌ Bad: await page.waitForTimeout(3000);
✅ Good: await page.waitForSelector('.results', { state: 'visible' });
- Handle Errors Gracefully
try { await page.click('button', { timeout: 5000 }); } catch (error) { await page.screenshot({ path: 'error.png' }); console.error('Click failed:', error.message); throw error; }
- Clean Up Resources
try { // automation code } finally { await browser.close(); }
- Use Page Object Model (POM)
class LoginPage { constructor(private page: Page) {}
async login(email: string, password: string) { await this.page.fill('[data-testid="email"]', email); await this.page.fill('[data-testid="password"]', password); await this.page.click('[data-testid="submit"]'); }
async isLoggedIn() { return this.page.locator('[data-testid="dashboard"]').isVisible(); } }
Common Pitfalls
- Race Conditions
// ❌ Race condition await page.click('button'); const text = await page.textContent('.result'); // May fail!
// ✅ Wait for element await page.click('button'); await page.waitForSelector('.result'); const text = await page.textContent('.result');
- Stale Element References
// ❌ Element may become stale const element = await page.$('button'); await page.reload(); await element.click(); // Error: element detached from DOM
// ✅ Re-query after page changes await page.reload(); await page.click('button');
- Timing Issues with Dynamic Content
// ❌ Assumes immediate load await page.goto('https://example.com'); await page.click('.dynamic-content'); // May fail!
// ✅ Wait for dynamic content await page.goto('https://example.com'); await page.waitForLoadState('networkidle'); await page.click('.dynamic-content');
Debugging Tools
- Headful Mode
const browser = await chromium.launch({ headless: false, slowMo: 100 });
- Screenshot on Failure
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status !== 'passed') {
await page.screenshot({ path: failure-${testInfo.title}.png });
}
});
- Trace Recording
await context.tracing.start({ screenshots: true, snapshots: true }); await page.goto('https://example.com'); // ... automation steps await context.tracing.stop({ path: 'trace.zip' });
- Console Logs
page.on('console', (msg) => console.log('Browser log:', msg.text()));
Performance Optimization
- Block Unnecessary Resources
await page.route('**/*.{png,jpg,jpeg,gif,svg,css}', (route) => route.abort());
- Reuse Browser Contexts
const context = await browser.newContext(); const page1 = await context.newPage(); const page2 = await context.newPage(); // Share cookies, storage, etc.
- Parallel Execution
await Promise.all([ page1.goto('https://example.com/page1'), page2.goto('https://example.com/page2'), page3.goto('https://example.com/page3'), ]);
Activation Keywords
Ask me about:
-
"How do I automate browser testing with Playwright?"
-
"Web scraping with Playwright/Puppeteer"
-
"Screenshot automation and visual regression"
-
"Form filling automation"
-
"Element selection strategies"
-
"Handling dynamic content in web automation"
-
"Best practices for UI testing"
-
"Debugging Playwright tests"
Related Skills
-
E2E Testing: e2e-playwright skill
-
Frontend Development: frontend skill for understanding DOM structure
-
API Testing: api-testing skill for mocking network requests
Tools & Frameworks
-
Playwright: Modern browser automation (recommended)
-
Puppeteer: Chrome/Chromium-specific automation
-
Selenium: Legacy cross-browser automation
-
Playwright Test: Full testing framework
-
Cypress: Alternative E2E testing framework