Agent Skills for Claude Code | Playwright Expert
| Domain | Quality & Testing |
| Role | specialist |
| Scope | testing |
| Output | code |
Triggers: Playwright, E2E test, end-to-end, browser testing, automation, UI testing, visual testing
Related Skills: Test Master · React Expert · DevOps Engineer
E2E testing specialist with deep expertise in Playwright for robust, maintainable browser automation.
Core Workflow
Section titled “Core Workflow”- Analyze requirements - Identify user flows to test
- Setup - Configure Playwright with proper settings
- Write tests - Use POM pattern, proper selectors, auto-waiting
- Debug - Run test → check trace → identify issue → fix → verify fix
- Integrate - Add to CI/CD pipeline
Reference Guide
Section titled “Reference Guide”Load detailed guidance based on context:
| Topic | Reference | Load When |
|---|---|---|
| Selectors | references/selectors-locators.md | Writing selectors, locator priority |
| Page Objects | references/page-object-model.md | POM patterns, fixtures |
| API Mocking | references/api-mocking.md | Route interception, mocking |
| Configuration | references/configuration.md | playwright.config.ts setup |
| Debugging | references/debugging-flaky.md | Flaky tests, trace viewer |
Constraints
Section titled “Constraints”MUST DO
Section titled “MUST DO”- Use role-based selectors when possible
- Leverage auto-waiting (don’t add arbitrary timeouts)
- Keep tests independent (no shared state)
- Use Page Object Model for maintainability
- Enable traces/screenshots for debugging
- Run tests in parallel
MUST NOT DO
Section titled “MUST NOT DO”- Use
waitForTimeout()(use proper waits) - Rely on CSS class selectors (brittle)
- Share state between tests
- Ignore flaky tests
- Use
first(),nth()without good reason
Code Examples
Section titled “Code Examples”Selector: Role-based (correct) vs CSS class (brittle)
Section titled “Selector: Role-based (correct) vs CSS class (brittle)”// ✅ Role-based selector — resilient to styling changesawait page.getByRole('button', { name: 'Submit' }).click();await page.getByLabel('Email address').fill('user@example.com');
// ❌ CSS class selector — breaks on refactorawait page.locator('.btn-primary.submit-btn').click();await page.locator('.email-input').fill('user@example.com');Page Object Model + Test File
Section titled “Page Object Model + Test File”import { type Page, type Locator } from '@playwright/test';
export class LoginPage { readonly page: Page; readonly emailInput: Locator; readonly passwordInput: Locator; readonly submitButton: Locator; readonly errorMessage: Locator;
constructor(page: Page) { this.page = page; this.emailInput = page.getByLabel('Email address'); this.passwordInput = page.getByLabel('Password'); this.submitButton = page.getByRole('button', { name: 'Sign in' }); this.errorMessage = page.getByRole('alert'); }
async goto() { await this.page.goto('/login'); }
async login(email: string, password: string) { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); }}import { test, expect } from '@playwright/test';import { LoginPage } from '../pages/LoginPage';
test.describe('Login', () => { let loginPage: LoginPage;
test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); await loginPage.goto(); });
test('successful login redirects to dashboard', async ({ page }) => { await loginPage.login('user@example.com', 'correct-password'); await expect(page).toHaveURL('/dashboard'); });
test('invalid credentials shows error', async () => { await loginPage.login('user@example.com', 'wrong-password'); await expect(loginPage.errorMessage).toBeVisible(); await expect(loginPage.errorMessage).toContainText('Invalid credentials'); });});Debugging Workflow for Flaky Tests
Section titled “Debugging Workflow for Flaky Tests”// 1. Run failing test with trace enableduse: { trace: 'on-first-retry', screenshot: 'only-on-failure',}
// 2. Re-run with retries to capture trace// npx playwright test --retries=2
// 3. Open trace viewer to inspect timeline// npx playwright show-trace test-results/.../trace.zip
// 4. Common fix — replace arbitrary timeout with proper wait// ❌ Flakyawait page.waitForTimeout(2000);await page.getByRole('button', { name: 'Save' }).click();
// ✅ Reliable — waits for element stateawait page.getByRole('button', { name: 'Save' }).waitFor({ state: 'visible' });await page.getByRole('button', { name: 'Save' }).click();
// 5. Verify fix — run test 10x to confirm stability// npx playwright test --repeat-each=10Output Templates
Section titled “Output Templates”When implementing Playwright tests, provide:
- Page Object classes
- Test files with proper assertions
- Fixture setup if needed
- Configuration recommendations
Knowledge Reference
Section titled “Knowledge Reference”Playwright, Page Object Model, auto-waiting, locators, fixtures, API mocking, trace viewer, visual comparisons, parallel execution, CI/CD integration