Webapp Testing (Playwright E2E)
Write comprehensive E2E tests using Playwright with a reconnaissance-then-action approach.
Phase 1: Reconnaissance
Before writing any tests, map the application:
- •Discover routes: Read the routing config, sitemap, or navigate manually
- •Catalog pages: List every page with its URL pattern and purpose
- •Identify flows: Map key user journeys (auth, CRUD operations, navigation)
- •Note interactive elements: Forms, buttons, modals, dropdowns, tabs
- •Check data requirements: What test data/fixtures are needed?
Output a test plan before writing code:
code
Page Map: / (Home) - hero, nav, CTA buttons /login - email/password form, OAuth buttons /dashboard - data table, filters, pagination /settings - form with multiple tabs User Flows: 1. Sign up → verify email → first login → onboarding 2. Create item → edit → delete 3. Search → filter → paginate → select
Phase 2: Page Object Model
Create page objects for each key page:
typescript
// pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
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 expectError(message: string) {
await expect(this.page.getByText(message)).toBeVisible();
}
}
Phase 3: Write Tests
For each user flow, write tests covering:
- •Happy path: The expected successful flow
- •Error states: Invalid input, network errors, edge cases
- •Navigation: Back button, direct URL access, deep links
Phase 4: Artifacts
Collect artifacts automatically:
- •Screenshots on failure:
await page.screenshot({ path: 'artifacts/failure.png' }) - •Traces:
await context.tracing.start({ screenshots: true, snapshots: true }) - •Videos: Configure in
playwright.config.tsfor CI
Selector Strategy
Prefer in this order:
- •
data-testidattributes (most stable) - •
getByRole(accessible, semantic) - •
getByText(user-visible text) - •CSS selectors (last resort)
Rules
- •NEVER use
page.waitForTimeout()— usepage.waitForSelector()orexpect().toBeVisible() - •NEVER hardcode URLs — use relative paths or config
- •Clean up test data after each test
- •Tests must be independent — no shared state between tests
- •Run in CI with
--retries 2for flaky network tests