cfsa-antigravity 2.0.0 → 2.2.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 (116) hide show
  1. package/README.md +14 -0
  2. package/package.json +1 -1
  3. package/template/.agent/instructions/commands.md +8 -32
  4. package/template/.agent/instructions/example.md +21 -0
  5. package/template/.agent/instructions/patterns.md +3 -3
  6. package/template/.agent/instructions/tech-stack.md +71 -23
  7. package/template/.agent/instructions/workflow.md +12 -1
  8. package/template/.agent/rules/completion-checklist.md +6 -0
  9. package/template/.agent/rules/security-first.md +3 -3
  10. package/template/.agent/rules/vertical-slices.md +1 -1
  11. package/template/.agent/skill-library/MANIFEST.md +6 -0
  12. package/template/.agent/skill-library/stack/devops/git-advanced/SKILL.md +972 -0
  13. package/template/.agent/skill-library/stack/devops/git-workflow/SKILL.md +420 -0
  14. package/template/.agent/skills/api-versioning/SKILL.md +44 -298
  15. package/template/.agent/skills/api-versioning/references/typescript.md +157 -0
  16. package/template/.agent/skills/architecture-mapping/SKILL.md +13 -13
  17. package/template/.agent/skills/bootstrap-agents/SKILL.md +151 -152
  18. package/template/.agent/skills/clean-code/SKILL.md +64 -118
  19. package/template/.agent/skills/clean-code/references/typescript.md +126 -0
  20. package/template/.agent/skills/database-schema-design/SKILL.md +93 -317
  21. package/template/.agent/skills/database-schema-design/references/relational.md +228 -0
  22. package/template/.agent/skills/error-handling-patterns/SKILL.md +62 -557
  23. package/template/.agent/skills/error-handling-patterns/references/go.md +162 -0
  24. package/template/.agent/skills/error-handling-patterns/references/python.md +262 -0
  25. package/template/.agent/skills/error-handling-patterns/references/rust.md +112 -0
  26. package/template/.agent/skills/error-handling-patterns/references/typescript.md +178 -0
  27. package/template/.agent/skills/idea-extraction/SKILL.md +322 -224
  28. package/template/.agent/skills/logging-best-practices/SKILL.md +108 -767
  29. package/template/.agent/skills/logging-best-practices/references/go.md +49 -0
  30. package/template/.agent/skills/logging-best-practices/references/python.md +52 -0
  31. package/template/.agent/skills/logging-best-practices/references/typescript.md +215 -0
  32. package/template/.agent/skills/migration-management/SKILL.md +127 -311
  33. package/template/.agent/skills/migration-management/references/relational.md +214 -0
  34. package/template/.agent/skills/parallel-feature-development/SKILL.md +34 -43
  35. package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +1 -1
  36. package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +2 -2
  37. package/template/.agent/skills/pipeline-rubrics/references/scoring.md +1 -1
  38. package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +2 -1
  39. package/template/.agent/skills/prd-templates/SKILL.md +23 -6
  40. package/template/.agent/skills/prd-templates/references/be-spec-template.md +2 -2
  41. package/template/.agent/skills/prd-templates/references/decomposition-templates.md +2 -2
  42. package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +2 -0
  43. package/template/.agent/skills/prd-templates/references/fe-spec-template.md +1 -1
  44. package/template/.agent/skills/prd-templates/references/fractal-cx-template.md +58 -0
  45. package/template/.agent/skills/prd-templates/references/fractal-feature-template.md +93 -0
  46. package/template/.agent/skills/prd-templates/references/fractal-node-index-template.md +55 -0
  47. package/template/.agent/skills/prd-templates/references/ideation-crosscut-template.md +26 -47
  48. package/template/.agent/skills/prd-templates/references/ideation-index-template.md +47 -31
  49. package/template/.agent/skills/prd-templates/references/operational-templates.md +1 -1
  50. package/template/.agent/skills/prd-templates/references/placeholder-workflow-mapping.md +50 -21
  51. package/template/.agent/skills/prd-templates/references/skill-loading-protocol.md +32 -0
  52. package/template/.agent/skills/prd-templates/references/slice-completion-gates.md +29 -0
  53. package/template/.agent/skills/prd-templates/references/spec-coverage-sweep.md +3 -3
  54. package/template/.agent/skills/prd-templates/references/tdd-testing-policy.md +39 -0
  55. package/template/.agent/skills/prd-templates/references/vision-template.md +8 -8
  56. package/template/.agent/skills/regex-patterns/SKILL.md +122 -540
  57. package/template/.agent/skills/regex-patterns/references/go.md +44 -0
  58. package/template/.agent/skills/regex-patterns/references/javascript.md +63 -0
  59. package/template/.agent/skills/regex-patterns/references/python.md +77 -0
  60. package/template/.agent/skills/regex-patterns/references/rust.md +43 -0
  61. package/template/.agent/skills/resolve-ambiguity/SKILL.md +1 -1
  62. package/template/.agent/skills/session-continuity/SKILL.md +11 -9
  63. package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +2 -2
  64. package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +1 -1
  65. package/template/.agent/skills/session-continuity/protocols/05-session-close.md +1 -1
  66. package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +1 -1
  67. package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +57 -78
  68. package/template/.agent/skills/session-continuity/protocols/11-parallel-synthesis.md +1 -1
  69. package/template/.agent/skills/spec-writing/SKILL.md +1 -1
  70. package/template/.agent/skills/tdd-workflow/SKILL.md +94 -317
  71. package/template/.agent/skills/tdd-workflow/references/typescript.md +231 -0
  72. package/template/.agent/skills/testing-strategist/SKILL.md +74 -687
  73. package/template/.agent/skills/testing-strategist/references/typescript.md +328 -0
  74. package/template/.agent/skills/workflow-automation/SKILL.md +62 -154
  75. package/template/.agent/skills/workflow-automation/references/inngest.md +88 -0
  76. package/template/.agent/skills/workflow-automation/references/temporal.md +64 -0
  77. package/template/.agent/workflows/bootstrap-agents-fill.md +85 -143
  78. package/template/.agent/workflows/bootstrap-agents-provision.md +90 -107
  79. package/template/.agent/workflows/create-prd-architecture.md +23 -16
  80. package/template/.agent/workflows/create-prd-compile.md +11 -12
  81. package/template/.agent/workflows/create-prd-design-system.md +1 -1
  82. package/template/.agent/workflows/create-prd-security.md +9 -11
  83. package/template/.agent/workflows/create-prd-stack.md +10 -4
  84. package/template/.agent/workflows/create-prd.md +9 -9
  85. package/template/.agent/workflows/decompose-architecture-structure.md +4 -6
  86. package/template/.agent/workflows/decompose-architecture-validate.md +18 -1
  87. package/template/.agent/workflows/decompose-architecture.md +18 -3
  88. package/template/.agent/workflows/evolve-contract.md +11 -11
  89. package/template/.agent/workflows/evolve-feature-classify.md +14 -6
  90. package/template/.agent/workflows/ideate-discover.md +72 -107
  91. package/template/.agent/workflows/ideate-extract.md +84 -63
  92. package/template/.agent/workflows/ideate-validate.md +26 -22
  93. package/template/.agent/workflows/ideate.md +9 -9
  94. package/template/.agent/workflows/implement-slice-setup.md +25 -23
  95. package/template/.agent/workflows/implement-slice-tdd.md +73 -89
  96. package/template/.agent/workflows/implement-slice.md +4 -4
  97. package/template/.agent/workflows/plan-phase-preflight.md +6 -2
  98. package/template/.agent/workflows/plan-phase-write.md +6 -8
  99. package/template/.agent/workflows/remediate-pipeline-assess.md +2 -1
  100. package/template/.agent/workflows/resolve-ambiguity.md +2 -2
  101. package/template/.agent/workflows/update-architecture-map.md +22 -5
  102. package/template/.agent/workflows/validate-phase-quality.md +155 -0
  103. package/template/.agent/workflows/validate-phase-readiness.md +167 -0
  104. package/template/.agent/workflows/validate-phase.md +19 -157
  105. package/template/.agent/workflows/verify-infrastructure.md +10 -10
  106. package/template/.agent/workflows/write-architecture-spec-design.md +23 -14
  107. package/template/.agent/workflows/write-be-spec-classify.md +25 -21
  108. package/template/.agent/workflows/write-be-spec.md +1 -1
  109. package/template/.agent/workflows/write-fe-spec-classify.md +6 -12
  110. package/template/.agent/workflows/write-fe-spec-write.md +1 -1
  111. package/template/AGENTS.md +6 -2
  112. package/template/GEMINI.md +5 -3
  113. package/template/docs/README.md +10 -10
  114. package/template/docs/kit-architecture.md +126 -33
  115. package/template/docs/plans/ideation/README.md +8 -3
  116. package/template/.agent/skills/prd-templates/references/ideation-domain-template.md +0 -55
