E2E Testing with Playwright
Use Playwright MCP tools for browser automation and E2E testing.
Quick Start
/test:e2e run # Run existing tests
/test:e2e record # Record browser session
/test:e2e generate # Generate test from URL
Spawn Agent
Task(subagent_type="playwright-tester", prompt="<task>")
Key Tools
| Tool | Purpose |
| -------------------------- | ---------------------- |
| browser_navigate | Go to URL |
| browser_snapshot | Get accessibility tree |
| browser_click | Click elements |
| browser_type | Type text |
| browser_fill_form | Fill form fields |
| browser_generate_locator | Get best locator |
| browser_verify_text | Assert text content |
Supported Stacks
- TypeScript: Playwright Test with Page Objects
- Go/HTMX: Test HTMX interactions, form submissions, partial updates
HTMX Testing Tips
- Use
browser_snapshotto verify DOM updates after HTMX swaps - Test
hx-trigger,hx-swap,hx-targetbehaviors - Verify
HX-*response headers in network requests - Assert partial page updates without full reload
Error Scenarios & Handling
Element Not Found
// BAD: Immediate failure
await page.click("#submit-btn");
// GOOD: Wait with timeout + fallback
const btn = page.locator("#submit-btn");
if ((await btn.count()) === 0) {
// Try alternative selector
await page.click('button[type="submit"]');
} else {
await btn.click();
}
Recovery strategies:
- Use
browser_snapshotto inspect current DOM state - Try alternative locators (text, role, data-testid)
- Check if element is in iframe/shadow DOM
- Verify page loaded correctly (check URL, title)
Timeout Errors
| Error | Cause | Solution | | ------------------ | ------------------------ | --------------------------------- | | Navigation timeout | Slow page load | Increase timeout, check network | | Action timeout | Element not interactable | Wait for visibility/enabled state | | Expect timeout | Assertion failed | Verify DOM state with snapshot |
// Configure timeouts
test.setTimeout(60000); // Test timeout
page.setDefaultTimeout(30000); // Action timeout
// Or per-action
await page.click("#btn", { timeout: 10000 });
Network Issues
// Wait for network idle
await page.waitForLoadState("networkidle");
// Mock failing endpoints
await page.route("**/api/**", (route) => {
route.fulfill({ status: 500, body: "Server Error" });
});
Flaky Test Patterns
Avoid:
- Fixed
page.waitForTimeout(1000)delays - Brittle selectors like
.btn-23 - Tests depending on animation timing
Prefer:
waitForSelector,waitForLoadState- Role/text-based selectors:
getByRole('button', { name: 'Submit' }) - Retry patterns for known flaky operations
Debugging Failed Tests
- Get snapshot:
browser_snapshotshows accessibility tree - Screenshot: Capture current visual state
- Console logs: Check browser console for JS errors
- Network tab: Verify API calls succeeded
- Trace: Enable Playwright trace for post-mortem
# Run with trace
npx playwright test --trace on
# View trace
npx playwright show-trace trace.zip