Playwright best practices including selectors, wait strategies, accessibility testing, responsive design, and flaky-test prevention. Use when writing or improving Playwright E2E tests.
Provides Playwright best practices for reliable E2E tests. Use when writing or improving tests to get guidance on semantic selectors, proper wait strategies, accessibility testing, responsive design, and preventing flaky tests.
/plugin marketplace add jovermier/cc-stack-marketplace/plugin install cc-playwright@cc-stack-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Expert guidance for writing reliable, maintainable Playwright tests.
| Concern | Best Practice | Avoid |
|---|---|---|
| Selectors | getByRole, getByTestId, getByLabel | CSS classes, DOM structure |
| Waits | Auto-waiting, explicit assertions | waitForTimeout, hardcoded delays |
| Accessibility | getByRole, a11y checks | Visual-only testing |
| Flaky tests | Proper waits, stable selectors | Timing-dependent assertions |
| Isolation | Cleanup after each test | Tests depending on each other |
| Parallel | Independent tests | Shared state |
Specify a number or describe your testing concern.
| Response | Reference to Read |
|---|---|
| 1, "selector", "locator", "getBy" | selectors.md |
| 2, "wait", "timeout", "delay" | waits.md |
| 3, "a11y", "accessibility", "keyboard" | accessibility.md |
| 4, "responsive", "mobile", "viewport" | responsive.md |
| 5, "flaky", "unstable", "retry" | flaky-tests.md |
Use semantic selectors: getByRole, getByLabel, getByTestId are stable. CSS classes and DOM structure change frequently.
Never waitForTimeout: Hardcoded delays make tests slow and flaky. Use auto-waiting and explicit assertions.
Test accessibility: getByRole ensures accessible markup. Keyboard tests verify a11y.
Isolate tests: Each test should work independently. Clean up test data after each test.
Responsive testing: Test mobile, tablet, desktop viewports.
// ❌ Bad: Fragile selectors
page.click('div > div > button')
page.click('.btn-primary')
page.click('#submit-btn-123')
// ✅ Good: Stable, semantic selectors
page.getByRole('button', { name: 'Submit' })
page.getByTestId('submit-button')
page.getByLabel('Email address')
// ❌ Bad: Hardcoded waits
page.waitForTimeout(5000) // Flaky, slow
// ✅ Good: Explicit waits
await page.waitForURL('/dashboard')
await page.waitForSelector('[data-testid="success-message"]')
await expect(page.getByTestId('loading')).toBeHidden()
await page.waitForResponse(resp => resp.url().includes('/api/users') && resp.status() === 200)
// Good: Semantic selectors enforce a11y
await page.getByRole('button', { name: 'Submit' }).click()
// Good: Keyboard navigation test
test('is keyboard navigable', async ({ page }) => {
await page.goto('/form')
await page.keyboard.press('Tab')
await expect(page.getByTestId('name-input')).toBeFocused()
})
// Good: A11y assertions (with axe-core)
await expect(page).toHaveAccessibleTree()
test.describe('Mobile', () => {
test.use({ viewport: { width: 375, height: 667 } })
test('shows mobile menu', async ({ page }) => {
await page.goto('/')
await expect(page.getByTestId('hamburger-menu')).toBeVisible()
})
})
| Anti-Pattern | Severity | Fix |
|---|---|---|
| waitForTimeout | Critical | Use explicit waits/assertions |
| CSS class selectors | High | Use getByRole/getByTestId |
| Tests depending on each other | High | Make tests independent |
| No cleanup | Medium | Use fixtures with proper cleanup |
| Only desktop testing | Low | Test multiple viewports |
| Hardcoded test data | Medium | Use data factories |
| File | Topics |
|---|---|
| selectors.md | getByRole, getByTestId, getByLabel |
| waits.md | Auto-waiting, explicit assertions |
| accessibility.md | A11y checks, keyboard navigation |
| responsive.md | Viewports, devices, mobile testing |
| flaky-tests.md | Isolation, retries, debugging |
Tests are reliable when:
Master authentication and authorization patterns including JWT, OAuth2, session management, and RBAC to build secure, scalable access control systems. Use when implementing auth systems, securing APIs, or debugging security issues.