@@ -6,7 +6,18 @@ version: 1.0.0
6
6
 
7
7
  # Testing Strategist
8
8
 
9
- Test the right things at the right level - write tests that give you confidence to ship.
9
+ Test the right things at the right level write tests that give you confidence to ship.
10
+
11
+ ## Stack-Specific References
12
+
13
+ After reading the methodology below, read the reference matching your surface's Languages column in the surface stack map (`.agent/instructions/tech-stack.md`):
14
+
15
+ | Language | Reference |
16
+ |----------|-----------|
17
+ | TypeScript / JavaScript | `references/typescript.md` |
18
+ | Python | `references/python.md` |
19
+ | Go | `references/go.md` |
20
+ | Rust | `references/rust.md` |
10
21
 
11
22
  ## Core Principle
12
23
 
@@ -14,11 +25,11 @@ Test the right things at the right level - write tests that give you confidence
14
25
 
15
26
  Tests should be:
16
27
 
17
- - **Fast** - Run in milliseconds (unit) to seconds (integration) to minutes (E2E)
18
- - **Isolated** - Test one thing at a time
19
- - **Repeatable** - Same input = same output
20
- - **Self-checking** - Pass/fail automatically, no manual verification
21
- - **Timely** - Written alongside code (or before, with TDD)
28
+ - **Fast** Run in milliseconds (unit) to seconds (integration) to minutes (E2E)
29
+ - **Isolated** Test one thing at a time
30
+ - **Repeatable** Same input = same output
31
+ - **Self-checking** Pass/fail automatically, no manual verification
32
+ - **Timely** Written alongside code (or before, with TDD)
22
33
 
