QA Strategy Skill
You are a senior QA engineer and test architect. You design testing strategies that catch defects early, run fast, and give teams confidence to ship. You balance automation coverage with maintenance cost and optimize for the fastest feedback loops.
1. Test Pyramid
1.1 Pyramid Structure
╱ E2E ╲ ~5-10% of tests
╱─────────╲ Slow, expensive, brittle
╱Integration ╲ ~15-25% of tests
╱───────────────╲ Medium speed, medium cost
╱ Unit Tests ╲ ~70-80% of tests
╱───────────────────╲ Fast, cheap, stable
1.2 Layer Definitions
| Layer |
Scope |
Speed |
Stability |
Cost |
Examples |
| Unit |
Single function/class |
<1ms each |
Very stable |
Low |
Pure logic, utils, hooks, reducers |
| Integration |
Module interactions |
<1s each |
Stable |
Medium |
API handlers, DB queries, component trees |
| E2E |
Full user flows |
10-60s each |
Brittle |
High |
Login flow, checkout, onboarding |
1.3 Recommended Ratios
| Project Type |
Unit |
Integration |
E2E |
| API/Backend |
70% |
25% |
5% |
| Frontend (SPA) |
60% |
30% |
10% |
| Mobile |
60% |
25% |
15% |
| Full-stack |
65% |
25% |
10% |
1.4 What to Test at Each Layer
Unit Tests:
- Pure functions and utility logic
- State management (reducers, stores)
- Data transformations and validation
- Custom hooks (React) / composables (Vue)
- Business rule engines
- Edge cases and boundary conditions
Integration Tests:
- API endpoint request/response cycles
- Database queries and migrations
- Component trees with state and props
- Service-to-service communication
- Authentication and authorization flows
- Third-party integration contracts
E2E Tests:
- Critical user journeys (happy path only)
- Revenue-impacting flows (signup, payment, checkout)
- Cross-cutting concerns (auth, permissions)
- Smoke tests for deployment validation
2. Automation Frameworks
2.1 Framework Selection Guide
| Framework |
Language |
Best For |
Speed |
Ecosystem |
| Jest |
JS/TS |
Unit + integration (React, Node) |
Fast |
Excellent |
| Vitest |
JS/TS |
Unit + integration (Vite-based) |
Very fast |
Growing |
| Playwright |
JS/TS/Python/C# |
E2E (cross-browser) |
Fast |
Excellent |
| Cypress |
JS/TS |
E2E + component (web) |
Medium |
Excellent |
| pytest |
Python |
Unit + integration (Python) |
Fast |
Excellent |
| Detox |
JS/TS |
E2E (React Native) |
Slow |
Good |
| Appium |
Multi-lang |
E2E (native mobile) |
Slow |
Good |
| k6 |
JS |
Load/performance testing |
— |
Good |
| Artillery |
JS/YAML |
Load testing |
— |
Good |
2.2 Playwright Configuration
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
reporter: [
['html', { open: 'never' }],
['junit', { outputFile: 'results/e2e-results.xml' }],
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
2.3 Test Patterns
Page Object Model (POM):
// e2e/pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}
readonly emailInput = this.page.getByLabel('Email');
readonly passwordInput = this.page.getByLabel('Password');
readonly submitButton = this.page.getByRole('button', { name: 'Sign in' });
readonly errorMessage = this.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();
}
}
// e2e/tests/login.spec.ts
test('successful login redirects to dashboard', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await expect(page).toHaveURL('/dashboard');
});
Arrange-Act-Assert (AAA):
test('calculates discount for premium users', () => {
// Arrange
const user = createUser({ tier: 'premium' });
const cart = createCart({ total: 100 });
// Act
const result = calculateDiscount(user, cart);
// Assert
expect(result.discount).toBe(20);
expect(result.total).toBe(80);
});
3. Test Data Management
3.1 Strategies
| Strategy |
When to Use |
Pros |
Cons |
| Factories |
Unit/integration tests |
Fast, predictable, isolated |
May miss real-world edge cases |
| Fixtures |
Integration/E2E tests |
Consistent starting state |
Can become stale |
| Seeding |
E2E/staging environments |
Realistic data |
Slow, complex setup |
| Snapshots |
API/DB-heavy tests |
Quick comparison |
Brittle, hard to update |
| Dynamic generation |
Load testing |
Unique data per run |
Complex teardown |
3.2 Factory Pattern
// test/factories/user.factory.ts
import { faker } from '@faker-js/faker';
interface UserOverrides {
email?: string;
name?: string;
tier?: 'free' | 'premium' | 'enterprise';
createdAt?: Date;
}
export function createUser(overrides: UserOverrides = {}) {
return {
id: faker.string.uuid(),
email: overrides.email ?? faker.internet.email(),
name: overrides.name ?? faker.person.fullName(),
tier: overrides.tier ?? 'free',
createdAt: overrides.createdAt ?? faker.date.past(),
...overrides,
};
}
export function createUsers(count: number, overrides: UserOverrides = {}) {
return Array.from({ length: count }, () => createUser(overrides));
}
3.3 Data Cleanup Rules
- Tests own their data. Create in setup, destroy in teardown.
- Isolated. Tests never share mutable state.
- Deterministic. Same test run = same data = same result.
- No production data in tests. Use synthetic/anonymized data only.
- Clean up after E2E. Use API-driven cleanup, not UI.
4. API Testing Patterns
4.1 API Test Template
describe('POST /api/playlists', () => {
describe('when authenticated', () => {
it('creates a playlist and returns 201', async () => {
const response = await request(app)
.post('/api/playlists')
.set('Authorization', `Bearer ${validToken}`)
.send({ name: 'My Playlist', description: 'Test' });
expect(response.status).toBe(201);
expect(response.body).toMatchObject({
id: expect.any(String),
name: 'My Playlist',
description: 'Test',
tracks: [],
createdAt: expect.any(String),
});
});
it('returns 400 for missing name', async () => {
const response = await request(app)
.post('/api/playlists')
.set('Authorization', `Bearer ${validToken}`)
.send({ description: 'No name' });
expect(response.status).toBe(400);
expect(response.body.errors).toContainEqual(
expect.objectContaining({ field: 'name', message: expect.any(String) })
);
});
});
describe('when unauthenticated', () => {
it('returns 401', async () => {
const response = await request(app)
.post('/api/playlists')
.send({ name: 'Unauthorized' });
expect(response.status).toBe(401);
});
});
});
4.2 API Test Checklist
| Category |
Tests |
| Happy Path |
Valid request → expected response (status + body) |
| Validation |
Missing fields, invalid types, boundary values |
| Auth |
Unauthenticated (401), unauthorized (403), expired token |
| Idempotency |
Duplicate requests produce same result |
| Pagination |
First page, last page, empty, out-of-range |
| Error Handling |
404 (not found), 409 (conflict), 422 (unprocessable), 500 (server error) |
| Rate Limiting |
429 after threshold |
| Headers |
Content-Type, Cache-Control, CORS |
| Contract |
Response schema matches OpenAPI spec |
5. Performance Testing
5.1 Test Types
| Type |
Purpose |
Duration |
Load Pattern |
| Load Test |
Validate expected traffic |
10–30 min |
Ramp to expected peak |
| Stress Test |
Find breaking point |
30–60 min |
Ramp beyond capacity |
| Spike Test |
Handle sudden surges |
5–10 min |
Sudden 10x jump |
| Soak Test |
Find memory leaks, degradation |
4–12 hours |
Steady moderate load |
| Breakpoint Test |
Find max capacity |
Until failure |
Linear ramp |
5.2 k6 Load Test Example
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
const errorRate = new Rate('errors');
const latency = new Trend('request_latency');
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up
{ duration: '5m', target: 50 }, // Sustain
{ duration: '2m', target: 200 }, // Peak
{ duration: '5m', target: 200 }, // Sustain peak
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
errors: ['rate<0.01'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get('https://api.example.com/playlists', {
headers: { Authorization: `Bearer ${__ENV.TOKEN}` },
});
check(res, {
'status is 200': (r) => r.status === 200,
'latency < 500ms': (r) => r.timings.duration < 500,
});
errorRate.add(res.status !== 200);
latency.add(res.timings.duration);
sleep(1);
}
5.3 Performance Budgets
| Metric |
Target |
Critical Threshold |
| API p50 latency |
< 100ms |
< 200ms |
| API p95 latency |
< 300ms |
< 500ms |
| API p99 latency |
< 500ms |
< 1000ms |
| Error rate |
< 0.1% |
< 1% |
| Throughput |
> 1000 rps |
> 500 rps |
| Page load (LCP) |
< 2.5s |
< 4.0s |
| First Input Delay |
< 100ms |
< 300ms |
| CLS |
< 0.1 |
< 0.25 |
| Bundle size (JS) |
< 200KB gzip |
< 350KB gzip |
6. Accessibility Testing
6.1 Automated Testing
// Using axe-core with Playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('home page has no a11y violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
6.2 Accessibility Test Checklist
| Category |
Test |
Tool |
| Color Contrast |
Text meets 4.5:1 (AA) |
axe, Stark, Lighthouse |
| Keyboard Navigation |
All interactive elements reachable via Tab |
Manual + Playwright |
| Focus Indicators |
Visible focus ring on all focusable elements |
Manual |
| Screen Reader |
Content announced correctly, ARIA labels present |
VoiceOver, NVDA |
| Alt Text |
All images have meaningful alt text |
axe |
| Heading Hierarchy |
H1 → H2 → H3 without skipping |
axe |
| Form Labels |
All inputs have associated labels |
axe |
| Touch Targets |
Minimum 44×44px for mobile |
Manual |
| Reduced Motion |
Respects prefers-reduced-motion |
Manual |
| Zoom |
Content usable at 200% zoom |
Manual |
6.3 A11y Testing Frequency
- Every PR: Automated axe scans (CI)
- Every sprint: Manual keyboard + screen reader for new features
- Quarterly: Full WCAG 2.1 AA audit with assistive technologies
- Annually: External accessibility audit
7. Visual Regression Testing
7.1 Tools
| Tool |
Approach |
Best For |
| Playwright screenshots |
Pixel comparison |
Component + page |
| Percy |
Visual diffing (cloud) |
Cross-browser visual |
| Chromatic |
Storybook visual tests |
Component libraries |
| BackstopJS |
Config-based screenshots |
Page-level regression |
7.2 Playwright Visual Test
test('playlist card renders correctly', async ({ page }) => {
await page.goto('/playlists');
const card = page.locator('[data-testid="playlist-card"]').first();
await expect(card).toHaveScreenshot('playlist-card.png', {
maxDiffPixelRatio: 0.01,
animations: 'disabled',
});
});
7.3 Visual Testing Best Practices
- Disable animations — They cause flaky diffs.
- Mock dynamic content — Dates, avatars, random data.
- Test components, not pages — Smaller surfaces = more stable.
- Review diffs carefully — Not all changes are regressions.
- Update baselines intentionally — Never auto-approve.
- Cross-browser — Chrome, Firefox, Safari have rendering differences.
8. Mobile Testing
8.1 Framework Comparison
| Framework |
Platform |
Language |
Speed |
Use Case |
| Detox |
React Native |
JS/TS |
Fast |
RN-specific E2E |
| Appium |
iOS + Android (native) |
Multi |
Slow |
Native app testing |
| Maestro |
iOS + Android |
YAML |
Fast |
Simple flow testing |
| XCUITest |
iOS |
Swift |
Fast |
iOS-specific |
| Espresso |
Android |
Kotlin/Java |
Fast |
Android-specific |
8.2 Mobile Test Checklist
| Category |
Tests |
| Gestures |
Tap, swipe, long press, pinch zoom, pull-to-refresh |
| Orientation |
Portrait ↔ landscape state preservation |
| Connectivity |
Offline mode, slow network (3G), network transitions |
| Background/Foreground |
App state preserved on background/resume |
| Notifications |
Push notification display and deep link handling |
| Permissions |
Camera, location, notifications permission flows |
| Device Variations |
Small screens, large screens, notched displays |
| OS Versions |
Min supported version through latest |
| Memory |
No leaks on repeated navigation |
| Battery |
No excessive CPU/GPS usage in background |
9. Bug Severity / Priority Matrix
9.1 Severity (Impact)
| Severity |
Definition |
Example |
| S1 — Critical |
Service down, data loss, security breach |
Login broken, payments failing, user data exposed |
| S2 — Major |
Core feature broken, no workaround |
Can't create playlists, search returns wrong results |
| S3 — Minor |
Feature impaired, workaround exists |
Sort doesn't work but filter does, UI misalignment |
| S4 — Cosmetic |
Visual only, no functional impact |
Wrong font weight, off-by-1px spacing, typo |
9.2 Priority (Urgency)
| Priority |
Definition |
SLA |
| P0 — Hotfix |
Fix immediately, drop everything |
< 4 hours |
| P1 — Urgent |
Fix in current sprint |
< 1 week |
| P2 — Normal |
Fix in next sprint |
< 2 weeks |
| P3 — Low |
Fix when convenient |
Next quarter |
| P4 — Backlog |
May never fix |
No SLA |
9.3 Severity × Priority Decision Matrix
|
S1 Critical |
S2 Major |
S3 Minor |
S4 Cosmetic |
| Affects all users |
P0 |
P1 |
P2 |
P3 |
| Affects many users |
P0 |
P1 |
P2 |
P3 |
| Affects few users |
P1 |
P2 |
P3 |
P4 |
| Edge case |
P1 |
P2 |
P3 |
P4 |
9.4 Bug Report Template
## Bug: [One-line title]
**Severity:** S[1-4] — [Critical/Major/Minor/Cosmetic]
**Priority:** P[0-4] — [Hotfix/Urgent/Normal/Low/Backlog]
**Reporter:** [Name]
**Date:** [Date]
**Assignee:** [Name]
**Environment:** [Production/Staging/Dev] — [Browser/OS/Device]
**Version:** [App version or commit]
### Description
[Clear, concise description of the bug]
### Steps to Reproduce
1. [Step 1]
2. [Step 2]
3. [Step 3]
### Expected Behavior
[What should happen]
### Actual Behavior
[What actually happens]
### Evidence
- Screenshot: [link]
- Video: [link]
- Console errors: [paste]
- Network logs: [paste]
### Impact
- **Users affected:** [All / Segment / Edge case]
- **Workaround:** [Yes — describe / No]
- **Business impact:** [Revenue, retention, compliance]
### Root Cause (if known)
[Technical analysis]
### Fix Verification
- [ ] Bug is reproducible in [environment]
- [ ] Fix deployed to staging
- [ ] Fix verified in staging
- [ ] Regression test added
- [ ] Fix deployed to production
- [ ] Fix verified in production
10. Test Plan Template
# Test Plan: [Feature / Release Name]
**Author:** [Name]
**Date:** [Date]
**Version:** [1.0]
**PRD Reference:** [Link]
## 1. Scope
### In Scope
- [Feature area 1]
- [Feature area 2]
- [Platforms: web, iOS, Android]
### Out of Scope
- [Explicitly excluded areas]
## 2. Test Strategy
| Layer | Scope | Tools | Owner |
|-------|-------|-------|-------|
| Unit | Business logic, utils | Jest/Vitest | Dev team |
| Integration | API, DB, component trees | Jest, Supertest | Dev team |
| E2E | Critical user flows | Playwright | QA team |
| Performance | API load, page speed | k6, Lighthouse | QA + DevOps |
| Accessibility | WCAG 2.1 AA | axe, manual | QA team |
| Visual | UI regression | Playwright screenshots | QA team |
## 3. Test Scenarios
### Happy Path
| # | Scenario | Steps | Expected Result | Priority |
|---|---------|-------|----------------|----------|
| 1 | [Scenario] | [Steps] | [Expected] | P0 |
### Edge Cases
| # | Scenario | Steps | Expected Result | Priority |
|---|---------|-------|----------------|----------|
| 1 | [Scenario] | [Steps] | [Expected] | P1 |
### Error Cases
| # | Scenario | Steps | Expected Result | Priority |
|---|---------|-------|----------------|----------|
| 1 | [Scenario] | [Steps] | [Expected] | P1 |
## 4. Environment Requirements
- [ ] Staging deployed with feature flag enabled
- [ ] Test data seeded
- [ ] Third-party integrations mocked/sandboxed
- [ ] Test accounts created for each role
## 5. Entry / Exit Criteria
### Entry Criteria
- [ ] Feature complete (all acceptance criteria implemented)
- [ ] Unit tests written and passing
- [ ] Code reviewed and merged to staging branch
- [ ] Test environment stable
### Exit Criteria
- [ ] All P0 and P1 test cases passed
- [ ] No open S1 or S2 bugs
- [ ] Test coverage ≥ 80% on new code
- [ ] Performance budgets met
- [ ] Accessibility audit passed
- [ ] PO sign-off obtained
## 6. Risks
| Risk | Probability | Impact | Mitigation |
|------|------------|--------|-----------|
| [Risk] | [H/M/L] | [H/M/L] | [Plan] |
## 7. Schedule
| Phase | Start | End | Owner |
|-------|-------|-----|-------|
| Test planning | [Date] | [Date] | QA Lead |
| Test execution | [Date] | [Date] | QA Team |
| Bug fixing | [Date] | [Date] | Dev Team |
| Regression | [Date] | [Date] | QA Team |
| Sign-off | [Date] | [Date] | PO + QA |
11. Coverage Requirements
Minimum Coverage Thresholds
| Metric |
Minimum |
Target |
Measured By |
| Line coverage (new code) |
80% |
90% |
Jest/Vitest --coverage |
| Branch coverage (new code) |
75% |
85% |
Jest/Vitest --coverage |
| Overall line coverage |
70% |
80% |
Jest/Vitest --coverage |
| Critical path E2E |
100% |
100% |
Playwright test count |
| API endpoint coverage |
90% |
100% |
Supertest/Playwright |
| Accessibility |
0 violations (A, AA) |
0 violations |
axe-core |
Coverage Configuration
// jest.config.ts or vitest.config.ts
{
"coverageThreshold": {
"global": {
"branches": 75,
"functions": 80,
"lines": 80,
"statements": 80
}
},
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts",
"!src/**/*.stories.{ts,tsx}",
"!src/**/*.test.{ts,tsx}",
"!src/**/index.ts",
"!src/types/**"
]
}
Coverage Anti-Patterns
- ❌ Testing implementation details instead of behavior
- ❌ Writing tests just to hit coverage numbers
- ❌ Ignoring branch coverage (hidden bugs live in untested branches)
- ❌ Not tracking coverage trends over time
- ❌ 100% coverage target (diminishing returns past 90%)
Quality Standards
- Shift left. Catch defects as early as possible — unit > integration > E2E.
- Fast feedback. Unit tests < 30s, integration < 2min, full suite < 15min.
- Deterministic. No flaky tests. Flaky tests erode trust and must be fixed or deleted.
- Independent. Tests run in any order, in parallel, without side effects.
- Readable. Tests are documentation — name them like sentences, structure with AAA.
- Maintained. Dead tests are deleted. Failing tests are fixed immediately.
- Risk-based. Test coverage follows risk — more tests for payment than for "About" page.
- Automated first. Manual testing is for exploratory only — everything repeatable is automated.
1---2name: qa-strategy3description: Testing strategy and QA patterns. Use for test pyramid design, E2E test architecture, test automation frameworks, performance testing, and bug triage processes.4---56# QA Strategy Skill78You are a senior QA engineer and test architect. You design testing strategies that catch defects early, run fast, and give teams confidence to ship. You balance automation coverage with maintenance cost and optimize for the fastest feedback loops.910---1112## 1. Test Pyramid1314### 1.1 Pyramid Structure1516```17 ╱ E2E ╲ ~5-10% of tests18 ╱─────────╲ Slow, expensive, brittle19 ╱Integration ╲ ~15-25% of tests20 ╱───────────────╲ Medium speed, medium cost21 ╱ Unit Tests ╲ ~70-80% of tests22 ╱───────────────────╲ Fast, cheap, stable23```2425### 1.2 Layer Definitions2627| Layer | Scope | Speed | Stability | Cost | Examples |28|-------|-------|-------|-----------|------|---------|29| **Unit** | Single function/class | <1ms each | Very stable | Low | Pure logic, utils, hooks, reducers |30| **Integration** | Module interactions | <1s each | Stable | Medium | API handlers, DB queries, component trees |31| **E2E** | Full user flows | 10-60s each | Brittle | High | Login flow, checkout, onboarding |3233### 1.3 Recommended Ratios3435| Project Type | Unit | Integration | E2E |36|-------------|------|-------------|-----|37| **API/Backend** | 70% | 25% | 5% |38| **Frontend (SPA)** | 60% | 30% | 10% |39| **Mobile** | 60% | 25% | 15% |40| **Full-stack** | 65% | 25% | 10% |4142### 1.4 What to Test at Each Layer4344**Unit Tests:**45- Pure functions and utility logic46- State management (reducers, stores)47- Data transformations and validation48- Custom hooks (React) / composables (Vue)49- Business rule engines50- Edge cases and boundary conditions5152**Integration Tests:**53- API endpoint request/response cycles54- Database queries and migrations55- Component trees with state and props56- Service-to-service communication57- Authentication and authorization flows58- Third-party integration contracts5960**E2E Tests:**61- Critical user journeys (happy path only)62- Revenue-impacting flows (signup, payment, checkout)63- Cross-cutting concerns (auth, permissions)64- Smoke tests for deployment validation6566---6768## 2. Automation Frameworks6970### 2.1 Framework Selection Guide7172| Framework | Language | Best For | Speed | Ecosystem |73|-----------|----------|----------|-------|-----------|74| **Jest** | JS/TS | Unit + integration (React, Node) | Fast | Excellent |75| **Vitest** | JS/TS | Unit + integration (Vite-based) | Very fast | Growing |76| **Playwright** | JS/TS/Python/C# | E2E (cross-browser) | Fast | Excellent |77| **Cypress** | JS/TS | E2E + component (web) | Medium | Excellent |78| **pytest** | Python | Unit + integration (Python) | Fast | Excellent |79| **Detox** | JS/TS | E2E (React Native) | Slow | Good |80| **Appium** | Multi-lang | E2E (native mobile) | Slow | Good |81| **k6** | JS | Load/performance testing | — | Good |82| **Artillery** | JS/YAML | Load testing | — | Good |8384### 2.2 Playwright Configuration85```typescript86// playwright.config.ts87import { defineConfig, devices } from '@playwright/test';8889export default defineConfig({90 testDir: './e2e',91 fullyParallel: true,92 forbidOnly: !!process.env.CI,93 retries: process.env.CI ? 2 : 0,94 workers: process.env.CI ? 4 : undefined,95 reporter: [96 ['html', { open: 'never' }],97 ['junit', { outputFile: 'results/e2e-results.xml' }],98 ],99 use: {100 baseURL: process.env.BASE_URL || 'http://localhost:3000',101 trace: 'on-first-retry',102 screenshot: 'only-on-failure',103 video: 'retain-on-failure',104 },105 projects: [106 { name: 'chromium', use: { ...devices['Desktop Chrome'] } },107 { name: 'firefox', use: { ...devices['Desktop Firefox'] } },108 { name: 'webkit', use: { ...devices['Desktop Safari'] } },109 { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },110 { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },111 ],112 webServer: {113 command: 'npm run dev',114 url: 'http://localhost:3000',115 reuseExistingServer: !process.env.CI,116 },117});118```119120### 2.3 Test Patterns121122**Page Object Model (POM):**123```typescript124// e2e/pages/login.page.ts125export class LoginPage {126 constructor(private page: Page) {}127128 readonly emailInput = this.page.getByLabel('Email');129 readonly passwordInput = this.page.getByLabel('Password');130 readonly submitButton = this.page.getByRole('button', { name: 'Sign in' });131 readonly errorMessage = this.page.getByRole('alert');132133 async goto() {134 await this.page.goto('/login');135 }136137 async login(email: string, password: string) {138 await this.emailInput.fill(email);139 await this.passwordInput.fill(password);140 await this.submitButton.click();141 }142}143144// e2e/tests/login.spec.ts145test('successful login redirects to dashboard', async ({ page }) => {146 const loginPage = new LoginPage(page);147 await loginPage.goto();148 await loginPage.login('user@example.com', 'password123');149 await expect(page).toHaveURL('/dashboard');150});151```152153**Arrange-Act-Assert (AAA):**154```typescript155test('calculates discount for premium users', () => {156 // Arrange157 const user = createUser({ tier: 'premium' });158 const cart = createCart({ total: 100 });159160 // Act161 const result = calculateDiscount(user, cart);162163 // Assert164 expect(result.discount).toBe(20);165 expect(result.total).toBe(80);166});167```168169---170171## 3. Test Data Management172173### 3.1 Strategies174175| Strategy | When to Use | Pros | Cons |176|----------|-------------|------|------|177| **Factories** | Unit/integration tests | Fast, predictable, isolated | May miss real-world edge cases |178| **Fixtures** | Integration/E2E tests | Consistent starting state | Can become stale |179| **Seeding** | E2E/staging environments | Realistic data | Slow, complex setup |180| **Snapshots** | API/DB-heavy tests | Quick comparison | Brittle, hard to update |181| **Dynamic generation** | Load testing | Unique data per run | Complex teardown |182183### 3.2 Factory Pattern184```typescript185// test/factories/user.factory.ts186import { faker } from '@faker-js/faker';187188interface UserOverrides {189 email?: string;190 name?: string;191 tier?: 'free' | 'premium' | 'enterprise';192 createdAt?: Date;193}194195export function createUser(overrides: UserOverrides = {}) {196 return {197 id: faker.string.uuid(),198 email: overrides.email ?? faker.internet.email(),199 name: overrides.name ?? faker.person.fullName(),200 tier: overrides.tier ?? 'free',201 createdAt: overrides.createdAt ?? faker.date.past(),202 ...overrides,203 };204}205206export function createUsers(count: number, overrides: UserOverrides = {}) {207 return Array.from({ length: count }, () => createUser(overrides));208}209```210211### 3.3 Data Cleanup Rules2122131. **Tests own their data.** Create in setup, destroy in teardown.2142. **Isolated.** Tests never share mutable state.2153. **Deterministic.** Same test run = same data = same result.2164. **No production data in tests.** Use synthetic/anonymized data only.2175. **Clean up after E2E.** Use API-driven cleanup, not UI.218219---220221## 4. API Testing Patterns222223### 4.1 API Test Template224```typescript225describe('POST /api/playlists', () => {226 describe('when authenticated', () => {227 it('creates a playlist and returns 201', async () => {228 const response = await request(app)229 .post('/api/playlists')230 .set('Authorization', `Bearer ${validToken}`)231 .send({ name: 'My Playlist', description: 'Test' });232233 expect(response.status).toBe(201);234 expect(response.body).toMatchObject({235 id: expect.any(String),236 name: 'My Playlist',237 description: 'Test',238 tracks: [],239 createdAt: expect.any(String),240 });241 });242243 it('returns 400 for missing name', async () => {244 const response = await request(app)245 .post('/api/playlists')246 .set('Authorization', `Bearer ${validToken}`)247 .send({ description: 'No name' });248249 expect(response.status).toBe(400);250 expect(response.body.errors).toContainEqual(251 expect.objectContaining({ field: 'name', message: expect.any(String) })252 );253 });254 });255256 describe('when unauthenticated', () => {257 it('returns 401', async () => {258 const response = await request(app)259 .post('/api/playlists')260 .send({ name: 'Unauthorized' });261262 expect(response.status).toBe(401);263 });264 });265});266```267268### 4.2 API Test Checklist269270| Category | Tests |271|----------|-------|272| **Happy Path** | Valid request → expected response (status + body) |273| **Validation** | Missing fields, invalid types, boundary values |274| **Auth** | Unauthenticated (401), unauthorized (403), expired token |275| **Idempotency** | Duplicate requests produce same result |276| **Pagination** | First page, last page, empty, out-of-range |277| **Error Handling** | 404 (not found), 409 (conflict), 422 (unprocessable), 500 (server error) |278| **Rate Limiting** | 429 after threshold |279| **Headers** | Content-Type, Cache-Control, CORS |280| **Contract** | Response schema matches OpenAPI spec |281282---283284## 5. Performance Testing285286### 5.1 Test Types287288| Type | Purpose | Duration | Load Pattern |289|------|---------|----------|-------------|290| **Load Test** | Validate expected traffic | 10–30 min | Ramp to expected peak |291| **Stress Test** | Find breaking point | 30–60 min | Ramp beyond capacity |292| **Spike Test** | Handle sudden surges | 5–10 min | Sudden 10x jump |293| **Soak Test** | Find memory leaks, degradation | 4–12 hours | Steady moderate load |294| **Breakpoint Test** | Find max capacity | Until failure | Linear ramp |295296### 5.2 k6 Load Test Example297```javascript298// load-test.js299import http from 'k6/http';300import { check, sleep } from 'k6';301import { Rate, Trend } from 'k6/metrics';302303const errorRate = new Rate('errors');304const latency = new Trend('request_latency');305306export const options = {307 stages: [308 { duration: '2m', target: 50 }, // Ramp up309 { duration: '5m', target: 50 }, // Sustain310 { duration: '2m', target: 200 }, // Peak311 { duration: '5m', target: 200 }, // Sustain peak312 { duration: '2m', target: 0 }, // Ramp down313 ],314 thresholds: {315 http_req_duration: ['p(95)<500', 'p(99)<1000'],316 errors: ['rate<0.01'],317 http_req_failed: ['rate<0.01'],318 },319};320321export default function () {322 const res = http.get('https://api.example.com/playlists', {323 headers: { Authorization: `Bearer ${__ENV.TOKEN}` },324 });325326 check(res, {327 'status is 200': (r) => r.status === 200,328 'latency < 500ms': (r) => r.timings.duration < 500,329 });330331 errorRate.add(res.status !== 200);332 latency.add(res.timings.duration);333334 sleep(1);335}336```337338### 5.3 Performance Budgets339340| Metric | Target | Critical Threshold |341|--------|--------|-------------------|342| **API p50 latency** | < 100ms | < 200ms |343| **API p95 latency** | < 300ms | < 500ms |344| **API p99 latency** | < 500ms | < 1000ms |345| **Error rate** | < 0.1% | < 1% |346| **Throughput** | > 1000 rps | > 500 rps |347| **Page load (LCP)** | < 2.5s | < 4.0s |348| **First Input Delay** | < 100ms | < 300ms |349| **CLS** | < 0.1 | < 0.25 |350| **Bundle size (JS)** | < 200KB gzip | < 350KB gzip |351352---353354## 6. Accessibility Testing355356### 6.1 Automated Testing357```typescript358// Using axe-core with Playwright359import { test, expect } from '@playwright/test';360import AxeBuilder from '@axe-core/playwright';361362test('home page has no a11y violations', async ({ page }) => {363 await page.goto('/');364365 const results = await new AxeBuilder({ page })366 .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])367 .analyze();368369 expect(results.violations).toEqual([]);370});371```372373### 6.2 Accessibility Test Checklist374375| Category | Test | Tool |376|----------|------|------|377| **Color Contrast** | Text meets 4.5:1 (AA) | axe, Stark, Lighthouse |378| **Keyboard Navigation** | All interactive elements reachable via Tab | Manual + Playwright |379| **Focus Indicators** | Visible focus ring on all focusable elements | Manual |380| **Screen Reader** | Content announced correctly, ARIA labels present | VoiceOver, NVDA |381| **Alt Text** | All images have meaningful alt text | axe |382| **Heading Hierarchy** | H1 → H2 → H3 without skipping | axe |383| **Form Labels** | All inputs have associated labels | axe |384| **Touch Targets** | Minimum 44×44px for mobile | Manual |385| **Reduced Motion** | Respects `prefers-reduced-motion` | Manual |386| **Zoom** | Content usable at 200% zoom | Manual |387388### 6.3 A11y Testing Frequency389- **Every PR:** Automated axe scans (CI)390- **Every sprint:** Manual keyboard + screen reader for new features391- **Quarterly:** Full WCAG 2.1 AA audit with assistive technologies392- **Annually:** External accessibility audit393394---395396## 7. Visual Regression Testing397398### 7.1 Tools399400| Tool | Approach | Best For |401|------|----------|----------|402| **Playwright screenshots** | Pixel comparison | Component + page |403| **Percy** | Visual diffing (cloud) | Cross-browser visual |404| **Chromatic** | Storybook visual tests | Component libraries |405| **BackstopJS** | Config-based screenshots | Page-level regression |406407### 7.2 Playwright Visual Test408```typescript409test('playlist card renders correctly', async ({ page }) => {410 await page.goto('/playlists');411 const card = page.locator('[data-testid="playlist-card"]').first();412413 await expect(card).toHaveScreenshot('playlist-card.png', {414 maxDiffPixelRatio: 0.01,415 animations: 'disabled',416 });417});418```419420### 7.3 Visual Testing Best Practices4214221. **Disable animations** — They cause flaky diffs.4232. **Mock dynamic content** — Dates, avatars, random data.4243. **Test components, not pages** — Smaller surfaces = more stable.4254. **Review diffs carefully** — Not all changes are regressions.4265. **Update baselines intentionally** — Never auto-approve.4276. **Cross-browser** — Chrome, Firefox, Safari have rendering differences.428429---430431## 8. Mobile Testing432433### 8.1 Framework Comparison434435| Framework | Platform | Language | Speed | Use Case |436|-----------|----------|----------|-------|----------|437| **Detox** | React Native | JS/TS | Fast | RN-specific E2E |438| **Appium** | iOS + Android (native) | Multi | Slow | Native app testing |439| **Maestro** | iOS + Android | YAML | Fast | Simple flow testing |440| **XCUITest** | iOS | Swift | Fast | iOS-specific |441| **Espresso** | Android | Kotlin/Java | Fast | Android-specific |442443### 8.2 Mobile Test Checklist444445| Category | Tests |446|----------|-------|447| **Gestures** | Tap, swipe, long press, pinch zoom, pull-to-refresh |448| **Orientation** | Portrait ↔ landscape state preservation |449| **Connectivity** | Offline mode, slow network (3G), network transitions |450| **Background/Foreground** | App state preserved on background/resume |451| **Notifications** | Push notification display and deep link handling |452| **Permissions** | Camera, location, notifications permission flows |453| **Device Variations** | Small screens, large screens, notched displays |454| **OS Versions** | Min supported version through latest |455| **Memory** | No leaks on repeated navigation |456| **Battery** | No excessive CPU/GPS usage in background |457458---459460## 9. Bug Severity / Priority Matrix461462### 9.1 Severity (Impact)463464| Severity | Definition | Example |465|----------|-----------|---------|466| **S1 — Critical** | Service down, data loss, security breach | Login broken, payments failing, user data exposed |467| **S2 — Major** | Core feature broken, no workaround | Can't create playlists, search returns wrong results |468| **S3 — Minor** | Feature impaired, workaround exists | Sort doesn't work but filter does, UI misalignment |469| **S4 — Cosmetic** | Visual only, no functional impact | Wrong font weight, off-by-1px spacing, typo |470471### 9.2 Priority (Urgency)472473| Priority | Definition | SLA |474|----------|-----------|-----|475| **P0 — Hotfix** | Fix immediately, drop everything | < 4 hours |476| **P1 — Urgent** | Fix in current sprint | < 1 week |477| **P2 — Normal** | Fix in next sprint | < 2 weeks |478| **P3 — Low** | Fix when convenient | Next quarter |479| **P4 — Backlog** | May never fix | No SLA |480481### 9.3 Severity × Priority Decision Matrix482483| | S1 Critical | S2 Major | S3 Minor | S4 Cosmetic |484|--|:-----------:|:--------:|:--------:|:-----------:|485| **Affects all users** | P0 | P1 | P2 | P3 |486| **Affects many users** | P0 | P1 | P2 | P3 |487| **Affects few users** | P1 | P2 | P3 | P4 |488| **Edge case** | P1 | P2 | P3 | P4 |489490### 9.4 Bug Report Template491```markdown492## Bug: [One-line title]493494**Severity:** S[1-4] — [Critical/Major/Minor/Cosmetic]495**Priority:** P[0-4] — [Hotfix/Urgent/Normal/Low/Backlog]496**Reporter:** [Name]497**Date:** [Date]498**Assignee:** [Name]499**Environment:** [Production/Staging/Dev] — [Browser/OS/Device]500**Version:** [App version or commit]501502### Description503[Clear, concise description of the bug]504505### Steps to Reproduce5061. [Step 1]5072. [Step 2]5083. [Step 3]509510### Expected Behavior511[What should happen]512513### Actual Behavior514[What actually happens]515516### Evidence517- Screenshot: [link]518- Video: [link]519- Console errors: [paste]520- Network logs: [paste]521522### Impact523- **Users affected:** [All / Segment / Edge case]524- **Workaround:** [Yes — describe / No]525- **Business impact:** [Revenue, retention, compliance]526527### Root Cause (if known)528[Technical analysis]529530### Fix Verification531- [ ] Bug is reproducible in [environment]532- [ ] Fix deployed to staging533- [ ] Fix verified in staging534- [ ] Regression test added535- [ ] Fix deployed to production536- [ ] Fix verified in production537```538539---540541## 10. Test Plan Template542543```markdown544# Test Plan: [Feature / Release Name]545546**Author:** [Name]547**Date:** [Date]548**Version:** [1.0]549**PRD Reference:** [Link]550551## 1. Scope552553### In Scope554- [Feature area 1]555- [Feature area 2]556- [Platforms: web, iOS, Android]557558### Out of Scope559- [Explicitly excluded areas]560561## 2. Test Strategy562563| Layer | Scope | Tools | Owner |564|-------|-------|-------|-------|565| Unit | Business logic, utils | Jest/Vitest | Dev team |566| Integration | API, DB, component trees | Jest, Supertest | Dev team |567| E2E | Critical user flows | Playwright | QA team |568| Performance | API load, page speed | k6, Lighthouse | QA + DevOps |569| Accessibility | WCAG 2.1 AA | axe, manual | QA team |570| Visual | UI regression | Playwright screenshots | QA team |571572## 3. Test Scenarios573574### Happy Path575| # | Scenario | Steps | Expected Result | Priority |576|---|---------|-------|----------------|----------|577| 1 | [Scenario] | [Steps] | [Expected] | P0 |578579### Edge Cases580| # | Scenario | Steps | Expected Result | Priority |581|---|---------|-------|----------------|----------|582| 1 | [Scenario] | [Steps] | [Expected] | P1 |583584### Error Cases585| # | Scenario | Steps | Expected Result | Priority |586|---|---------|-------|----------------|----------|587| 1 | [Scenario] | [Steps] | [Expected] | P1 |588589## 4. Environment Requirements590- [ ] Staging deployed with feature flag enabled591- [ ] Test data seeded592- [ ] Third-party integrations mocked/sandboxed593- [ ] Test accounts created for each role594595## 5. Entry / Exit Criteria596597### Entry Criteria598- [ ] Feature complete (all acceptance criteria implemented)599- [ ] Unit tests written and passing600- [ ] Code reviewed and merged to staging branch601- [ ] Test environment stable602603### Exit Criteria604- [ ] All P0 and P1 test cases passed605- [ ] No open S1 or S2 bugs606- [ ] Test coverage ≥ 80% on new code607- [ ] Performance budgets met608- [ ] Accessibility audit passed609- [ ] PO sign-off obtained610611## 6. Risks612| Risk | Probability | Impact | Mitigation |613|------|------------|--------|-----------|614| [Risk] | [H/M/L] | [H/M/L] | [Plan] |615616## 7. Schedule617| Phase | Start | End | Owner |618|-------|-------|-----|-------|619| Test planning | [Date] | [Date] | QA Lead |620| Test execution | [Date] | [Date] | QA Team |621| Bug fixing | [Date] | [Date] | Dev Team |622| Regression | [Date] | [Date] | QA Team |623| Sign-off | [Date] | [Date] | PO + QA |624```625626---627628## 11. Coverage Requirements629630### Minimum Coverage Thresholds631632| Metric | Minimum | Target | Measured By |633|--------|---------|--------|-------------|634| **Line coverage (new code)** | 80% | 90% | Jest/Vitest --coverage |635| **Branch coverage (new code)** | 75% | 85% | Jest/Vitest --coverage |636| **Overall line coverage** | 70% | 80% | Jest/Vitest --coverage |637| **Critical path E2E** | 100% | 100% | Playwright test count |638| **API endpoint coverage** | 90% | 100% | Supertest/Playwright |639| **Accessibility** | 0 violations (A, AA) | 0 violations | axe-core |640641### Coverage Configuration642```json643// jest.config.ts or vitest.config.ts644{645 "coverageThreshold": {646 "global": {647 "branches": 75,648 "functions": 80,649 "lines": 80,650 "statements": 80651 }652 },653 "collectCoverageFrom": [654 "src/**/*.{ts,tsx}",655 "!src/**/*.d.ts",656 "!src/**/*.stories.{ts,tsx}",657 "!src/**/*.test.{ts,tsx}",658 "!src/**/index.ts",659 "!src/types/**"660 ]661}662```663664### Coverage Anti-Patterns665- ❌ Testing implementation details instead of behavior666- ❌ Writing tests just to hit coverage numbers667- ❌ Ignoring branch coverage (hidden bugs live in untested branches)668- ❌ Not tracking coverage trends over time669- ❌ 100% coverage target (diminishing returns past 90%)670671---672673## Quality Standards6746751. **Shift left.** Catch defects as early as possible — unit > integration > E2E.6762. **Fast feedback.** Unit tests < 30s, integration < 2min, full suite < 15min.6773. **Deterministic.** No flaky tests. Flaky tests erode trust and must be fixed or deleted.6784. **Independent.** Tests run in any order, in parallel, without side effects.6795. **Readable.** Tests are documentation — name them like sentences, structure with AAA.6806. **Maintained.** Dead tests are deleted. Failing tests are fixed immediately.6817. **Risk-based.** Test coverage follows risk — more tests for payment than for "About" page.6828. **Automated first.** Manual testing is for exploratory only — everything repeatable is automated.