qa-workflow-cc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +461 -0
  2. package/VERSION +1 -0
  3. package/bin/install.js +116 -0
  4. package/commands/qa/continue.md +77 -0
  5. package/commands/qa/full.md +149 -0
  6. package/commands/qa/init.md +105 -0
  7. package/commands/qa/resume.md +91 -0
  8. package/commands/qa/status.md +66 -0
  9. package/package.json +28 -0
  10. package/skills/qa/SKILL.md +420 -0
  11. package/skills/qa/references/continuation-format.md +58 -0
  12. package/skills/qa/references/exit-criteria.md +53 -0
  13. package/skills/qa/references/lifecycle.md +181 -0
  14. package/skills/qa/references/model-profiles.md +77 -0
  15. package/skills/qa/templates/agent-skeleton.md +733 -0
  16. package/skills/qa/templates/component-test.md +1088 -0
  17. package/skills/qa/templates/domain-research-queries.md +101 -0
  18. package/skills/qa/templates/domain-security-profiles.md +182 -0
  19. package/skills/qa/templates/e2e-test.md +1200 -0
  20. package/skills/qa/templates/nielsen-heuristics.md +274 -0
  21. package/skills/qa/templates/performance-benchmarks-base.md +321 -0
  22. package/skills/qa/templates/qa-report-template.md +271 -0
  23. package/skills/qa/templates/security-checklist-owasp.md +451 -0
  24. package/skills/qa/templates/stop-points/bootstrap-complete.md +36 -0
  25. package/skills/qa/templates/stop-points/certified.md +25 -0
  26. package/skills/qa/templates/stop-points/escalated.md +32 -0
  27. package/skills/qa/templates/stop-points/fix-ready.md +43 -0
  28. package/skills/qa/templates/stop-points/phase-transition.md +4 -0
  29. package/skills/qa/templates/stop-points/status-dashboard.md +32 -0
  30. package/skills/qa/templates/test-standards.md +652 -0
  31. package/skills/qa/templates/unit-test.md +998 -0
  32. package/skills/qa/templates/visual-regression.md +418 -0
  33. package/skills/qa/workflows/bootstrap.md +45 -0
  34. package/skills/qa/workflows/decision-gate.md +66 -0
  35. package/skills/qa/workflows/fix-execute.md +132 -0
  36. package/skills/qa/workflows/fix-plan.md +52 -0
  37. package/skills/qa/workflows/report-phase.md +64 -0
  38. package/skills/qa/workflows/test-phase.md +86 -0
  39. package/skills/qa/workflows/verify-phase.md +65 -0