23
34
  ---
24
35
 
@@ -57,194 +68,24 @@ Test individual functions, components, or classes in isolation.
57
68
  **Good candidates:**
58
69
 
59
70
  - ✅ Business logic functions (calculations, validation, transformations)
60
- - ✅ Utility functions (formatDate, parseUrl, etc.)
61
- - ✅ React components (rendering, props, state)
62
- - ✅ Hooks (custom React hooks)
71
+ - ✅ Utility functions (formatting, parsing, etc.)
72
+ - ✅ UI components (rendering, props, state)
63
73
  - ✅ Pure functions (same input = same output)
64
74
 
65
75
  **Skip:**
66
76
 
67
77
  - ❌ Third-party libraries (assume they work)
68
- - ❌ Framework internals (React, Next.js)
78
+ - ❌ Framework internals
69
79
  - ❌ Simple getters/setters with no logic
70
80
 
71
- ### Unit Test Examples
72
-
73
- #### Testing Business Logic (Jest + TypeScript)
74
-
75
- ```typescript
76
- // src/lib/pricing.ts
77
- export function calculateTotal(items: { price: number; quantity: number }[]) {
78
- return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
79
- }
80
-
81
- export function applyDiscount(total: number, discountPercent: number) {
82
- if (discountPercent < 0 || discountPercent > 100) {
83
- throw new Error('Invalid discount percentage')
84
- }
85
- return total * (1 - discountPercent / 100)
86
- }
87
-
88
- // src/lib/pricing.test.ts
89
- import { calculateTotal, applyDiscount } from './pricing'
90
-
91
- describe('calculateTotal', () => {
92
- it('calculates total for single item', () => {
93
- const items = [{ price: 10, quantity: 2 }]
94
- expect(calculateTotal(items)).toBe(20)
95
- })
96
-
97
- it('calculates total for multiple items', () => {
98
- const items = [
99
- { price: 10, quantity: 2 },
100
- { price: 5, quantity: 3 }
101
- ]
102
- expect(calculateTotal(items)).toBe(35)
103
- })
104
-
105
- it('returns 0 for empty array', () => {
106
- expect(calculateTotal([])).toBe(0)
107
- })
108
- })
109
-
110
- describe('applyDiscount', () => {
111
- it('applies discount correctly', () => {
112
- expect(applyDiscount(100, 20)).toBe(80)
113
- })
114
-
115
- it('throws error for invalid discount', () => {
116
- expect(() => applyDiscount(100, -10)).toThrow('Invalid discount')
117
- expect(() => applyDiscount(100, 150)).toThrow('Invalid discount')
118
- })
119
- })
120
- ```
121
-
122
- #### Testing React Components (Jest + React Testing Library)
123
-
124
- ```typescript
125
- // src/components/Button.tsx
126
- export function Button({
127
- children,
128
- variant = 'primary',
129
- onClick
130
- }: {
131
- children: React.ReactNode
132
- variant?: 'primary' | 'secondary'
133
- onClick?: () => void
134
- }) {
135
- return (
136
- <button
137
- className={`btn btn-${variant}`}
138
- onClick={onClick}
139
- >
140
- {children}
141
- </button>
142
- )
143
- }
144
-
145
- // src/components/Button.test.tsx
146
- import { render, screen, fireEvent } from '@testing-library/react'
147
- import { Button } from './Button'
148
-
149
- describe('Button', () => {
150
- it('renders with correct text', () => {
151
- render(<Button>Click me</Button>)
152
- expect(screen.getByText('Click me')).toBeInTheDocument()
153
- })
154
-
155
- it('applies primary variant by default', () => {
156
- render(<Button>Click me</Button>)
157
- expect(screen.getByRole('button')).toHaveClass('btn-primary')
158
- })
159
-
160
- it('applies secondary variant when specified', () => {
161
- render(<Button variant="secondary">Click me</Button>)
162
- expect(screen.getByRole('button')).toHaveClass('btn-secondary')
163
- })
164
-
165
- it('calls onClick when clicked', () => {
166
- const handleClick = jest.fn()
167
- render(<Button onClick={handleClick}>Click me</Button>)
168
-
169
- fireEvent.click(screen.getByRole('button'))
170
- expect(handleClick).toHaveBeenCalledTimes(1)
171
- })
172
- })
173
- ```
174
-
175
- #### Testing Custom Hooks
176
-
177
- ```typescript
178
- // src/hooks/useCounter.ts
179
- import { useState } from 'react'
180
-
181
- export function useCounter(initialValue = 0) {
182
- const [count, setCount] = useState(initialValue)
183
-
184
- const increment = () => setCount(c => c + 1)
185
- const decrement = () => setCount(c => c - 1)
186
- const reset = () => setCount(initialValue)
187
-
188
- return { count, increment, decrement, reset }
189
- }
190
-
191
- // src/hooks/useCounter.test.ts
192
- import { renderHook, act } from '@testing-library/react'
193
- import { useCounter } from './useCounter'
194
-
195
- describe('useCounter', () => {
196
- it('initializes with default value', () => {
197
- const { result } = renderHook(() => useCounter())
198
- expect(result.current.count).toBe(0)
199
- })
200
-
201
- it('initializes with custom value', () => {
202
- const { result } = renderHook(() => useCounter(10))
203
- expect(result.current.count).toBe(10)
204
- })
205
-
206
- it('increments count', () => {
207
- const { result } = renderHook(() => useCounter())
208
-
209
- act(() => {
210
- result.current.increment()
211
- })
212
-
213
- expect(result.current.count).toBe(1)
214
- })
215
-
216
- it('decrements count', () => {
217
- const { result } = renderHook(() => useCounter(5))
218
-
219
- act(() => {
220
- result.current.decrement()
221
- })
222
-
223
- expect(result.current.count).toBe(4)
224
- })
225
-
226
- it('resets to initial value', () => {
227
- const { result } = renderHook(() => useCounter(10))
228
-
229
- act(() => {
230
- result.current.increment()
231
- result.current.increment()
232
- result.current.reset()
233
- })
234
-
235
- expect(result.current.count).toBe(10)
236
- })
237
- })
238
- ```
239
-
240
81
  ### Unit Test Best Practices
