120 lines
3.6 KiB
Markdown
120 lines
3.6 KiB
Markdown
---
|
|
name: e2e-testing
|
|
description: End-to-end testing strategy using Playwright or similar frameworks. Covers critical user journeys, visual regression, and cross-browser testing. Use when setting up E2E tests, writing E2E suites, or troubleshooting E2E failures.
|
|
---
|
|
|
|
# E2E Testing Skill
|
|
|
|
Based on ECC e2e-testing and Playwright patterns.
|
|
|
|
## When to Use
|
|
|
|
- Setting up E2E tests for a project
|
|
- Writing E2E test scenarios
|
|
- Fixing flaky E2E tests
|
|
- User requests E2E test implementation
|
|
|
|
## Strategy
|
|
|
|
### What to Test E2E
|
|
|
|
Test CRITICAL user journeys only. Not every function. E2E is slow and expensive.
|
|
|
|
Prioritize:
|
|
1. User signup and login flow
|
|
2. Core feature: the ONE thing this app is built for
|
|
3. Payment / checkout (if applicable)
|
|
4. Key data creation, reading, update, deletion
|
|
5. Error flow: what happens when things break
|
|
|
|
### What NOT to Test E2E
|
|
|
|
- Individual functions (unit tests)
|
|
- Internal API contracts (integration tests)
|
|
- UI component rendering (component tests)
|
|
- Edge cases that are hard to trigger (unit tests)
|
|
|
|
## Setup Pattern
|
|
|
|
### Playwright (Recommended)
|
|
|
|
```typescript
|
|
// tests/e2e/login.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Authentication', () => {
|
|
test('user can login with valid credentials', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.fill('[name="email"]', 'user@example.com');
|
|
await page.fill('[name="password"]', 'correct-password');
|
|
await page.click('button[type="submit"]');
|
|
await expect(page).toHaveURL('/dashboard');
|
|
});
|
|
|
|
test('login fails with wrong password', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.fill('[name="email"]', 'user@example.com');
|
|
await page.fill('[name="password"]', 'wrong');
|
|
await page.click('button[type="submit"]');
|
|
await expect(page.locator('[data-testid="error"]')).toBeVisible();
|
|
});
|
|
});
|
|
```
|
|
|
|
### Page Object Model (For Complex Apps)
|
|
|
|
```typescript
|
|
// tests/e2e/pages/LoginPage.ts
|
|
export class LoginPage {
|
|
constructor(private page: Page) {}
|
|
|
|
async goto() { await this.page.goto('/login'); }
|
|
async fillEmail(email: string) { await this.page.fill('[name="email"]', email); }
|
|
async fillPassword(password: string) { await this.page.fill('[name="password"]', password); }
|
|
async submit() { await this.page.click('button[type="submit"]'); }
|
|
async getError() { return this.page.locator('[data-testid="error"]'); }
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Independent tests**: Each test starts fresh (no shared state)
|
|
2. **Deterministic**: No flaky timing; use proper waits, not sleep
|
|
3. **Readable**: Test names describe behavior, not implementation
|
|
4. **Fast**: Parallelize when possible; limit browser contexts
|
|
5. **Data isolation**: Use test fixtures, not production data
|
|
6. **Screenshots on failure**: Configure in playwright.config.ts
|
|
|
|
```typescript
|
|
// playwright.config.ts
|
|
export default defineConfig({
|
|
use: {
|
|
screenshot: 'only-on-failure',
|
|
trace: 'retain-on-failure',
|
|
},
|
|
fullyParallel: true,
|
|
retries: process.env.CI ? 2 : 0,
|
|
});
|
|
```
|
|
|
|
## Common Anti-Patterns
|
|
|
|
- Testing every possible input combination (do unit tests instead)
|
|
- Hard-coded sleep/waitFixed (use proper waits on DOM conditions)
|
|
- Shared test data between tests (creates flakiness)
|
|
- Testing implementation details instead of user behavior
|
|
- Too many E2E tests (aim for 20-30 covering critical flows max)
|
|
|
|
## Report Format
|
|
|
|
```
|
|
## E2E Test Report
|
|
Tests: X pass / Y fail / Z skipped
|
|
Critical flows covered: [list]
|
|
Flaky tests detected: [list or "none"]
|
|
Screenshots saved: [path or "none"]
|
|
Browser: Chromium/Firefox/WebKit (version)
|
|
|
|
Status: All critical flows verified / Issues found: [list]
|
|
```
|