@@ -0,0 +1,652 @@
1
+ # Testing Standards
2
+
3
+ > Generic testing standards template. Load this skill when writing tests. Not needed every session.
4
+
5
+ ## Template Variables
6
+
7
+ | Variable | Description | Default |
8
+ |----------|-------------|---------|
9
+ | `{{testRunner}}` | Test runner name (vitest, jest) | vitest |
10
+ | `{{testCommand}}` | Command to run tests | pnpm test |
11
+ | `{{testCoverageCommand}}` | Command for coverage report | pnpm test:coverage |
12
+ | `{{e2eCommand}}` | Command to run E2E tests | pnpm e2e |
13
+ | `{{coverageThresholds}}` | Coverage threshold block | See defaults below |
14
+ | `{{testCommands}}` | Project-specific test commands block | See defaults below |
15
+
16
+ ---
17
+
18
+ ## Coverage Thresholds
19
+
20
+ ### Global Defaults
21
+
22
+ {{coverageThresholds}}
23
+
24
+ | Metric | Threshold |
25
+ |--------|-----------|
26
+ | Statements | 80% |
27
+ | Branches | 70% |
28
+ | Functions | 80% |
29
+ | Lines | 80% |
30
+
31
+ ### Coverage by Code Area
32
+
33
+ Define coverage targets by area based on criticality:
34
+
35
+ | Area | Target | Priority | Rationale |
36
+ |------|--------|----------|-----------|
37
+ | API / Server Routes | 90% | CRITICAL | Data integrity, security boundaries |
38
+ | Authentication / Authorization | 95% | CRITICAL | Security-critical code paths |
39
+ | Data Access Layer | 85% | HIGH | Database queries, data transformations |
40
+ | Utility Functions | 85% | HIGH | Widely reused, high leverage |
41
+ | Hooks / State Management | 80% | HIGH | Complex state logic |
42
+ | UI Components | 70% | MEDIUM | Visual correctness via snapshot + behavior tests |
43
+ | E2E Flows | Critical paths only | CRITICAL | User journeys that must never break |
44
+ | Configuration / Constants | 50% | LOW | Mostly static, low risk |
45
+
46
+ > **Project-specific override:** Replace the table above with your project's actual coverage targets during bootstrapping.
47
+
48
+ ---
49
+
50
+ ## Test Type Requirements
51
+
52
+ ### Unit Tests (Required for all new logic)
53
+
54
+ Every new utility function, hook, or pure logic module MUST have unit tests.
55
+
56
+ **File naming:** `[module].test.ts` or `__tests__/[module].test.ts`
57
+
58
+ **What to test:**
59
+ - Input/output mapping for all cases (happy path, edge cases, errors)
60
+ - Boundary conditions (empty, null, undefined, max values)
61
+ - Error throwing and error messages
62
+ - Default values and optional parameters
63
+
64
+ **Example areas:**
65
+ - Utility functions (formatters, validators, calculators)
66
+ - Custom hooks (state changes, effects, cleanup)
67
+ - Data transformation functions
68
+ - Schema validation logic (Zod, Yup, etc.)
69
+ - Business logic modules
70
+
71
+ ### API / Router Tests (Required for all endpoints)
72
+
73
+ Every API endpoint or server-side route MUST have integration tests covering:
74
+
75
+ - CRUD operations (create, read, update, delete)
76
+ - Input validation (valid, invalid, edge cases)
77
+ - Authorization checks (authenticated vs. unauthenticated)
78
+ - Multi-tenancy isolation (if applicable -- data scoped to organization/user)
79
+ - Error handling (not found, forbidden, conflict, bad request)
80
+ - Pagination (cursor-based or offset-based)
81
+
82
+ **File naming:** `routers/__tests__/[router].test.ts` or `routes/__tests__/[route].test.ts`
83
+
84
+ ### Component Tests (Required for complex components)
85
+
86
+ Cover:
87
+ - Rendering with different props (variants, states)
88
+ - User interactions (clicks, form submission, keyboard)
89
+ - Loading, error, and empty states
90
+ - Accessibility (ARIA attributes, keyboard navigation)
91
+ - Conditional rendering logic
92
+
93
+ **Use Testing Library patterns, not implementation details.**
94
+
95
+ **File naming:** `components/__tests__/[Component].test.tsx`
96
+
97
+ ### E2E Tests (Required for critical flows)
98
+
99
+ Cover the critical user journeys that MUST NOT break:
100
+
101
+ - Authentication flow (sign up, sign in, sign out)
102
+ - Primary CRUD operations (create, edit, delete core entities)
103
+ - Navigation between major sections
104
+ - Form wizards / multi-step flows
105
+ - Payment / checkout flows (if applicable)
106
+ - File upload flows (if applicable)
107
+
108
+ **File naming:** `e2e/[flow-name].spec.ts`
109
+
110
+ ---
111
+
112
+ ## Test Patterns
113
+
114
+ ### DO (Recommended Patterns)
115
+
116
+ | Pattern | Description |
117
+ |---------|-------------|
118
+ | Semantic queries | `getByRole`, `getByLabelText`, `getByText` first |
119
+ | `userEvent` | Simulates real user interactions (not `fireEvent` for web) |
120
+ | `waitFor` / `findBy` | Handles async rendering and data fetching |
121
+ | Behavior testing | Test what the user sees and does, not internal state |
122
+ | `describe` blocks | Group related tests for readability |
123
+ | `it.each` / parameterized | Reduce duplication for similar test cases |
124
+ | Factory functions | Create test data with sensible defaults |
125
+ | Custom render | Wrap providers in test-utils for consistent setup |
126
+ | Mock cleanup | `vi.clearAllMocks()` or `jest.clearAllMocks()` in `beforeEach` |
127
+ | Fake timers | `vi.useFakeTimers()` for time-dependent code |
128
+
129
+ ### DON'T (Anti-Patterns)
130
+
131
+ | Anti-Pattern | Why |
132
+ |--------------|-----|
133
+ | `getByClassName` | Implementation detail, breaks on refactor |
134
+ | `getByTestId` as default | Use semantic queries first, testId as last resort |
135
+ | Hardcoded waits (`sleep(2000)`) | Flaky, slow -- use `waitFor` or `findBy` |
136
+ | Testing internal state | Test behavior, not `useState` values |
137
+ | Testing implementation details | Internal methods, private functions, CSS classes |
138
+ | Snapshot-only tests | Snapshots detect changes but don't verify correctness |
139
+ | Shared mutable state | Each test must be independent |
140
+ | `any` in test types | Weakens test reliability |
141
+ | Console.log debugging left in | Clean up before committing |
142
+
143
+ ---
144
+
145
+ ## Multi-Tenancy / Isolation Testing (CRITICAL)
146
+
147
+ If your application is multi-tenant (organizations, workspaces, teams), every database query test MUST verify isolation:
148
+
149
+ ```typescript
150
+ describe('data isolation', () => {
151
+ it('does not return other tenant data', async () => {
152
+ // Create data for Org A
153
+ const itemA = await createItem({ orgId: 'org-a', name: 'Item A' })
154
+
155
+ // Query as Org B -- should NOT see Org A's data
156
+ const result = await listItems({ orgId: 'org-b' })
157
+
158
+ expect(result.items).not.toContainEqual(
159
+ expect.objectContaining({ id: itemA.id })
160
+ )
161
+ })
162
+
163
+ it('rejects access to other tenant resources', async () => {
164
+ const item = await createItem({ orgId: 'org-a', name: 'Private Item' })
165
+
166
+ // Try to access as different org
167
+ await expect(
168
+ getItem({ orgId: 'org-b', itemId: item.id })
169
+ ).rejects.toThrow() // Should throw FORBIDDEN or NOT_FOUND
170
+ })
171
+
172
+ it('scopes mutations to current tenant', async () => {
173
+ const item = await createItem({ orgId: 'org-a', name: 'Original' })
174
+
175
+ // Try to update as different org
176
+ await expect(
177
+ updateItem({ orgId: 'org-b', itemId: item.id, name: 'Hacked' })
178
+ ).rejects.toThrow()
179
+
180
+ // Verify original is unchanged
181
+ const unchanged = await getItem({ orgId: 'org-a', itemId: item.id })
182
+ expect(unchanged.name).toBe('Original')
183
+ })
184
+ })
185
+ ```
186
+
187
+ **Checklist for isolation:**
188
+ - [ ] All list queries filter by tenant identifier
189
+ - [ ] All single-resource queries verify tenant ownership
190
+ - [ ] All mutations verify tenant ownership before modifying
191
+ - [ ] Aggregation queries (counts, sums) scope to tenant
192
+ - [ ] Search/filter queries scope to tenant
193
+
194
+ ---
195
+
196
+ ## Test Data Patterns
197
+
198
+ ### Factory Functions
199
+
200
+ ```typescript
201
+ // test/factories.ts
202
+ let idCounter = 0
203
+
204
+ export function createTestUser(overrides: Partial<User> = {}): User {
205
+ idCounter++
206
+ return {
207
+ id: `user-${idCounter}`,
208
+ name: `Test User ${idCounter}`,
209
+ email: `user${idCounter}@test.com`,
210
+ role: 'member',
211
+ createdAt: new Date(),
212
+ ...overrides,
213
+ }
214
+ }
215
+
216
+ export function createTestItem(overrides: Partial<Item> = {}): Item {
217
+ idCounter++
218
+ return {
219
+ id: `item-${idCounter}`,
220
+ name: `Test Item ${idCounter}`,
221
+ status: 'DRAFT',
222
+ createdAt: new Date(),
223
+ ...overrides,
224
+ }
225
+ }
226
+
227
+ // Usage
228
+ const user = createTestUser({ role: 'admin' })
229
+ const item = createTestItem({ status: 'ACTIVE', name: 'Custom Name' })
230
+ ```
231
+
232
+ ### Builder Pattern (For Complex Objects)
233
+
234
+ ```typescript
235
+ export class TestItemBuilder {
236
+ private item: Partial<Item> = {}
237
+
238
+ withName(name: string) { this.item.name = name; return this }
239
+ withStatus(status: string) { this.item.status = status; return this }
240
+ withOwner(userId: string) { this.item.ownerId = userId; return this }
241
+ withTags(tags: string[]) { this.item.tags = tags; return this }
242
+
243
+ build(): Item {
244
+ return createTestItem(this.item)
245
+ }
246
+ }
247
+
248
+ // Usage
249
+ const item = new TestItemBuilder()
250
+ .withName('Important')
251
+ .withStatus('ACTIVE')
252
+ .withTags(['urgent', 'client'])
253
+ .build()
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Test Organization
259
+
260
+ ### Directory Structure
261
+
262
+ ```
263
+ project/
264
+ src/
265
+ __tests__/ # Unit tests (mirrors src/ structure)
266
+ utils/
267
+ currency.test.ts
268
+ date.test.ts
269
+ hooks/
270
+ useDebounce.test.ts
271
+ services/
272
+ email.test.ts
273
+ components/
274
+ __tests__/ # Component tests (co-located)
275
+ ItemCard.test.tsx
276
+ Dashboard.test.tsx
277
+ e2e/ # E2E tests
278
+ fixtures/
279
+ auth.ts
280
+ testData.ts
281
+ pages/ # Page objects
282
+ ItemsPage.ts
283
+ DashboardPage.ts
284
+ item-management.spec.ts
285
+ auth-flow.spec.ts
286
+ test/ # Test utilities and setup
287
+ setup.ts
288
+ factories.ts
289
+ test-utils.tsx
290
+ mocks/
291
+ handlers.ts
292
+ ```
293
+
294
+ ### Test Naming Guidelines
295
+
296
+ ```typescript
297
+ // Describe blocks: noun (the thing being tested)
298
+ describe('formatCurrency', () => { ... })
299
+ describe('ItemCard', () => { ... })
300
+ describe('useDebounce', () => { ... })
301
+
302
+ // Test names: "it [does something expected]"
303
+ it('formats positive numbers with commas', () => { ... })
304
+ it('renders item name and status', () => { ... })
305
+ it('debounces rapid value changes', () => { ... })
306
+
307
+ // Group by scenario
308
+ describe('ItemCard', () => {
309
+ describe('when item is active', () => { ... })
310
+ describe('when item is archived', () => { ... })
311
+ describe('user interactions', () => { ... })
312
+ describe('accessibility', () => { ... })
313
+ })
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Security Testing Patterns
319
+
320
+ ### Authentication Boundary Tests
321
+
322
+ ```typescript
323
+ describe('authentication boundaries', () => {
324
+ it('rejects unauthenticated requests', async () => {
325
+ await expect(
326
+ callEndpoint({ authToken: undefined })
327
+ ).rejects.toThrow('UNAUTHORIZED')
328
+ })
329
+
330
+ it('rejects expired tokens', async () => {
331
+ await expect(
332
+ callEndpoint({ authToken: expiredToken })
333
+ ).rejects.toThrow('UNAUTHORIZED')
334
+ })
335
+
336
+ it('rejects invalid tokens', async () => {
337
+ await expect(
338
+ callEndpoint({ authToken: 'invalid-garbage' })
339
+ ).rejects.toThrow('UNAUTHORIZED')
340
+ })
341
+ })
342
+ ```
343
+
344
+ ### Authorization Tests
345
+
346
+ ```typescript
347
+ describe('authorization', () => {
348
+ it('allows admin to delete items', async () => {
349
+ const result = await deleteItem({ role: 'admin', itemId: '1' })
350
+ expect(result.success).toBe(true)
351
+ })
352
+
353
+ it('forbids regular user from deleting items', async () => {
354
+ await expect(
355
+ deleteItem({ role: 'member', itemId: '1' })
356
+ ).rejects.toThrow('FORBIDDEN')
357
+ })
358
+
359
+ it('allows item owner to edit their own item', async () => {
360
+ const result = await editItem({ userId: 'owner-1', itemId: '1' })
361
+ expect(result.success).toBe(true)
362
+ })
363
+
364
+ it('forbids editing another user item', async () => {
365
+ await expect(
366
+ editItem({ userId: 'other-user', itemId: '1' })
367
+ ).rejects.toThrow('FORBIDDEN')
368
+ })
369
+ })
370
+ ```
371
+
372
+ ### Input Sanitization Tests
373
+
374
+ ```typescript
375
+ describe('input sanitization', () => {
376
+ it('strips HTML from user input', () => {
377
+ const result = sanitize('<script>alert("xss")</script>Hello')
378
+ expect(result).toBe('Hello')
379
+ expect(result).not.toContain('<script>')
380
+ })
381
+
382
+ it('rejects SQL injection attempts', async () => {
383
+ await expect(
384
+ searchItems({ query: "'; DROP TABLE items; --" })
385
+ ).resolves.not.toThrow()
386
+ // Items table should still exist
387
+ })
388
+ })
389
+ ```
390
+
391
+ ---
392
+
393
+ ## Performance Testing Patterns
394
+
395
+ ### Response Time Assertions
396
+
397
+ ```typescript
398
+ describe('performance', () => {
399
+ it('list query responds within 300ms', async () => {
400
+ const start = performance.now()
401
+ await listItems({ limit: 20 })
402
+ const duration = performance.now() - start
403
+
404
+ expect(duration).toBeLessThan(300)
405
+ })
406
+
407
+ it('search responds within 500ms', async () => {
408
+ const start = performance.now()
409
+ await searchItems({ query: 'test' })
410
+ const duration = performance.now() - start
411
+
412
+ expect(duration).toBeLessThan(500)
413
+ })
414
+ })
415
+ ```
416
+
417
+ ### Lighthouse CI (E2E)
418
+
419
+ ```typescript
420
+ // e2e/performance.spec.ts
421
+ test('meets Lighthouse performance targets', async ({ page }) => {
422
+ await page.goto('{{baseUrl}}/dashboard')
423
+
424
+ // Check critical metrics
425
+ const metrics = await page.evaluate(() => {
426
+ return new Promise(resolve => {
427
+ new PerformanceObserver(list => {
428
+ const entries = list.getEntries()
429
+ resolve({
430
+ lcp: entries.find(e => e.entryType === 'largest-contentful-paint')?.startTime,
431
+ fid: entries.find(e => e.entryType === 'first-input')?.processingStart,
432
+ })
433
+ }).observe({ entryTypes: ['largest-contentful-paint', 'first-input'] })
434
+ })
435
+ })
436
+ })
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Performance Benchmarks
442
+
443
+ | Metric | Target | Priority |
444
+ |--------|--------|----------|
445
+ | API simple queries | p95 < 300ms | HIGH |
446
+ | API mutations | p95 < 500ms | HIGH |
447
+ | API complex queries | p95 < 1000ms | MEDIUM |
448
+ | Lighthouse Performance | >= 80 | HIGH |
449
+ | Lighthouse Accessibility | >= 85 | HIGH |
450
+ | Lighthouse Best Practices | >= 90 | MEDIUM |
451
+ | Time to Interactive (TTI) | < 3.5s | HIGH |
452
+ | Largest Contentful Paint (LCP) | < 2.5s | HIGH |
453
+ | Cumulative Layout Shift (CLS) | < 0.1 | MEDIUM |
454
+
455
+ ---
456
+
457
+ ## CI Integration
458
+
459
+ ### Running Tests in CI
460
+
461
+ {{testCommands}}
462
+
463
+ ```bash
464
+ # Unit + Integration tests
465
+ {{testCommand}}
466
+
467
+ # With coverage report
468
+ {{testCoverageCommand}}
469
+
470
+ # E2E tests (headless)
471
+ CI=true {{e2eCommand}}
472
+
473
+ # Run only changed files (for PR checks)
474
+ {{testCommand}} --changed
475
+
476
+ # Generate coverage report for upload
477
+ {{testCoverageCommand}} --reporter=json --outputFile=coverage.json
478
+ ```
479
+
480
+ ### CI Pipeline Stages
481
+
482
+ ```
483
+ 1. Lint → eslint, prettier check
484
+ 2. Type Check → tsc --noEmit
485
+ 3. Unit Tests → {{testCommand}} --coverage
486
+ 4. Build → build the application
487
+ 5. E2E Tests → {{e2eCommand}} (against build)
488
+ 6. Coverage Gate → check thresholds
489
+ ```
490
+
491
+ ### Coverage Report Upload
492
+
493
+ ```yaml
494
+ # Example CI step (GitHub Actions)
495
+ - name: Upload coverage
496
+ uses: codecov/codecov-action@v4
497
+ with:
498
+ file: ./coverage/coverage-final.json
499
+ fail_ci_if_error: true
500
+ ```
501
+
502
+ ---
503
+
504
+ ## When to Write Tests
505
+
506
+ | Scenario | Test Type | Priority |
507
+ |----------|-----------|----------|
508
+ | New API endpoint / route | Unit + Integration | REQUIRED |
509
+ | New utility function | Unit | REQUIRED |
510
+ | New custom hook | Unit | REQUIRED |
511
+ | New page / screen | Component + E2E | REQUIRED |
512
+ | New complex component | Component | REQUIRED |
513
+ | New form | Component (validation) + E2E | REQUIRED |
514
+ | Bug fix | Regression test first | REQUIRED |
515
+ | Refactoring | Verify existing tests pass | REQUIRED |
516
+ | Performance optimization | Benchmark before/after | RECOMMENDED |
517
+ | Security fix | Security test case | REQUIRED |
518
+ | Config change | Verify build still works | REQUIRED |
519
+ | Dependency update | Run full suite | REQUIRED |
520
+
521
+ ---
522
+
523
+ ## QA Program Integration
524
+
525
+ Test standards are part of a broader QA program. When bootstrapping a new project, create these QA resources:
526
+
527
+ ### Resource Files to Generate
528
+
529
+ | Resource | Purpose |
530
+ |----------|---------|
531
+ | `test-matrix.md` | Map features to test cases |
532
+ | `security-checklist.md` | OWASP + auth + isolation checks |
533
+ | `nielsen-heuristics.md` | UX quality rubric |
534
+ | `performance-benchmarks.md` | Response time budgets |
535
+ | `qa-report-template.md` | Standardized QA report format |
536
+
537
+ ### QA Agents (When Using Agent Framework)
538
+
539
+ | Agent | Role |
540
+ |-------|------|
541
+ | `qa-test-executor` | Runs functional + API tests |
542
+ | `qa-security-auditor` | Auth boundaries + data isolation |
543
+ | `qa-ux-optimizer` | Heuristic evaluation + WCAG |
544
+ | `qa-report-writer` | Consolidates findings |
545
+ | `qa-fix-planner` | Prioritizes defects |
546
+ | `qa-verifier` | Re-runs failed tests after fixes |
547
+
548
+ ### QA Cycle
549
+
550
+ ```
551
+ 1. Plan tests (from PRD / feature spec)
552
+ 2. Write tests (using skill templates)
553
+ 3. Execute tests (via test runner + E2E)
554
+ 4. Report findings (qa-report-template)
555
+ 5. Fix defects (prioritized by severity)
556
+ 6. Re-verify fixes (regression suite)
557
+ 7. Update test matrix (mark as covered)
558
+ ```
559
+
560
+ ---
561
+
562
+ ## Test Configuration Templates
563
+
564
+ ### Vitest Config
565
+
566
+ ```typescript
567
+ // vitest.config.ts
568
+ import { defineConfig } from 'vitest/config'
569
+ import path from 'path'
570
+
571
+ export default defineConfig({
572
+ test: {
573
+ globals: true,
574
+ environment: 'jsdom', // or 'node' for API tests
575
+ setupFiles: ['./test/setup.ts'],
576
+ include: ['**/*.test.{ts,tsx}'],
577
+ exclude: ['node_modules', 'dist', 'e2e'],
578
+ coverage: {
579
+ provider: 'v8',
580
+ reporter: ['text', 'json', 'html'],
581
+ thresholds: {
582
+ statements: 80,
583
+ branches: 70,
584
+ functions: 80,
585
+ lines: 80,
586
+ },
587
+ },
588
+ },
589
+ resolve: {
590
+ alias: {
591
+ '@': path.resolve(__dirname, './src'),
592
+ },
593
+ },
594
+ })
595
+ ```
596
+
597
+ ### Jest Config
598
+
599
+ ```typescript
600
+ // jest.config.ts
601
+ import type { Config } from 'jest'
602
+
603
+ const config: Config = {
604
+ preset: 'ts-jest',
605
+ testEnvironment: 'jsdom', // or 'node'
606
+ setupFilesAfterSetup: ['./test/setup.ts'],
607
+ moduleNameMapper: {
608
+ '^@/(.*)$': '<rootDir>/src/$1',
609
+ },
610
+ coverageThreshold: {
611
+ global: {
612
+ statements: 80,
613
+ branches: 70,
614
+ functions: 80,
615
+ lines: 80,
616
+ },
617
+ },
618
+ collectCoverageFrom: [
619
+ 'src/**/*.{ts,tsx}',
620
+ '!src/**/*.d.ts',
621
+ '!src/**/index.ts',
622
+ ],
623
+ }
624
+
625
+ export default config
626
+ ```
627
+
628
+ ### Test Setup File
629
+
630
+ ```typescript
631
+ // test/setup.ts
632
+ import '@testing-library/jest-dom' // or '@testing-library/jest-dom/vitest'
633
+
634
+ // Global mocks
635
+ beforeAll(() => {
636
+ // Mock environment variables for tests
637
+ process.env.NODE_ENV = 'test'
638
+ })
639
+
640
+ afterEach(() => {
641
+ // Clean up between tests
642
+ vi.clearAllMocks() // or jest.clearAllMocks()
643
+ })
644
+ ```
645
+
646
+ ---
647
+
648
+ ## See Also
649
+
650
+ - `unit-test.md` - Detailed unit test patterns
651
+ - `component-test.md` - Component test patterns
652
+ - `e2e-test.md` - End-to-end test patterns