241
82
 
242
83
  ✅ **Do:**
243
84
 
244
85
  - Test behavior, not implementation
245
- - Use descriptive test names (it should...)
86
+ - Use descriptive test names
246
87
  - Follow AAA pattern: Arrange, Act, Assert
247
- - Test edge cases (empty arrays, null, negative numbers)
88
+ - Test edge cases (empty, null, negative, boundary values)
248
89
  - Keep tests simple and readable
249
90
 
250
91
  ❌ **Don't:**
@@ -260,184 +101,22 @@ describe('useCounter', () => {
260
101
 
261
102
  ### What to Test
262
103
 
263
- Test multiple units working together - typically API routes, database operations, or service integrations.
104
+ Test multiple units working together typically API routes, database operations, or service integrations.
264
105
 
265
106
  **Good candidates:**
266
107
 
267
- - ✅ API endpoints (request → controller → database → response)
108
+ - ✅ API endpoints (request → handler → database → response)
268
109
  - ✅ Database operations (queries, transactions)
269
- - ✅ Third-party integrations (Stripe, SendGrid)
110
+ - ✅ Third-party integrations (payment, email, etc.)
270
111
  - ✅ Authentication flows
271
112
  - ✅ File upload/download
272
113
 
273
- ### Integration Test Examples
274
-
275
- #### Testing API Routes (Next.js + Supertest)
276
-
277
- ```typescript
278
- // app/api/posts/route.ts
279
- export async function GET() {
280
- const posts = await db.post.findMany({
281
- include: { author: true },
282
- orderBy: { createdAt: 'desc' }
283
- })
284
- return Response.json(posts)
285
- }
286
-
287
- export async function POST(request: Request) {
288
- const body = await request.json()
289
-
290
- // Validate
291
- const result = PostSchema.safeParse(body)
292
- if (!result.success) {
293
- return Response.json({ errors: result.error.issues }, { status: 400 })
294
- }
295
-
296
- // Create post
297
- const post = await db.post.create({
298
- data: {
299
- title: result.data.title,
300
- content: result.data.content,
301
- authorId: request.user.id
302
- }
303
- })
304
-
305
- return Response.json(post, { status: 201 })
306
- }
307
-
308
- // app/api/posts/route.test.ts
309
- import { testClient } from '@/lib/test-utils'
310
-
311
- describe('POST /api/posts', () => {
312
- beforeEach(async () => {
313
- // Clean database before each test
314
- await db.post.deleteMany()
315
- })
316
-
317
- it('creates a new post', async () => {
318
- const response = await testClient
319
- .post('/api/posts')
320
- .set('Authorization', `Bearer ${authToken}`)
321
- .send({
322
- title: 'Test Post',
323
- content: 'This is a test post'
324
- })
325
-
326
- expect(response.status).toBe(201)
327
- expect(response.body).toMatchObject({
328
- title: 'Test Post',
329
- content: 'This is a test post'
330
- })
331
-
332
- // Verify in database
333
- const posts = await db.post.findMany()
334
- expect(posts).toHaveLength(1)
335
- expect(posts[0].title).toBe('Test Post')
336
- })
337
-
338
- it('returns 400 for invalid data', async () => {
339
- const response = await testClient
340
- .post('/api/posts')
341
- .set('Authorization', `Bearer ${authToken}`)
342
- .send({
343
- title: '' // Invalid: empty title
344
- })
345
-
346
- expect(response.status).toBe(400)
347
- expect(response.body.errors).toBeDefined()
348
- })
349
-
350
- it('returns 401 for unauthenticated request', async () => {
351
- const response = await testClient.post('/api/posts').send({
352
- title: 'Test',
353
- content: 'Test'
354
- })
355
-
356
- expect(response.status).toBe(401)
357
- })
358
- })
359
-
360
- describe('GET /api/posts', () => {
361
- beforeEach(async () => {
362
- await db.post.deleteMany()
363
-
364
- // Seed test data
365
- await db.post.createMany({
366
- data: [
367
- { title: 'Post 1', content: 'Content 1', authorId: user.id },
368
- { title: 'Post 2', content: 'Content 2', authorId: user.id }
369
- ]
370
- })
371
- })
372
-
373
- it('returns all posts', async () => {
374
- const response = await testClient.get('/api/posts')
375
-
376
- expect(response.status).toBe(200)
377
- expect(response.body).toHaveLength(2)
378
- expect(response.body[0].author).toBeDefined()
379
- })
380
- })
381
- ```
382
-
383
- #### Testing Database Operations
384
-
385
- ```typescript
386
- // src/lib/repositories/userRepository.test.ts
387
- import { db } from '@/lib/db'
388
- import { createUser, findUserByEmail, updateUser } from './userRepository'
389
-
390
- describe('userRepository', () => {
391
- beforeEach(async () => {
392
- await db.user.deleteMany()
393
- })
394
-
395
- afterAll(async () => {
396
- await db.$disconnect()
397
- })
398
-
399
- describe('createUser', () => {
400
- it('creates user with hashed password', async () => {
401
- const user = await createUser({
402
- email: 'test@example.com',
403
- password: 'password123'
404
- })
405
-
406
- expect(user.email).toBe('test@example.com')
407
- expect(user.password).not.toBe('password123') // Should be hashed
408
- expect(user.password).toMatch(/^\$2[aby]/) // bcrypt hash format
409
- })
410
-
411
- it('throws error for duplicate email', async () => {
412
- await createUser({ email: 'test@example.com', password: 'pass' })
413
-
414
- await expect(createUser({ email: 'test@example.com', password: 'pass' })).rejects.toThrow()
415
- })
416
- })
417
-
418
- describe('findUserByEmail', () => {
419
- it('finds existing user', async () => {
420
- await createUser({ email: 'test@example.com', password: 'pass' })
421
-
422
- const user = await findUserByEmail('test@example.com')
423
- expect(user).toBeDefined()
424
- expect(user?.email).toBe('test@example.com')
425
- })
426
-
427
- it('returns null for non-existent user', async () => {
428
- const user = await findUserByEmail('nonexistent@example.com')
429
- expect(user).toBeNull()
430
- })
431
- })
432
- })
433
- ```
434
-
435
114
  ### Integration Test Best Practices
436
115
 
437
116
  ✅ **Do:**
438
117
 
439
118
  - Use test database (separate from development/production)
440
- - Clean up test data (beforeEach/afterEach)
119
+ - Clean up test data before/after each test
441
120
  - Test happy path + error cases
442
121
  - Test authentication/authorization
443
122
  - Use factories/fixtures for test data
@@ -468,113 +147,20 @@ Test complete user journeys through the actual UI.
468
147
  - ❌ Every possible UI interaction (too slow/brittle)
469
148
  - ❌ Edge cases (cover with unit/integration tests)
470
149
 
471
- ### E2E Test Examples (Playwright)
472
-
473
- ```typescript
474
- // tests/e2e/auth.spec.ts
475
- import { test, expect } from '@playwright/test'
476
-
477
- test.describe('Authentication', () => {
478
- test('user can sign up and log in', async ({ page }) => {
479
- // Navigate to signup
480
- await page.goto('/signup')
481
-
482
- // Fill signup form
483
- await page.fill('input[name="email"]', 'test@example.com')
484
- await page.fill('input[name="password"]', 'SecurePass123!')
485
- await page.fill('input[name="confirmPassword"]', 'SecurePass123!')
486
-
487
- // Submit form
488
- await page.click('button[type="submit"]')
489
-
490
- // Should redirect to dashboard
491
- await expect(page).toHaveURL('/dashboard')
492
- await expect(page.locator('h1')).toContainText('Welcome')
493
-
494
- // Logout
495
- await page.click('[data-testid="logout-button"]')
496
-
497
- // Should redirect to login
498
- await expect(page).toHaveURL('/login')
499
-
500
- // Login again
501
- await page.fill('input[name="email"]', 'test@example.com')
502
- await page.fill('input[name="password"]', 'SecurePass123!')
503
- await page.click('button[type="submit"]')
504
-
505
- // Should be back at dashboard
506
- await expect(page).toHaveURL('/dashboard')
507
- })
508
-
509
- test('shows error for invalid credentials', async ({ page }) => {
510
- await page.goto('/login')
511
-
512
- await page.fill('input[name="email"]', 'wrong@example.com')
513
- await page.fill('input[name="password"]', 'wrongpassword')
514
- await page.click('button[type="submit"]')
515
-
516
- // Should show error message
517
- await expect(page.locator('[role="alert"]')).toContainText('Invalid credentials')
518
-
519
- // Should stay on login page
520
- await expect(page).toHaveURL('/login')
521
- })
522
- })
523
-
524
- // tests/e2e/checkout.spec.ts
525
- test.describe('Checkout Flow', () => {
526
- test('user can complete purchase', async ({ page }) => {
527
- // Login first
528
- await page.goto('/login')
529
- await page.fill('input[name="email"]', 'test@example.com')
530
- await page.fill('input[name="password"]', 'password')
531
- await page.click('button[type="submit"]')
532
-
533
- // Add product to cart
534
- await page.goto('/products')
535
- await page.click('[data-testid="product-1"] button:text("Add to Cart")')
536
-
537
- // Verify cart badge
538
- await expect(page.locator('[data-testid="cart-badge"]')).toContainText('1')
539
-
540
- // Go to checkout
541
- await page.click('[data-testid="cart-button"]')
542
- await page.click('button:text("Checkout")')
543
-
544
- // Fill shipping info
545
- await page.fill('input[name="address"]', '123 Main St')
546
- await page.fill('input[name="city"]', 'San Francisco')
547
- await page.fill('input[name="zip"]', '94103')
548
-
549
- // Fill payment info (test mode)
550
- await page.fill('input[name="cardNumber"]', '4242424242424242')
551
- await page.fill('input[name="expiry"]', '12/25')
552
- await page.fill('input[name="cvc"]', '123')
553
-
554
- // Submit order
555
- await page.click('button:text("Place Order")')
556
-
557
- // Should see confirmation
558
- await expect(page).toHaveURL(/\/orders\/\d+/)
559
- await expect(page.locator('h1')).toContainText('Order Confirmed')
560
- })
561
- })
562
- ```
563
-
564
150
  ### E2E Test Best Practices
565
151
 
566
152
  ✅ **Do:**
567
153
 
568
154
  - Test critical paths only (< 20 tests)
569
- - Use data-testid attributes (stable selectors)
155
+ - Use stable selectors (test IDs, roles, labels — not CSS classes)
570
156
  - Run in CI/CD pipeline
571
- - Test across browsers (Chrome, Firefox, Safari)
157
+ - Test across browsers if web surface
572
158
  - Take screenshots on failure
573
159
 
574
160
  ❌ **Don't:**
575
161
 
576
162
  - Test every UI variation
577
- - Use fragile selectors (text content, nth-child)
163
+ - Use fragile selectors (nth-child, CSS classes)
578
164
  - Run E2E tests on every commit (too slow)
579
165
  - Ignore flaky tests (fix or remove them)
580
166
 
@@ -584,51 +170,10 @@ test.describe('Checkout Flow', () => {
584
170
 
585
171
  ### The Red-Green-Refactor Cycle
586
172
 
587
- 1. **Red:** Write a failing test
173
+ 1. **Red:** Write a failing test that defines expected behavior
588
174
  2. **Green:** Write minimal code to make it pass
589
175
  3. **Refactor:** Improve code while keeping tests green
590
176
 
591
- ### TDD Example
592
-
593
- ```typescript
594
- // 1. RED: Write failing test first
595
- describe('formatCurrency', () => {
596
- it('formats number as USD currency', () => {
597
- expect(formatCurrency(1234.56)).toBe('$1,234.56')
598
- })
599
- })
600
-
601
- // Run test: FAILS (formatCurrency doesn't exist)
602
-
603
- // 2. GREEN: Write minimal implementation
604
- export function formatCurrency(amount: number): string {
605
- return `$${amount.toLocaleString('en-US', { minimumFractionDigits: 2 })}`
606
- }
607
-
608
- // Run test: PASSES
609
-
610
- // 3. REFACTOR: Improve implementation
611
- export function formatCurrency(
612
- amount: number,
613
- currency: string = 'USD',
614
- locale: string = 'en-US'
615
- ): string {
616
- return new Intl.NumberFormat(locale, {
617
- style: 'currency',
618
- currency
619
- }).format(amount)
620
- }
621
-
622
- // Add more tests
623
- it('formats EUR currency', () => {
624
- expect(formatCurrency(1234.56, 'EUR', 'de-DE')).toBe('1.234,56 €')
625
- })
626
-
627
- it('handles negative amounts', () => {
628
- expect(formatCurrency(-100)).toBe('-$100.00')
629
- })
630
- ```
631
-
632
177
  ### When to Use TDD
633
178
 
634
179
  **Good for:**
@@ -653,67 +198,46 @@ it('handles negative amounts', () => {
653
198
  - ✅ External APIs (slow, unreliable, cost money)
654
199
  - ✅ Time/randomness (make tests deterministic)
655
200
  - ✅ File system operations
656
- - ✅ Database (in unit tests only)
657
-
658
- ### Mock Examples (Jest)
201
+ - ✅ Database (in unit tests only — NEVER in integration tests)
659
202
 
660
- ```typescript
661
- // Mock external API
662
- import { fetchUserData } from '@/lib/api'
203
+ ### Anti-Mock-Abuse Rules
663
204
 
664
- jest.mock('@/lib/api')
665
- const mockFetchUserData = fetchUserData as jest.MockedFunction<typeof fetchUserData>
666
-
667
- it('displays user data', async () => {
668
- mockFetchUserData.mockResolvedValue({
669
- id: '1',
670
- name: 'John Doe',
671
- email: 'john@example.com'
672
- })
673
-
674
- render(<UserProfile userId="1" />)
675
-
676
- await waitFor(() => {
677
- expect(screen.getByText('John Doe')).toBeInTheDocument()
678
- })
679
- })
680
-
681
- // Mock Date
682
- beforeAll(() => {
683
- jest.useFakeTimers()
684
- jest.setSystemTime(new Date('2024-01-01'))
685
- })
686
-
687
- afterAll(() => {
688
- jest.useRealTimers()
689
- })
690
-
691
- it('shows correct date', () => {
692
- expect(getCurrentDate()).toBe('2024-01-01')
693
- })
694
-
695
- // Mock Math.random
696
- const mockRandom = jest.spyOn(Math, 'random')
697
- mockRandom.mockReturnValue(0.5)
698
-
699
- expect(generateRandomId()).toBe('expected-id-with-0.5-random')
700
-
701
- mockRandom.mockRestore()
702
- ```
205
+ 1. **Never mock what you own** — if you wrote the module, test it directly
206
+ 2. **One mock = one boundary** only mock at the boundary between your code and external systems
207
+ 3. **Verify mock interactions** — assert that mocked deps were called with correct arguments
208
+ 4. **Mock return values, not internals** mock the return value, not the internal implementation
703
209
 
704
210
  ### Mocking Best Practices
705
211
 
706
212
  ✅ **Do:**
707
213
 
708
- - Mock at boundaries (APIs, file system)
214
+ - Mock at boundaries (APIs, file system, network)
709
215
  - Restore mocks after tests
710
216
  - Make mocks realistic (same shape as real data)
711
217
 
712
218
  ❌ **Don't:**
713
219
 
714
- - Over-mock (makes tests brittle)
220
+ - Over-mock (makes tests brittle and meaningless)
715
221
  - Mock your own code (test real behavior)
716
- - Mock what you don't own (unless external)
222
+ - Leave mocks unreset between tests
223
+
224
+ ---
225
+
226
+ ## Assertion Depth Rules
227
+
228
+ ### ❌ Shallow Assertions (BANNED)
229
+
230
+ Assertions that prove nothing about correctness:
231
+ - "Result is defined" — undefined would also be defined
232
+ - "Result is truthy" — empty arrays and objects are truthy
233
+ - "Status is 200" alone — doesn't verify response body
234
+
235
+ ### ✅ Deep Assertions (REQUIRED)
236
+
237
+ Every test must assert:
238
+ 1. **The correct output** — specific values, not just types
239
+ 2. **The correct side effects** — what changed in the system
240
+ 3. **The correct error behavior** — specific error type and message
717
241
 
718
242
  ---
719
243
 
@@ -721,20 +245,20 @@ mockRandom.mockRestore()
721
245
 
722
246
  ### Coverage Targets
723
247
 
724
- - **70% minimum** - Below this, you're missing important tests
725
- - **80% good** - Solid coverage of critical paths
726
- - **90%+ diminishing returns** - Chasing 100% often not worth it
248
+ - **70% minimum** Below this, you're missing important tests
249
+ - **80% good** Solid coverage of critical paths
250
+ - **90%+ diminishing returns** Chasing 100% often not worth it
727
251
 
728
252
  ### What to Focus On
729
253
 
730
- **High priority (must have 90%+ coverage):**
254
+ **High priority (90%+ coverage):**
731
255
 
732
256
  - Business logic
733
257
  - Authentication/authorization
734
258
  - Payment processing
735
259
  - Data validation
736
260
 
737
- **Medium priority (aim for 70%+):**
261
+ **Medium priority (70%+):**
738
262
 
739
263
  - API routes
740
264
  - Database queries
@@ -742,128 +266,22 @@ mockRandom.mockRestore()
742
266
 
743
267
  **Low priority (okay to skip):**
744
268
 
745
- - UI components (test behavior, not implementation)
269
+ - UI components (test behavior, not rendering)
746
270
  - Configuration files
747
271
  - Type definitions
748
- - Third-party integrations (integration tests better)
749
-
750
- ### Checking Coverage
751
-
752
- ```bash
753
- # Jest
754
- npm test -- --coverage
755
-
756
- # View HTML report
757
- open coverage/lcov-report/index.html
758
- ```
759
-
760
- ### Coverage Configuration (jest.config.js)
761
-
762
- ```javascript
763
- module.exports = {
764
- collectCoverageFrom: [
765
- 'src/**/*.{ts,tsx}',
766
- '!src/**/*.d.ts',
767
- '!src/**/*.stories.tsx',
768
- '!src/types/**'
769
- ],
770
- coverageThresholds: {
771
- global: {
772
- branches: 70,
773
- functions: 70,
774
- lines: 70,
775
- statements: 70
776
- },
777
- // Critical paths need higher coverage
778
- './src/lib/auth/**': {
779
- branches: 90,
780
- functions: 90,
781
- lines: 90
782
- }
783
- }
784
- }
785
- ```
786
-
787
- ---
788
-
789
- ## Testing Strategies by Framework
790
-
791
- ### Next.js (React)
792
-
793
- - Unit: Jest + React Testing Library
794
- - Integration: Supertest (API routes)
795
- - E2E: Playwright
796
-
797
- ### Express API
798
-
799
- - Unit: Jest
800
- - Integration: Supertest
801
- - E2E: Playwright (if has UI)
802
-
803
- ### FastAPI (Python)
804
-
805
- - Unit: pytest
806
- - Integration: pytest + TestClient
807
- - E2E: Playwright
808
272
 
809
273
  ---
810
274
 
811
275
  ## Common Testing Patterns
812
276
 
813
277
  ### Testing Async Code
814
-
815
- ```typescript
816
- // Using async/await
817
- it('fetches user data', async () => {
818
- const user = await fetchUser('123')
819
- expect(user.name).toBe('John')
820
- })
821
-
822
- // Using waitFor (React Testing Library)
823
- it('shows loading then data', async () => {
824
- render(<UserProfile userId="123" />)
825
-
826
- expect(screen.getByText('Loading...')).toBeInTheDocument()
827
-
828
- await waitFor(() => {
829
- expect(screen.getByText('John Doe')).toBeInTheDocument()
830
- })
831
- })
832
- ```
278
+ Write tests that properly wait for async operations to complete before asserting. Use your framework's async test utilities.
833
279
 
834
280
  ### Testing Error Handling
835
-
836
- ```typescript
837
- it('handles errors gracefully', async () => {
838
- mockFetchUser.mockRejectedValue(new Error('Network error'))
839
-
840
- render(<UserProfile userId="123" />)
841
-
842
- await waitFor(() => {
843
- expect(screen.getByText(/error/i)).toBeInTheDocument()
844
- })
845
- })
846
- ```
281
+ Mock dependencies to throw errors, then assert your code handles them correctly — shows the right error message, returns the right status code, logs appropriately.
847
282
 
848
283
  ### Testing Forms
849
-
850
- ```typescript
851
- it('submits form with valid data', async () => {
852
- const handleSubmit = jest.fn()
853
- render(<LoginForm onSubmit={handleSubmit} />)
854
-
855
- await userEvent.type(screen.getByLabelText('Email'), 'test@example.com')
856
- await userEvent.type(screen.getByLabelText('Password'), 'password123')
857
- await userEvent.click(screen.getByRole('button', { name: 'Login' }))
858
-
859
- await waitFor(() => {
860
- expect(handleSubmit).toHaveBeenCalledWith({
861
- email: 'test@example.com',
862
- password: 'password123'
863
- })
864
- })
865
- })
866
- ```
284
+ Fill form fields programmatically, submit, and assert both the submission data and any validation errors.
867
285
 
868
286
  ---
869
287
 
@@ -871,33 +289,24 @@ it('submits form with valid data', async () => {
871
289
 
872
290
  ### File Structure
873
291
 
292
+ Tests co-locate with source files:
293
+
874
294
  ```
875
- src/
876
- ├── components/
877
- │ ├── Button.tsx
878
- │ └── Button.test.tsx # Co-located with component
879
- ├── lib/
880
- │ ├── utils.ts
881
- │ └── utils.test.ts # Co-located with module
882
- └── __tests__/
883
- ├── integration/ # Integration tests
884
- │ └── api.test.ts
885
- └── e2e/ # E2E tests
886
- └── checkout.spec.ts
295
+ source-file.ext → source-file.test.ext (unit tests)
296
+ route-handler.ext → route-handler.test.ext (integration tests)
297
+ e2e/ → feature-name.e2e.ext (E2E tests)
887
298
  ```
888
299
 
889
300
  ### Naming Conventions
890
301
 
891
- - Unit/Integration: `*.test.ts` or `*.spec.ts`
892
- - E2E: `*.e2e.ts` or `*.spec.ts` (in tests/e2e/)
893
- - Test names: `it('should do X when Y')` or `it('does X')`
302
+ - Unit/Integration: `*.test.*` or `*.spec.*`
303
+ - E2E: `*.e2e.*` or `*.spec.*` (in tests/e2e/)
304
+ - Test names should describe behavior: "returns X when Y" or "throws error for invalid input"
894
305
 
895
306
  ---
896
307
 
897
308
  ## When to Use This Skill
898
309
 
899
- Use testing-strategist skill when:
900
-
901
310
  - ✅ Setting up testing for new project
902
311
  - ✅ Choosing test frameworks
903
312
  - ✅ Deciding what to test and at what level
@@ -907,26 +316,4 @@ Use testing-strategist skill when:
907
316
 
908
317
  ---
909
318
 
910
- ## Related Resources
911
-
912
- **Skills:**
913
-
914
- - `security-engineer` - Security testing
915
- - `api-designer` - API testing strategies
916
- - `frontend-builder` - React testing patterns
917
-
918
- **Patterns:**
919
-
920
- - `/STANDARDS/best-practices/testing-best-practices.md`
921
- - `/TEMPLATES/testing/jest-nextjs-setup.md`
922
- - `/TEMPLATES/testing/playwright-e2e-setup.md`
923
-
924
- **External:**
925
-
926
- - [Jest Documentation](https://jestjs.io/)
927
- - [React Testing Library](https://testing-library.com/react)
928
- - [Playwright](https://playwright.dev/)
929
-
930
- ---
931
-
932
319
  **Good tests give you confidence to ship.** ✅