@champpaba/claude-agent-kit 2.7.0 → 2.8.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.
- package/.claude/CLAUDE.md +49 -0
- package/.claude/commands/csetup.md +364 -38
- package/.claude/commands/cview.md +364 -364
- package/.claude/contexts/design/accessibility.md +611 -611
- package/.claude/contexts/design/layout.md +400 -400
- package/.claude/contexts/design/responsive.md +551 -551
- package/.claude/contexts/design/shadows.md +522 -522
- package/.claude/contexts/design/typography.md +465 -465
- package/.claude/contexts/domain/README.md +164 -164
- package/.claude/contexts/patterns/agent-coordination.md +388 -388
- package/.claude/contexts/patterns/development-principles.md +513 -513
- package/.claude/contexts/patterns/error-handling.md +478 -478
- package/.claude/contexts/patterns/logging.md +424 -424
- package/.claude/contexts/patterns/tdd-classification.md +516 -516
- package/.claude/contexts/patterns/testing.md +413 -413
- package/.claude/lib/tdd-classifier.md +345 -345
- package/.claude/lib/validation-gates.md +484 -484
- package/.claude/settings.local.json +42 -42
- package/.claude/templates/context-template.md +45 -45
- package/.claude/templates/flags-template.json +42 -42
- package/.claude/templates/phases-sections/accessibility-test.md +17 -17
- package/.claude/templates/phases-sections/api-design.md +37 -37
- package/.claude/templates/phases-sections/backend-tests.md +16 -16
- package/.claude/templates/phases-sections/backend.md +37 -37
- package/.claude/templates/phases-sections/business-logic-validation.md +16 -16
- package/.claude/templates/phases-sections/component-tests.md +17 -17
- package/.claude/templates/phases-sections/contract-backend.md +16 -16
- package/.claude/templates/phases-sections/contract-frontend.md +16 -16
- package/.claude/templates/phases-sections/database.md +35 -35
- package/.claude/templates/phases-sections/e2e-tests.md +16 -16
- package/.claude/templates/phases-sections/fix-implementation.md +17 -17
- package/.claude/templates/phases-sections/frontend-integration.md +18 -18
- package/.claude/templates/phases-sections/manual-flow-test.md +15 -15
- package/.claude/templates/phases-sections/manual-ux-test.md +16 -16
- package/.claude/templates/phases-sections/refactor-implementation.md +17 -17
- package/.claude/templates/phases-sections/refactor.md +16 -16
- package/.claude/templates/phases-sections/regression-tests.md +15 -15
- package/.claude/templates/phases-sections/responsive-test.md +16 -16
- package/.claude/templates/phases-sections/script-implementation.md +43 -43
- package/.claude/templates/phases-sections/test-coverage.md +16 -16
- package/.claude/templates/phases-sections/user-approval.md +14 -14
- package/LICENSE +21 -21
- package/README.md +25 -0
- package/package.json +8 -4
|
@@ -1,413 +1,413 @@
|
|
|
1
|
-
# Testing Strategy & TDD Methodology
|
|
2
|
-
|
|
3
|
-
**Red-Green-Refactor:** Write test → Watch it fail → Write code → Make it pass → Refactor
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Test-Driven Development (TDD)
|
|
8
|
-
|
|
9
|
-
### Automatic TDD Classification
|
|
10
|
-
|
|
11
|
-
**Orchestrator automatically determines if TDD is required based on task content.**
|
|
12
|
-
|
|
13
|
-
You don't decide. Orchestrator decides. Your job is to **follow the `tdd_required` flag**.
|
|
14
|
-
|
|
15
|
-
### When You Receive a Task
|
|
16
|
-
|
|
17
|
-
**Check the metadata from Orchestrator:**
|
|
18
|
-
```json
|
|
19
|
-
{
|
|
20
|
-
"description": "Implement POST /api/auth/login",
|
|
21
|
-
"type": "critical",
|
|
22
|
-
"tdd_required": true,
|
|
23
|
-
"workflow": "red-green-refactor",
|
|
24
|
-
"reason": "API endpoint + authentication logic"
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Classification Rules (Orchestrator Logic)
|
|
29
|
-
|
|
30
|
-
✅ **TDD Required (`tdd_required: true`) for:**
|
|
31
|
-
- API mutations (POST, PUT, PATCH, DELETE)
|
|
32
|
-
- Business logic (calculations, transformations, validation)
|
|
33
|
-
- External service integrations (payment, email, SMS, storage)
|
|
34
|
-
- Data transformations (ETL, serialization, aggregations)
|
|
35
|
-
- Security operations (authentication, authorization, encryption)
|
|
36
|
-
- Complex UI logic (multi-step forms, state machines, accessibility)
|
|
37
|
-
|
|
38
|
-
⚠️ **Test-Alongside OK (`tdd_required: false`) for:**
|
|
39
|
-
- Simple GET endpoints (read-only)
|
|
40
|
-
- Presentational UI components
|
|
41
|
-
- Simple CRUD operations
|
|
42
|
-
- Configuration files
|
|
43
|
-
- Trivial utilities
|
|
44
|
-
|
|
45
|
-
**Note:** For full classification logic, see `@.claude/contexts/patterns/task-classification.md`
|
|
46
|
-
|
|
47
|
-
### TDD Workflow (Red-Green-Refactor)
|
|
48
|
-
|
|
49
|
-
**Use when:** `tdd_required: true`
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
1. RED Phase - Write the test first
|
|
53
|
-
→ Define expected behavior before implementation
|
|
54
|
-
→ Run test → MUST FAIL (proves test works)
|
|
55
|
-
→ Log: "tdd_red_phase"
|
|
56
|
-
|
|
57
|
-
2. GREEN Phase - Write minimal code
|
|
58
|
-
→ Just enough to make the test pass
|
|
59
|
-
→ Run test → MUST PASS
|
|
60
|
-
→ Log: "tdd_green_phase"
|
|
61
|
-
|
|
62
|
-
3. REFACTOR Phase - Improve code quality
|
|
63
|
-
→ Add logging for observability
|
|
64
|
-
→ Add error handling
|
|
65
|
-
→ Add documentation
|
|
66
|
-
→ Run test → MUST STILL PASS
|
|
67
|
-
→ Log: "tdd_refactor_phase"
|
|
68
|
-
|
|
69
|
-
4. Repeat
|
|
70
|
-
→ One test at a time
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Standard Workflow (Test-Alongside)
|
|
74
|
-
|
|
75
|
-
**Use when:** `tdd_required: false`
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
1. Write implementation first
|
|
79
|
-
2. Write tests to verify
|
|
80
|
-
3. Run tests → PASS
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Testing Layers
|
|
86
|
-
|
|
87
|
-
### 1. Unit Tests
|
|
88
|
-
|
|
89
|
-
**Purpose:** Test individual functions/modules in isolation
|
|
90
|
-
|
|
91
|
-
**Framework:** Vitest (JS/TS) | Pytest (Python)
|
|
92
|
-
|
|
93
|
-
#### TypeScript Example
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
// __tests__/scoring.test.ts
|
|
97
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
98
|
-
import { calculateDiscount } from '@/lib/pricing'
|
|
99
|
-
import { logger } from '@/lib/logger'
|
|
100
|
-
|
|
101
|
-
vi.mock('@/lib/logger')
|
|
102
|
-
|
|
103
|
-
describe('calculateDiscount', () => {
|
|
104
|
-
it('applies 10% discount for members', () => {
|
|
105
|
-
const result = calculateDiscount(100, { isMember: true })
|
|
106
|
-
expect(result).toBe(90)
|
|
107
|
-
|
|
108
|
-
expect(logger.info).toHaveBeenCalledWith(
|
|
109
|
-
'discount_calculated',
|
|
110
|
-
expect.objectContaining({
|
|
111
|
-
originalPrice: 100,
|
|
112
|
-
discount: 10,
|
|
113
|
-
finalPrice: 90
|
|
114
|
-
})
|
|
115
|
-
)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('no discount for non-members', () => {
|
|
119
|
-
const result = calculateDiscount(100, { isMember: false })
|
|
120
|
-
expect(result).toBe(100)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('throws on invalid input', () => {
|
|
124
|
-
expect(() => calculateDiscount(-1, {})).toThrow('Price must be positive')
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
#### Python Example
|
|
130
|
-
|
|
131
|
-
```python
|
|
132
|
-
# tests/test_pricing.py
|
|
133
|
-
import pytest
|
|
134
|
-
from app.lib.pricing import calculate_discount
|
|
135
|
-
from unittest.mock import patch
|
|
136
|
-
|
|
137
|
-
@pytest.mark.parametrize("price,is_member,expected", [
|
|
138
|
-
(100, True, 90), # Member gets 10% discount
|
|
139
|
-
(100, False, 100), # Non-member no discount
|
|
140
|
-
(50, True, 45), # Member discount on lower price
|
|
141
|
-
])
|
|
142
|
-
def test_calculate_discount(price, is_member, expected):
|
|
143
|
-
result = calculate_discount(price, is_member=is_member)
|
|
144
|
-
assert result == expected
|
|
145
|
-
|
|
146
|
-
def test_calculate_discount_invalid_price():
|
|
147
|
-
with pytest.raises(ValueError, match="Price must be positive"):
|
|
148
|
-
calculate_discount(-1, is_member=True)
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**When to use:**
|
|
152
|
-
- Pure functions
|
|
153
|
-
- Business logic
|
|
154
|
-
- Data transformations
|
|
155
|
-
- Utility functions
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
### 2. Integration Tests
|
|
160
|
-
|
|
161
|
-
**Purpose:** Test multiple components working together
|
|
162
|
-
|
|
163
|
-
#### API Route Test (Next.js)
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// __tests__/api/items.test.ts
|
|
167
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
168
|
-
import { POST } from '@/app/api/items/route'
|
|
169
|
-
|
|
170
|
-
describe('POST /api/items', () => {
|
|
171
|
-
it('creates item and returns 201', async () => {
|
|
172
|
-
const request = new Request('http://localhost/api/items', {
|
|
173
|
-
method: 'POST',
|
|
174
|
-
headers: { 'Content-Type': 'application/json' },
|
|
175
|
-
body: JSON.stringify({
|
|
176
|
-
name: 'Test Item',
|
|
177
|
-
price: 99.99
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
const response = await POST(request)
|
|
182
|
-
const data = await response.json()
|
|
183
|
-
|
|
184
|
-
expect(response.status).toBe(201)
|
|
185
|
-
expect(data.id).toBeDefined()
|
|
186
|
-
expect(data.name).toBe('Test Item')
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it('returns 400 on validation error', async () => {
|
|
190
|
-
const request = new Request('http://localhost/api/items', {
|
|
191
|
-
method: 'POST',
|
|
192
|
-
body: JSON.stringify({ name: '' }) // Missing price
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
const response = await POST(request)
|
|
196
|
-
expect(response.status).toBe(400)
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
#### API Test (FastAPI)
|
|
202
|
-
|
|
203
|
-
```python
|
|
204
|
-
# tests/test_api.py
|
|
205
|
-
import pytest
|
|
206
|
-
from httpx import AsyncClient
|
|
207
|
-
|
|
208
|
-
@pytest.mark.asyncio
|
|
209
|
-
async def test_create_item(client: AsyncClient):
|
|
210
|
-
response = await client.post("/api/items", json={
|
|
211
|
-
"name": "Test Item",
|
|
212
|
-
"price": 99.99
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
assert response.status_code == 201
|
|
216
|
-
data = response.json()
|
|
217
|
-
assert data["id"] is not None
|
|
218
|
-
assert data["name"] == "Test Item"
|
|
219
|
-
|
|
220
|
-
@pytest.mark.asyncio
|
|
221
|
-
async def test_create_item_validation_error(client: AsyncClient):
|
|
222
|
-
response = await client.post("/api/items", json={
|
|
223
|
-
"name": "" # Invalid
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
assert response.status_code == 422 # Validation error
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**When to use:**
|
|
230
|
-
- API routes
|
|
231
|
-
- Database operations
|
|
232
|
-
- External service integrations
|
|
233
|
-
- Multi-step workflows
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
### 3. E2E Tests (Playwright)
|
|
238
|
-
|
|
239
|
-
**Purpose:** Test complete user flows in real browser
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
// e2e/checkout.spec.ts
|
|
243
|
-
import { test, expect } from '@playwright/test'
|
|
244
|
-
|
|
245
|
-
test('user completes checkout flow', async ({ page }) => {
|
|
246
|
-
// Add item to cart
|
|
247
|
-
await page.goto('/products')
|
|
248
|
-
await page.click('button:text("Add to Cart")')
|
|
249
|
-
|
|
250
|
-
// Go to cart
|
|
251
|
-
await page.click('[href="/cart"]')
|
|
252
|
-
await expect(page.locator('h1')).toContainText('Shopping Cart')
|
|
253
|
-
|
|
254
|
-
// Proceed to checkout
|
|
255
|
-
await page.click('button:text("Checkout")')
|
|
256
|
-
|
|
257
|
-
// Fill shipping info
|
|
258
|
-
await page.fill('#name', 'John Doe')
|
|
259
|
-
await page.fill('#email', 'john@example.com')
|
|
260
|
-
await page.fill('#address', '123 Main St')
|
|
261
|
-
await page.click('button:text("Continue")')
|
|
262
|
-
|
|
263
|
-
// Payment
|
|
264
|
-
await page.fill('#cardNumber', '4242424242424242')
|
|
265
|
-
await page.fill('#expiry', '12/25')
|
|
266
|
-
await page.fill('#cvc', '123')
|
|
267
|
-
await page.click('button:text("Pay Now")')
|
|
268
|
-
|
|
269
|
-
// Verify order confirmation
|
|
270
|
-
await expect(page.locator('h1')).toContainText('Order Confirmed')
|
|
271
|
-
await expect(page.locator('[data-testid="order-number"]')).toBeVisible()
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
test('shows validation errors on empty form', async ({ page }) => {
|
|
275
|
-
await page.goto('/checkout')
|
|
276
|
-
await page.click('button:text("Continue")')
|
|
277
|
-
|
|
278
|
-
await expect(page.locator('text=Name is required')).toBeVisible()
|
|
279
|
-
await expect(page.locator('text=Email is required')).toBeVisible()
|
|
280
|
-
})
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**When to use:**
|
|
284
|
-
- Complete user journeys
|
|
285
|
-
- Cross-browser compatibility
|
|
286
|
-
- UI/UX validation
|
|
287
|
-
- Critical paths
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## Testing Best Practices
|
|
292
|
-
|
|
293
|
-
### DO:
|
|
294
|
-
- ✅ Test behavior, not implementation
|
|
295
|
-
- ✅ Write tests first for critical code (TDD)
|
|
296
|
-
- ✅ Mock external dependencies
|
|
297
|
-
- ✅ Use descriptive test names
|
|
298
|
-
- ✅ Test edge cases and error scenarios
|
|
299
|
-
- ✅ Keep tests fast (< 1s per test)
|
|
300
|
-
- ✅ Verify logging in tests
|
|
301
|
-
|
|
302
|
-
### DON'T:
|
|
303
|
-
- ❌ Test framework internals (React hooks, etc.)
|
|
304
|
-
- ❌ Test trivial code (getters/setters)
|
|
305
|
-
- ❌ Write flaky tests (time-dependent)
|
|
306
|
-
- ❌ Skip error case tests
|
|
307
|
-
- ❌ Commit failing tests
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## Test Organization
|
|
312
|
-
|
|
313
|
-
### TypeScript/JavaScript
|
|
314
|
-
```
|
|
315
|
-
project/
|
|
316
|
-
├── __tests__/
|
|
317
|
-
│ ├── unit/
|
|
318
|
-
│ │ ├── lib/
|
|
319
|
-
│ │ │ ├── pricing.test.ts
|
|
320
|
-
│ │ │ └── validation.test.ts
|
|
321
|
-
│ │ └── utils/
|
|
322
|
-
│ │ └── formatters.test.ts
|
|
323
|
-
│ ├── integration/
|
|
324
|
-
│ │ ├── api/
|
|
325
|
-
│ │ │ ├── items.test.ts
|
|
326
|
-
│ │ │ └── users.test.ts
|
|
327
|
-
│ │ └── stores/
|
|
328
|
-
│ │ └── app-store.test.ts
|
|
329
|
-
│ └── e2e/
|
|
330
|
-
│ ├── checkout.spec.ts
|
|
331
|
-
│ └── auth.spec.ts
|
|
332
|
-
├── vitest.config.ts
|
|
333
|
-
└── playwright.config.ts
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Python
|
|
337
|
-
```
|
|
338
|
-
project/
|
|
339
|
-
├── tests/
|
|
340
|
-
│ ├── unit/
|
|
341
|
-
│ │ ├── test_pricing.py
|
|
342
|
-
│ │ └── test_validation.py
|
|
343
|
-
│ ├── integration/
|
|
344
|
-
│ │ ├── test_api.py
|
|
345
|
-
│ │ └── test_db.py
|
|
346
|
-
│ └── conftest.py (fixtures)
|
|
347
|
-
└── pytest.ini
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## Coverage Guidelines
|
|
353
|
-
|
|
354
|
-
| Type | Target Coverage |
|
|
355
|
-
|------|----------------|
|
|
356
|
-
| Business Logic | 95%+ |
|
|
357
|
-
| API Routes | 90%+ |
|
|
358
|
-
| Utilities | 85%+ |
|
|
359
|
-
| UI Components | 85%+ |
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## Quick Reference
|
|
364
|
-
|
|
365
|
-
### Vitest Commands
|
|
366
|
-
```bash
|
|
367
|
-
pnpm test # Run all tests
|
|
368
|
-
pnpm test -- --run # Run without watch mode
|
|
369
|
-
pnpm test -- path/to/test # Run specific test
|
|
370
|
-
pnpm test -- --coverage # Run with coverage
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
### Pytest Commands
|
|
374
|
-
```bash
|
|
375
|
-
pytest # Run all tests
|
|
376
|
-
pytest -v # Verbose output
|
|
377
|
-
pytest tests/unit/ # Run specific directory
|
|
378
|
-
pytest --cov=app # Run with coverage
|
|
379
|
-
pytest -k "test_create" # Run tests matching pattern
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### Playwright Commands
|
|
383
|
-
```bash
|
|
384
|
-
pnpm test:e2e # Run E2E tests
|
|
385
|
-
pnpm test:e2e --ui # Run with UI mode
|
|
386
|
-
pnpm test:e2e --debug # Run with debugger
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
---
|
|
392
|
-
|
|
393
|
-
## Trust the Classification
|
|
394
|
-
|
|
395
|
-
**Orchestrator uses comprehensive pattern matching:**
|
|
396
|
-
- API endpoints (HTTP methods, routes)
|
|
397
|
-
- Business logic (calculations, validation)
|
|
398
|
-
- External integrations (Stripe, SendGrid, Twilio, S3, etc.)
|
|
399
|
-
- Data transformations (ETL, serialization)
|
|
400
|
-
- Security operations (auth, encryption)
|
|
401
|
-
|
|
402
|
-
**Your responsibility:**
|
|
403
|
-
1. Check `tdd_required` flag in task metadata
|
|
404
|
-
2. If `true` → Use Red-Green-Refactor workflow
|
|
405
|
-
3. If `false` → Use Test-Alongside workflow
|
|
406
|
-
4. Log each phase for observability
|
|
407
|
-
|
|
408
|
-
**Don't override the classification unless you find a clear error.**
|
|
409
|
-
If classification seems wrong, report to user for pattern refinement.
|
|
410
|
-
|
|
411
|
-
---
|
|
412
|
-
|
|
413
|
-
**💡 Remember:** Tests are documentation. Write tests that explain what your code does!
|
|
1
|
+
# Testing Strategy & TDD Methodology
|
|
2
|
+
|
|
3
|
+
**Red-Green-Refactor:** Write test → Watch it fail → Write code → Make it pass → Refactor
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Test-Driven Development (TDD)
|
|
8
|
+
|
|
9
|
+
### Automatic TDD Classification
|
|
10
|
+
|
|
11
|
+
**Orchestrator automatically determines if TDD is required based on task content.**
|
|
12
|
+
|
|
13
|
+
You don't decide. Orchestrator decides. Your job is to **follow the `tdd_required` flag**.
|
|
14
|
+
|
|
15
|
+
### When You Receive a Task
|
|
16
|
+
|
|
17
|
+
**Check the metadata from Orchestrator:**
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"description": "Implement POST /api/auth/login",
|
|
21
|
+
"type": "critical",
|
|
22
|
+
"tdd_required": true,
|
|
23
|
+
"workflow": "red-green-refactor",
|
|
24
|
+
"reason": "API endpoint + authentication logic"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Classification Rules (Orchestrator Logic)
|
|
29
|
+
|
|
30
|
+
✅ **TDD Required (`tdd_required: true`) for:**
|
|
31
|
+
- API mutations (POST, PUT, PATCH, DELETE)
|
|
32
|
+
- Business logic (calculations, transformations, validation)
|
|
33
|
+
- External service integrations (payment, email, SMS, storage)
|
|
34
|
+
- Data transformations (ETL, serialization, aggregations)
|
|
35
|
+
- Security operations (authentication, authorization, encryption)
|
|
36
|
+
- Complex UI logic (multi-step forms, state machines, accessibility)
|
|
37
|
+
|
|
38
|
+
⚠️ **Test-Alongside OK (`tdd_required: false`) for:**
|
|
39
|
+
- Simple GET endpoints (read-only)
|
|
40
|
+
- Presentational UI components
|
|
41
|
+
- Simple CRUD operations
|
|
42
|
+
- Configuration files
|
|
43
|
+
- Trivial utilities
|
|
44
|
+
|
|
45
|
+
**Note:** For full classification logic, see `@.claude/contexts/patterns/task-classification.md`
|
|
46
|
+
|
|
47
|
+
### TDD Workflow (Red-Green-Refactor)
|
|
48
|
+
|
|
49
|
+
**Use when:** `tdd_required: true`
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
1. RED Phase - Write the test first
|
|
53
|
+
→ Define expected behavior before implementation
|
|
54
|
+
→ Run test → MUST FAIL (proves test works)
|
|
55
|
+
→ Log: "tdd_red_phase"
|
|
56
|
+
|
|
57
|
+
2. GREEN Phase - Write minimal code
|
|
58
|
+
→ Just enough to make the test pass
|
|
59
|
+
→ Run test → MUST PASS
|
|
60
|
+
→ Log: "tdd_green_phase"
|
|
61
|
+
|
|
62
|
+
3. REFACTOR Phase - Improve code quality
|
|
63
|
+
→ Add logging for observability
|
|
64
|
+
→ Add error handling
|
|
65
|
+
→ Add documentation
|
|
66
|
+
→ Run test → MUST STILL PASS
|
|
67
|
+
→ Log: "tdd_refactor_phase"
|
|
68
|
+
|
|
69
|
+
4. Repeat
|
|
70
|
+
→ One test at a time
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Standard Workflow (Test-Alongside)
|
|
74
|
+
|
|
75
|
+
**Use when:** `tdd_required: false`
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
1. Write implementation first
|
|
79
|
+
2. Write tests to verify
|
|
80
|
+
3. Run tests → PASS
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Testing Layers
|
|
86
|
+
|
|
87
|
+
### 1. Unit Tests
|
|
88
|
+
|
|
89
|
+
**Purpose:** Test individual functions/modules in isolation
|
|
90
|
+
|
|
91
|
+
**Framework:** Vitest (JS/TS) | Pytest (Python)
|
|
92
|
+
|
|
93
|
+
#### TypeScript Example
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// __tests__/scoring.test.ts
|
|
97
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
98
|
+
import { calculateDiscount } from '@/lib/pricing'
|
|
99
|
+
import { logger } from '@/lib/logger'
|
|
100
|
+
|
|
101
|
+
vi.mock('@/lib/logger')
|
|
102
|
+
|
|
103
|
+
describe('calculateDiscount', () => {
|
|
104
|
+
it('applies 10% discount for members', () => {
|
|
105
|
+
const result = calculateDiscount(100, { isMember: true })
|
|
106
|
+
expect(result).toBe(90)
|
|
107
|
+
|
|
108
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
109
|
+
'discount_calculated',
|
|
110
|
+
expect.objectContaining({
|
|
111
|
+
originalPrice: 100,
|
|
112
|
+
discount: 10,
|
|
113
|
+
finalPrice: 90
|
|
114
|
+
})
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('no discount for non-members', () => {
|
|
119
|
+
const result = calculateDiscount(100, { isMember: false })
|
|
120
|
+
expect(result).toBe(100)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('throws on invalid input', () => {
|
|
124
|
+
expect(() => calculateDiscount(-1, {})).toThrow('Price must be positive')
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Python Example
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# tests/test_pricing.py
|
|
133
|
+
import pytest
|
|
134
|
+
from app.lib.pricing import calculate_discount
|
|
135
|
+
from unittest.mock import patch
|
|
136
|
+
|
|
137
|
+
@pytest.mark.parametrize("price,is_member,expected", [
|
|
138
|
+
(100, True, 90), # Member gets 10% discount
|
|
139
|
+
(100, False, 100), # Non-member no discount
|
|
140
|
+
(50, True, 45), # Member discount on lower price
|
|
141
|
+
])
|
|
142
|
+
def test_calculate_discount(price, is_member, expected):
|
|
143
|
+
result = calculate_discount(price, is_member=is_member)
|
|
144
|
+
assert result == expected
|
|
145
|
+
|
|
146
|
+
def test_calculate_discount_invalid_price():
|
|
147
|
+
with pytest.raises(ValueError, match="Price must be positive"):
|
|
148
|
+
calculate_discount(-1, is_member=True)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**When to use:**
|
|
152
|
+
- Pure functions
|
|
153
|
+
- Business logic
|
|
154
|
+
- Data transformations
|
|
155
|
+
- Utility functions
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### 2. Integration Tests
|
|
160
|
+
|
|
161
|
+
**Purpose:** Test multiple components working together
|
|
162
|
+
|
|
163
|
+
#### API Route Test (Next.js)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// __tests__/api/items.test.ts
|
|
167
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
168
|
+
import { POST } from '@/app/api/items/route'
|
|
169
|
+
|
|
170
|
+
describe('POST /api/items', () => {
|
|
171
|
+
it('creates item and returns 201', async () => {
|
|
172
|
+
const request = new Request('http://localhost/api/items', {
|
|
173
|
+
method: 'POST',
|
|
174
|
+
headers: { 'Content-Type': 'application/json' },
|
|
175
|
+
body: JSON.stringify({
|
|
176
|
+
name: 'Test Item',
|
|
177
|
+
price: 99.99
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const response = await POST(request)
|
|
182
|
+
const data = await response.json()
|
|
183
|
+
|
|
184
|
+
expect(response.status).toBe(201)
|
|
185
|
+
expect(data.id).toBeDefined()
|
|
186
|
+
expect(data.name).toBe('Test Item')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('returns 400 on validation error', async () => {
|
|
190
|
+
const request = new Request('http://localhost/api/items', {
|
|
191
|
+
method: 'POST',
|
|
192
|
+
body: JSON.stringify({ name: '' }) // Missing price
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const response = await POST(request)
|
|
196
|
+
expect(response.status).toBe(400)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### API Test (FastAPI)
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
# tests/test_api.py
|
|
205
|
+
import pytest
|
|
206
|
+
from httpx import AsyncClient
|
|
207
|
+
|
|
208
|
+
@pytest.mark.asyncio
|
|
209
|
+
async def test_create_item(client: AsyncClient):
|
|
210
|
+
response = await client.post("/api/items", json={
|
|
211
|
+
"name": "Test Item",
|
|
212
|
+
"price": 99.99
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
assert response.status_code == 201
|
|
216
|
+
data = response.json()
|
|
217
|
+
assert data["id"] is not None
|
|
218
|
+
assert data["name"] == "Test Item"
|
|
219
|
+
|
|
220
|
+
@pytest.mark.asyncio
|
|
221
|
+
async def test_create_item_validation_error(client: AsyncClient):
|
|
222
|
+
response = await client.post("/api/items", json={
|
|
223
|
+
"name": "" # Invalid
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
assert response.status_code == 422 # Validation error
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**When to use:**
|
|
230
|
+
- API routes
|
|
231
|
+
- Database operations
|
|
232
|
+
- External service integrations
|
|
233
|
+
- Multi-step workflows
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### 3. E2E Tests (Playwright)
|
|
238
|
+
|
|
239
|
+
**Purpose:** Test complete user flows in real browser
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// e2e/checkout.spec.ts
|
|
243
|
+
import { test, expect } from '@playwright/test'
|
|
244
|
+
|
|
245
|
+
test('user completes checkout flow', async ({ page }) => {
|
|
246
|
+
// Add item to cart
|
|
247
|
+
await page.goto('/products')
|
|
248
|
+
await page.click('button:text("Add to Cart")')
|
|
249
|
+
|
|
250
|
+
// Go to cart
|
|
251
|
+
await page.click('[href="/cart"]')
|
|
252
|
+
await expect(page.locator('h1')).toContainText('Shopping Cart')
|
|
253
|
+
|
|
254
|
+
// Proceed to checkout
|
|
255
|
+
await page.click('button:text("Checkout")')
|
|
256
|
+
|
|
257
|
+
// Fill shipping info
|
|
258
|
+
await page.fill('#name', 'John Doe')
|
|
259
|
+
await page.fill('#email', 'john@example.com')
|
|
260
|
+
await page.fill('#address', '123 Main St')
|
|
261
|
+
await page.click('button:text("Continue")')
|
|
262
|
+
|
|
263
|
+
// Payment
|
|
264
|
+
await page.fill('#cardNumber', '4242424242424242')
|
|
265
|
+
await page.fill('#expiry', '12/25')
|
|
266
|
+
await page.fill('#cvc', '123')
|
|
267
|
+
await page.click('button:text("Pay Now")')
|
|
268
|
+
|
|
269
|
+
// Verify order confirmation
|
|
270
|
+
await expect(page.locator('h1')).toContainText('Order Confirmed')
|
|
271
|
+
await expect(page.locator('[data-testid="order-number"]')).toBeVisible()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('shows validation errors on empty form', async ({ page }) => {
|
|
275
|
+
await page.goto('/checkout')
|
|
276
|
+
await page.click('button:text("Continue")')
|
|
277
|
+
|
|
278
|
+
await expect(page.locator('text=Name is required')).toBeVisible()
|
|
279
|
+
await expect(page.locator('text=Email is required')).toBeVisible()
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**When to use:**
|
|
284
|
+
- Complete user journeys
|
|
285
|
+
- Cross-browser compatibility
|
|
286
|
+
- UI/UX validation
|
|
287
|
+
- Critical paths
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Testing Best Practices
|
|
292
|
+
|
|
293
|
+
### DO:
|
|
294
|
+
- ✅ Test behavior, not implementation
|
|
295
|
+
- ✅ Write tests first for critical code (TDD)
|
|
296
|
+
- ✅ Mock external dependencies
|
|
297
|
+
- ✅ Use descriptive test names
|
|
298
|
+
- ✅ Test edge cases and error scenarios
|
|
299
|
+
- ✅ Keep tests fast (< 1s per test)
|
|
300
|
+
- ✅ Verify logging in tests
|
|
301
|
+
|
|
302
|
+
### DON'T:
|
|
303
|
+
- ❌ Test framework internals (React hooks, etc.)
|
|
304
|
+
- ❌ Test trivial code (getters/setters)
|
|
305
|
+
- ❌ Write flaky tests (time-dependent)
|
|
306
|
+
- ❌ Skip error case tests
|
|
307
|
+
- ❌ Commit failing tests
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Test Organization
|
|
312
|
+
|
|
313
|
+
### TypeScript/JavaScript
|
|
314
|
+
```
|
|
315
|
+
project/
|
|
316
|
+
├── __tests__/
|
|
317
|
+
│ ├── unit/
|
|
318
|
+
│ │ ├── lib/
|
|
319
|
+
│ │ │ ├── pricing.test.ts
|
|
320
|
+
│ │ │ └── validation.test.ts
|
|
321
|
+
│ │ └── utils/
|
|
322
|
+
│ │ └── formatters.test.ts
|
|
323
|
+
│ ├── integration/
|
|
324
|
+
│ │ ├── api/
|
|
325
|
+
│ │ │ ├── items.test.ts
|
|
326
|
+
│ │ │ └── users.test.ts
|
|
327
|
+
│ │ └── stores/
|
|
328
|
+
│ │ └── app-store.test.ts
|
|
329
|
+
│ └── e2e/
|
|
330
|
+
│ ├── checkout.spec.ts
|
|
331
|
+
│ └── auth.spec.ts
|
|
332
|
+
├── vitest.config.ts
|
|
333
|
+
└── playwright.config.ts
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Python
|
|
337
|
+
```
|
|
338
|
+
project/
|
|
339
|
+
├── tests/
|
|
340
|
+
│ ├── unit/
|
|
341
|
+
│ │ ├── test_pricing.py
|
|
342
|
+
│ │ └── test_validation.py
|
|
343
|
+
│ ├── integration/
|
|
344
|
+
│ │ ├── test_api.py
|
|
345
|
+
│ │ └── test_db.py
|
|
346
|
+
│ └── conftest.py (fixtures)
|
|
347
|
+
└── pytest.ini
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Coverage Guidelines
|
|
353
|
+
|
|
354
|
+
| Type | Target Coverage |
|
|
355
|
+
|------|----------------|
|
|
356
|
+
| Business Logic | 95%+ |
|
|
357
|
+
| API Routes | 90%+ |
|
|
358
|
+
| Utilities | 85%+ |
|
|
359
|
+
| UI Components | 85%+ |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Quick Reference
|
|
364
|
+
|
|
365
|
+
### Vitest Commands
|
|
366
|
+
```bash
|
|
367
|
+
pnpm test # Run all tests
|
|
368
|
+
pnpm test -- --run # Run without watch mode
|
|
369
|
+
pnpm test -- path/to/test # Run specific test
|
|
370
|
+
pnpm test -- --coverage # Run with coverage
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Pytest Commands
|
|
374
|
+
```bash
|
|
375
|
+
pytest # Run all tests
|
|
376
|
+
pytest -v # Verbose output
|
|
377
|
+
pytest tests/unit/ # Run specific directory
|
|
378
|
+
pytest --cov=app # Run with coverage
|
|
379
|
+
pytest -k "test_create" # Run tests matching pattern
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Playwright Commands
|
|
383
|
+
```bash
|
|
384
|
+
pnpm test:e2e # Run E2E tests
|
|
385
|
+
pnpm test:e2e --ui # Run with UI mode
|
|
386
|
+
pnpm test:e2e --debug # Run with debugger
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Trust the Classification
|
|
394
|
+
|
|
395
|
+
**Orchestrator uses comprehensive pattern matching:**
|
|
396
|
+
- API endpoints (HTTP methods, routes)
|
|
397
|
+
- Business logic (calculations, validation)
|
|
398
|
+
- External integrations (Stripe, SendGrid, Twilio, S3, etc.)
|
|
399
|
+
- Data transformations (ETL, serialization)
|
|
400
|
+
- Security operations (auth, encryption)
|
|
401
|
+
|
|
402
|
+
**Your responsibility:**
|
|
403
|
+
1. Check `tdd_required` flag in task metadata
|
|
404
|
+
2. If `true` → Use Red-Green-Refactor workflow
|
|
405
|
+
3. If `false` → Use Test-Alongside workflow
|
|
406
|
+
4. Log each phase for observability
|
|
407
|
+
|
|
408
|
+
**Don't override the classification unless you find a clear error.**
|
|
409
|
+
If classification seems wrong, report to user for pattern refinement.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
**💡 Remember:** Tests are documentation. Write tests that explain what your code does!
|