Write tests around user-visible behavior. Avoid implementation details.
When to Use
- Writing Playwright tests
- Reviewing test changes
- Fixing flaky tests
- Choosing locators, assertions, fixtures, or projects
- Debugging browser test failures
Goal
Create deterministic tests that act like users and fail for useful reasons.
Rules
- Prefer user-facing locators.
- Use web-first assertions.
- Trust Playwright auto-waiting.
- Do not use fixed sleeps.
- Keep tests isolated and order-independent.
- Control test data and external services.
- Mock third-party dependencies.
- Add cross-browser projects only when risk justifies it.
- Prefer clear duplication over clever abstraction.
Locator Order
getByRolegetByLabelgetByPlaceholdergetByTextgetByAltTextgetByTestId- Scoped
filter()chains
Avoid CSS classes, DOM position selectors, XPath, component names, and internal state.
Pattern
// Good
await page.getByRole('button', { name: 'Save for later' }).click()
await expect(page.getByRole('alert')).toHaveText('Saved')
// Bad
await page.locator('.buttonIcon').click()
await page.waitForTimeout(2000)
expect(await page.getByText('Saved').isVisible()).toBe(true)
Flow
- State user behavior under test.
- Create fresh state.
- Use user-facing locator.
- Perform user action.
- Assert visible result with
await expect. - Debug with trace, screenshot, video, or UI mode.
- Fix root cause.
Output
## Playwright Test Review
Behavior tested: [user behavior]
Locator quality: [good/issues]
Assertions: [web-first / issue]
Isolation: [fresh state / shared state issue]
Flake risk:
- [risk]
Fixes:
- [change]