specweave 0.26.3 → 0.26.5

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 (64) hide show
  1. package/CLAUDE.md +261 -1382
  2. package/dist/src/cli/commands/plan/plan-orchestrator.js +2 -2
  3. package/dist/src/cli/commands/plan/plan-orchestrator.js.map +1 -1
  4. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +147 -55
  6. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  7. package/dist/src/config/types.d.ts +203 -1208
  8. package/dist/src/config/types.d.ts.map +1 -1
  9. package/dist/src/core/increment/completion-validator.d.ts +4 -0
  10. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  11. package/dist/src/core/increment/completion-validator.js +36 -0
  12. package/dist/src/core/increment/completion-validator.js.map +1 -1
  13. package/dist/src/core/increment/increment-reopener.d.ts.map +1 -1
  14. package/dist/src/core/increment/increment-reopener.js +13 -14
  15. package/dist/src/core/increment/increment-reopener.js.map +1 -1
  16. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  17. package/dist/src/core/increment/metadata-manager.js +16 -0
  18. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  19. package/dist/src/core/increment/status-change-sync-trigger.d.ts +85 -0
  20. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -0
  21. package/dist/src/core/increment/status-change-sync-trigger.js +137 -0
  22. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -0
  23. package/dist/src/core/increment/sync-circuit-breaker.d.ts +64 -0
  24. package/dist/src/core/increment/sync-circuit-breaker.d.ts.map +1 -0
  25. package/dist/src/core/increment/sync-circuit-breaker.js +95 -0
  26. package/dist/src/core/increment/sync-circuit-breaker.js.map +1 -0
  27. package/dist/src/core/living-docs/living-docs-sync.d.ts +12 -0
  28. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  29. package/dist/src/core/living-docs/living-docs-sync.js +161 -31
  30. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  31. package/dist/src/core/us-sync-throttle.d.ts +113 -0
  32. package/dist/src/core/us-sync-throttle.d.ts.map +1 -0
  33. package/dist/src/core/us-sync-throttle.js +195 -0
  34. package/dist/src/core/us-sync-throttle.js.map +1 -0
  35. package/dist/src/init/architecture/types.d.ts +33 -140
  36. package/dist/src/init/architecture/types.d.ts.map +1 -1
  37. package/dist/src/init/compliance/types.d.ts +30 -27
  38. package/dist/src/init/compliance/types.d.ts.map +1 -1
  39. package/dist/src/init/repo/types.d.ts +11 -34
  40. package/dist/src/init/repo/types.d.ts.map +1 -1
  41. package/dist/src/init/research/src/config/types.d.ts +15 -82
  42. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  43. package/dist/src/init/research/types.d.ts +38 -93
  44. package/dist/src/init/research/types.d.ts.map +1 -1
  45. package/dist/src/init/team/types.d.ts +4 -42
  46. package/dist/src/init/team/types.d.ts.map +1 -1
  47. package/dist/src/utils/external-tool-drift-detector.d.ts +76 -0
  48. package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -0
  49. package/dist/src/utils/external-tool-drift-detector.js +235 -0
  50. package/dist/src/utils/external-tool-drift-detector.js.map +1 -0
  51. package/package.json +5 -5
  52. package/plugins/specweave/hooks/post-task-completion.sh +6 -6
  53. package/plugins/specweave/hooks/pre-increment-start.sh +6 -1
  54. package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +62 -89
  55. package/plugins/specweave/lib/hooks/us-completion-orchestrator.ts +215 -0
  56. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +16 -0
  57. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  58. package/plugins/specweave/skills/brownfield-analyzer/SKILL.md +267 -868
  59. package/plugins/specweave/skills/increment-planner/SKILL.md +337 -1253
  60. package/plugins/specweave/skills/role-orchestrator/SKILL.md +293 -969
  61. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +333 -839
  62. package/plugins/specweave-release/hooks/post-task-completion.sh +5 -1
  63. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +269 -749
  64. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +318 -810
@@ -5,17 +5,15 @@ description: Test-Driven Development (TDD) expertise covering red-green-refactor
5
5
 
6
6
  # Test-Driven Development (TDD) Expert
7
7
 
8
- ## Core Philosophy
8
+ **Self-contained TDD expertise for ANY user project.**
9
9
 
10
- Test-Driven Development is a software development approach where tests are written BEFORE the implementation code. This forces better design, ensures testability, and provides a safety net for refactoring.
11
-
12
- **Core Principle**: "Red, Green, Refactor"
10
+ ---
13
11
 
14
- ## The TDD Cycle
12
+ ## The TDD Cycle: Red-Green-Refactor
15
13
 
16
- ### 1. Red Phase: Write a Failing Test
14
+ ### 1. RED Phase: Write Failing Test
17
15
 
18
- **Goal**: Define expected behavior through a test that fails
16
+ **Goal**: Define expected behavior through a failing test
19
17
 
20
18
  ```typescript
21
19
  import { describe, it, expect } from 'vitest';
@@ -24,911 +22,433 @@ import { Calculator } from './Calculator';
24
22
  describe('Calculator', () => {
25
23
  it('should add two numbers', () => {
26
24
  const calculator = new Calculator();
27
-
28
- // This test WILL fail because Calculator doesn't exist yet
29
- expect(calculator.add(2, 3)).toBe(5);
25
+ expect(calculator.add(2, 3)).toBe(5); // WILL FAIL - Calculator doesn't exist
30
26
  });
31
27
  });
32
28
  ```
33
29
 
34
- **Red Phase Checklist**:
30
+ **RED Checklist**:
35
31
  - [ ] Test describes ONE specific behavior
36
- - [ ] Test fails for the RIGHT reason (not syntax error)
37
- - [ ] Test is readable and understandable
38
- - [ ] Expected behavior is clear from test name
32
+ - [ ] Test fails for RIGHT reason (not syntax error)
33
+ - [ ] Test name is clear
34
+ - [ ] Expected behavior obvious
39
35
 
40
- **Common Mistakes in Red Phase**:
41
- - Writing multiple assertions in one test
42
- - Testing implementation details instead of behavior
43
- - Writing tests that pass immediately (no value!)
44
- - Unclear test names
36
+ ### 2. GREEN Phase: Minimal Implementation
45
37
 
46
- ### 2. Green Phase: Make It Pass (Minimal Implementation)
47
-
48
- **Goal**: Write the simplest code that makes the test pass
38
+ **Goal**: Simplest code that makes test pass
49
39
 
50
40
  ```typescript
51
41
  // Calculator.ts
52
42
  export class Calculator {
53
43
  add(a: number, b: number): number {
54
- return 5; // Hardcoded! But test passes
44
+ return a + b; // Minimal implementation
55
45
  }
56
46
  }
57
47
  ```
58
48
 
59
- **Wait, hardcoded 5?** Yes! This is intentional. The green phase is about making tests pass with minimal code. The next test will force us to generalize.
60
-
61
- **Add another test** (triangulation):
62
- ```typescript
63
- it('should add different numbers', () => {
64
- const calculator = new Calculator();
65
- expect(calculator.add(10, 20)).toBe(30); // Now hardcoded 5 fails!
66
- });
67
- ```
68
-
69
- **Now implement properly**:
70
- ```typescript
71
- export class Calculator {
72
- add(a: number, b: number): number {
73
- return a + b; // Generalized solution
74
- }
75
- }
76
- ```
77
-
78
- **Green Phase Checklist**:
79
- - [ ] All tests pass
80
- - [ ] Implementation is minimal (no premature optimization)
81
- - [ ] No extra features beyond what tests require
82
- - [ ] Code is simple and direct
83
-
84
- **Common Mistakes in Green Phase**:
85
- - Over-engineering the solution
86
- - Adding features not covered by tests
87
- - Premature optimization
88
- - Skipping the refactor phase
49
+ **GREEN Checklist**:
50
+ - [ ] Test passes
51
+ - [ ] Code is simplest possible
52
+ - [ ] No premature optimization
53
+ - [ ] No extra features
89
54
 
90
- ### 3. Refactor Phase: Improve Code Quality
55
+ ### 3. REFACTOR Phase: Improve Design
91
56
 
92
- **Goal**: Improve code structure while keeping all tests green
57
+ **Goal**: Improve code quality without changing behavior
93
58
 
94
59
  ```typescript
95
- // Before refactoring
60
+ // Refactor: Support variable arguments
96
61
  export class Calculator {
97
- add(a: number, b: number): number {
98
- return a + b;
99
- }
100
-
101
- subtract(a: number, b: number): number {
102
- return a - b;
103
- }
104
-
105
- multiply(a: number, b: number): number {
106
- return a * b;
107
- }
108
-
109
- divide(a: number, b: number): number {
110
- if (b === 0) {
111
- throw new Error('Division by zero');
112
- }
113
- return a / b;
114
- }
115
- }
116
- ```
117
-
118
- **Refactored with better design**:
119
- ```typescript
120
- // Extract operation interface
121
- interface Operation {
122
- execute(a: number, b: number): number;
123
- }
124
-
125
- class AddOperation implements Operation {
126
- execute(a: number, b: number): number {
127
- return a + b;
128
- }
129
- }
130
-
131
- class DivideOperation implements Operation {
132
- execute(a: number, b: number): number {
133
- if (b === 0) {
134
- throw new Error('Division by zero');
135
- }
136
- return a / b;
62
+ add(...numbers: number[]): number {
63
+ return numbers.reduce((sum, n) => sum + n, 0);
137
64
  }
138
65
  }
139
66
 
140
- export class Calculator {
141
- private operations: Map<string, Operation> = new Map([
142
- ['add', new AddOperation()],
143
- ['divide', new DivideOperation()],
144
- ]);
145
-
146
- perform(operation: string, a: number, b: number): number {
147
- const op = this.operations.get(operation);
148
- if (!op) {
149
- throw new Error(`Unknown operation: ${operation}`);
150
- }
151
- return op.execute(a, b);
152
- }
153
- }
67
+ // Tests still pass!
154
68
  ```
155
69
 
156
- **Refactor Phase Checklist**:
70
+ **REFACTOR Checklist**:
157
71
  - [ ] All tests still pass
158
72
  - [ ] Code is more readable
159
- - [ ] Duplication removed (DRY principle)
160
- - [ ] Code follows SOLID principles
161
- - [ ] Better separation of concerns
162
- - [ ] No new functionality added
163
-
164
- **Refactoring Patterns**:
165
- - Extract Method/Function
166
- - Extract Class
167
- - Introduce Parameter Object
168
- - Replace Conditional with Polymorphism
169
- - Extract Interface
170
- - Rename for clarity
171
-
172
- ## TDD Best Practices
73
+ - [ ] Removed duplication
74
+ - [ ] Better design patterns
173
75
 
174
- ### 1. Test Behavior, Not Implementation
175
-
176
- **❌ BAD: Testing implementation details**
177
- ```typescript
178
- it('should call internal helper method', () => {
179
- const spy = vi.spyOn(calculator, '_internalHelper');
180
- calculator.add(2, 3);
181
- expect(spy).toHaveBeenCalled(); // Brittle! Breaks on refactor
182
- });
183
- ```
184
-
185
- **✅ GOOD: Testing behavior**
186
- ```typescript
187
- it('should return sum of two numbers', () => {
188
- expect(calculator.add(2, 3)).toBe(5); // Robust!
189
- });
190
- ```
76
+ ---
191
77
 
192
- ### 2. One Assertion Per Test (When Possible)
78
+ ## TDD Benefits
193
79
 
194
- **❌ BAD: Multiple unrelated assertions**
195
- ```typescript
196
- it('should validate user', () => {
197
- expect(user.name).toBe('John');
198
- expect(user.email).toBe('john@example.com');
199
- expect(user.isActive).toBe(true);
200
- expect(user.roles).toContain('admin');
201
- });
202
- ```
80
+ **Design Benefits**:
81
+ - Forces modular, testable code
82
+ - Reveals design problems early
83
+ - Encourages SOLID principles
84
+ - Promotes simple solutions
203
85
 
204
- **✅ GOOD: Focused tests**
205
- ```typescript
206
- it('should have correct name', () => {
207
- expect(user.name).toBe('John');
208
- });
86
+ **Quality Benefits**:
87
+ - 100% test coverage (by definition)
88
+ - Tests document behavior
89
+ - Regression safety net
90
+ - Faster debugging
209
91
 
210
- it('should have valid email', () => {
211
- expect(user.email).toBe('john@example.com');
212
- });
92
+ **Productivity Benefits**:
93
+ - Less time debugging
94
+ - Confidence to refactor
95
+ - Faster iterations
96
+ - Clearer requirements
213
97
 
214
- it('should be active by default', () => {
215
- expect(user.isActive).toBe(true);
216
- });
98
+ ---
217
99
 
218
- it('should include admin role', () => {
219
- expect(user.roles).toContain('admin');
220
- });
221
- ```
100
+ ## BDD: Behavior-Driven Development
222
101
 
223
- ### 3. Test Edge Cases and Boundaries
102
+ **Extension of TDD with natural language tests**
224
103
 
225
- **Test pyramid for a single function**:
226
- 1. Happy path (normal input)
227
- 2. Edge cases (empty, null, boundary values)
228
- 3. Error cases (invalid input)
104
+ ### Given-When-Then Pattern
229
105
 
230
106
  ```typescript
231
- describe('divide', () => {
232
- // Happy path
233
- it('should divide two positive numbers', () => {
234
- expect(calculator.divide(10, 2)).toBe(5);
235
- });
236
-
237
- // Edge cases
238
- it('should handle division resulting in decimal', () => {
239
- expect(calculator.divide(5, 2)).toBe(2.5);
240
- });
241
-
242
- it('should handle negative numbers', () => {
243
- expect(calculator.divide(-10, 2)).toBe(-5);
244
- });
107
+ describe('Shopping Cart', () => {
108
+ it('should apply 10% discount when total exceeds $100', () => {
109
+ // Given: A cart with $120 worth of items
110
+ const cart = new ShoppingCart();
111
+ cart.addItem({ price: 120, quantity: 1 });
245
112
 
246
- it('should handle zero dividend', () => {
247
- expect(calculator.divide(0, 5)).toBe(0);
248
- });
113
+ // When: Getting the total
114
+ const total = cart.getTotal();
249
115
 
250
- // Error case
251
- it('should throw error for division by zero', () => {
252
- expect(() => calculator.divide(10, 0)).toThrow('Division by zero');
116
+ // Then: 10% discount applied
117
+ expect(total).toBe(108); // $120 - $12 (10%)
253
118
  });
254
119
  });
255
120
  ```
256
121
 
257
- ### 4. Use Descriptive Test Names
122
+ **BDD Benefits**:
123
+ - Tests readable by non-developers
124
+ - Clear business requirements
125
+ - Better stakeholder communication
126
+ - Executable specifications
258
127
 
259
- **Pattern**: `should [expected behavior] when [condition]`
128
+ ---
260
129
 
261
- ```typescript
262
- // ✅ GOOD: Clear and descriptive
263
- it('should return empty array when no users exist', () => { ... });
264
- it('should throw ValidationError when email is invalid', () => { ... });
265
- it('should apply discount when user has premium membership', () => { ... });
266
-
267
- // ❌ BAD: Vague and unclear
268
- it('works', () => { ... });
269
- it('test user creation', () => { ... });
270
- it('returns data', () => { ... });
271
- ```
130
+ ## TDD Patterns
272
131
 
273
- ### 5. Follow AAA Pattern (Arrange-Act-Assert)
132
+ ### Pattern 1: Test List
274
133
 
275
- ```typescript
276
- it('should calculate total with discount', () => {
277
- // ARRANGE: Set up test data and dependencies
278
- const cart = new ShoppingCart();
279
- cart.addItem({ name: 'Book', price: 20 });
280
- cart.addItem({ name: 'Pen', price: 5 });
281
- const discountCode = 'SAVE10';
282
-
283
- // ACT: Execute the behavior under test
284
- const total = cart.calculateTotal(discountCode);
285
-
286
- // ASSERT: Verify the result
287
- expect(total).toBe(22.5); // 25 * 0.9 = 22.5
288
- });
134
+ Before coding, list all tests needed:
135
+
136
+ ```markdown
137
+ Calculator Tests:
138
+ - [ ] add two positive numbers
139
+ - [ ] add negative numbers
140
+ - [ ] add zero
141
+ - [ ] add multiple numbers
142
+ - [ ] multiply two numbers
143
+ - [ ] divide two numbers
144
+ - [ ] divide by zero (error)
289
145
  ```
290
146
 
291
- ### 6. Test Isolation (No Shared State)
147
+ Work through list one by one.
292
148
 
293
- **❌ BAD: Shared state between tests**
294
- ```typescript
295
- let user: User; // SHARED STATE!
149
+ ### Pattern 2: Fake It Till You Make It
296
150
 
297
- beforeAll(() => {
298
- user = new User('John'); // Created once
299
- });
151
+ Start with hardcoded returns, generalize later:
300
152
 
301
- it('should update name', () => {
302
- user.updateName('Jane'); // MUTATES SHARED STATE
303
- expect(user.name).toBe('Jane');
304
- });
153
+ ```typescript
154
+ // Test 1: add(2, 3) = 5
155
+ add(a, b) { return 5; } // Hardcoded!
305
156
 
306
- it('should have original name', () => {
307
- expect(user.name).toBe('John'); // FAILS! Name is 'Jane' from previous test
308
- });
157
+ // Test 2: add(5, 7) = 12
158
+ add(a, b) { return a + b; } // Generalized
309
159
  ```
310
160
 
311
- **✅ GOOD: Isolated tests**
161
+ ### Pattern 3: Triangulation
162
+
163
+ Use multiple tests to force generalization:
164
+
312
165
  ```typescript
313
- describe('User', () => {
314
- let user: User;
166
+ // Test 1
167
+ expect(fizzbuzz(3)).toBe('Fizz');
315
168
 
316
- beforeEach(() => {
317
- user = new User('John'); // FRESH instance for each test
318
- });
169
+ // Test 2
170
+ expect(fizzbuzz(5)).toBe('Buzz');
319
171
 
320
- it('should update name', () => {
321
- user.updateName('Jane');
322
- expect(user.name).toBe('Jane');
323
- });
172
+ // Test 3
173
+ expect(fizzbuzz(15)).toBe('FizzBuzz');
324
174
 
325
- it('should have original name', () => {
326
- expect(user.name).toBe('John'); // PASSES! Fresh instance
327
- });
328
- });
175
+ // Forces complete implementation
329
176
  ```
330
177
 
331
- ## TDD Workflow Examples
178
+ ### Pattern 4: Test Data Builders
332
179
 
333
- ### Example 1: Building a User Validator
180
+ Create test helpers for complex objects:
334
181
 
335
- **Step 1: RED - Write failing test**
336
182
  ```typescript
337
- describe('UserValidator', () => {
338
- it('should validate email format', () => {
339
- const validator = new UserValidator();
340
- expect(validator.validateEmail('user@example.com')).toBe(true);
341
- });
342
- });
343
- ```
183
+ class UserBuilder {
184
+ private user = { name: 'Test', email: 'test@example.com', role: 'user' };
344
185
 
345
- **Step 2: GREEN - Minimal implementation**
346
- ```typescript
347
- export class UserValidator {
348
- validateEmail(email: string): boolean {
349
- return true; // Hardcoded! But test passes
186
+ withName(name: string) {
187
+ this.user.name = name;
188
+ return this;
350
189
  }
351
- }
352
- ```
353
190
 
354
- **Step 3: Add negative test (triangulation)**
355
- ```typescript
356
- it('should reject invalid email', () => {
357
- const validator = new UserValidator();
358
- expect(validator.validateEmail('invalid')).toBe(false); // Forces real implementation
359
- });
360
- ```
361
-
362
- **Step 4: GREEN - Real implementation**
363
- ```typescript
364
- export class UserValidator {
365
- validateEmail(email: string): boolean {
366
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
191
+ withRole(role: string) {
192
+ this.user.role = role;
193
+ return this;
367
194
  }
368
- }
369
- ```
370
-
371
- **Step 5: REFACTOR - Extract regex**
372
- ```typescript
373
- export class UserValidator {
374
- private static EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
375
195
 
376
- validateEmail(email: string): boolean {
377
- return UserValidator.EMAIL_REGEX.test(email);
196
+ build() {
197
+ return this.user;
378
198
  }
379
199
  }
380
- ```
381
-
382
- **Step 6: Add more edge cases**
383
- ```typescript
384
- it('should reject empty email', () => {
385
- expect(validator.validateEmail('')).toBe(false);
386
- });
387
200
 
388
- it('should reject null email', () => {
389
- expect(validator.validateEmail(null as any)).toBe(false);
390
- });
391
-
392
- it('should reject email without domain', () => {
393
- expect(validator.validateEmail('user@')).toBe(false);
394
- });
201
+ // Usage
202
+ const admin = new UserBuilder().withRole('admin').build();
395
203
  ```
396
204
 
397
- **Final refactored implementation**:
398
- ```typescript
399
- export class UserValidator {
400
- private static EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
401
-
402
- validateEmail(email: string | null | undefined): boolean {
403
- if (!email || typeof email !== 'string') {
404
- return false;
405
- }
406
- return UserValidator.EMAIL_REGEX.test(email);
407
- }
408
- }
409
- ```
205
+ ---
410
206
 
411
- ### Example 2: Building a Shopping Cart
207
+ ## Refactoring with Confidence
412
208
 
413
- **RED: Test adding items**
414
- ```typescript
415
- describe('ShoppingCart', () => {
416
- it('should add item to cart', () => {
417
- const cart = new ShoppingCart();
418
- cart.addItem({ id: 1, name: 'Book', price: 20 });
209
+ **The TDD Safety Net**
419
210
 
420
- expect(cart.getItemCount()).toBe(1);
421
- });
422
- });
423
- ```
211
+ ### Refactoring Types
424
212
 
425
- **GREEN: Minimal implementation**
213
+ **1. Extract Method**:
426
214
  ```typescript
427
- interface CartItem {
428
- id: number;
429
- name: string;
430
- price: number;
215
+ // Before
216
+ function processOrder(order) {
217
+ const total = order.items.reduce((sum, item) => sum + item.price, 0);
218
+ const tax = total * 0.1;
219
+ return total + tax;
431
220
  }
432
221
 
433
- export class ShoppingCart {
434
- private items: CartItem[] = [];
435
-
436
- addItem(item: CartItem): void {
437
- this.items.push(item);
438
- }
439
-
440
- getItemCount(): number {
441
- return this.items.length;
442
- }
222
+ // After (refactored with test safety)
223
+ function calculateTotal(items) {
224
+ return items.reduce((sum, item) => sum + item.price, 0);
443
225
  }
444
- ```
445
-
446
- **RED: Test calculating total**
447
- ```typescript
448
- it('should calculate total price', () => {
449
- const cart = new ShoppingCart();
450
- cart.addItem({ id: 1, name: 'Book', price: 20 });
451
- cart.addItem({ id: 2, name: 'Pen', price: 5 });
452
-
453
- expect(cart.getTotal()).toBe(25);
454
- });
455
- ```
456
226
 
457
- **GREEN: Implement total**
458
- ```typescript
459
- export class ShoppingCart {
460
- // ... previous code ...
227
+ function calculateTax(total) {
228
+ return total * 0.1;
229
+ }
461
230
 
462
- getTotal(): number {
463
- return this.items.reduce((sum, item) => sum + item.price, 0);
464
- }
231
+ function processOrder(order) {
232
+ const total = calculateTotal(order.items);
233
+ const tax = calculateTax(total);
234
+ return total + tax;
465
235
  }
466
236
  ```
467
237
 
468
- **RED: Test removing items**
238
+ **2. Remove Duplication**:
469
239
  ```typescript
470
- it('should remove item from cart', () => {
471
- const cart = new ShoppingCart();
472
- cart.addItem({ id: 1, name: 'Book', price: 20 });
473
- cart.removeItem(1);
474
-
475
- expect(cart.getItemCount()).toBe(0);
240
+ // Tests force you to see duplication
241
+ it('should validate email', () => {
242
+ expect(validateEmail('test@example.com')).toBe(true);
243
+ expect(validateEmail('invalid')).toBe(false);
476
244
  });
477
- ```
478
245
 
479
- **GREEN: Implement remove**
480
- ```typescript
481
- export class ShoppingCart {
482
- // ... previous code ...
246
+ it('should validate phone', () => {
247
+ expect(validatePhone('+1-555-0100')).toBe(true);
248
+ expect(validatePhone('invalid')).toBe(false);
249
+ });
483
250
 
484
- removeItem(itemId: number): void {
485
- this.items = this.items.filter(item => item.id !== itemId);
486
- }
487
- }
251
+ // Extract common validation pattern
488
252
  ```
489
253
 
490
- **REFACTOR: Extract calculations**
491
- ```typescript
492
- export class ShoppingCart {
493
- private items: CartItem[] = [];
494
-
495
- addItem(item: CartItem): void {
496
- this.items.push(item);
497
- }
254
+ ### Refactoring Workflow
498
255
 
499
- removeItem(itemId: number): void {
500
- this.items = this.items.filter(item => item.id !== itemId);
501
- }
502
-
503
- getItemCount(): number {
504
- return this.items.length;
505
- }
506
-
507
- getTotal(): number {
508
- return this.calculateTotal(this.items);
509
- }
510
-
511
- private calculateTotal(items: CartItem[]): number {
512
- return items.reduce((sum, item) => sum + item.price, 0);
513
- }
514
-
515
- isEmpty(): boolean {
516
- return this.items.length === 0;
517
- }
518
- }
256
+ ```
257
+ 1. All tests GREEN? Continue
258
+ 2. Identify code smell
259
+ 3. Make small refactoring
260
+ 4. Run tests → GREEN? → Continue
261
+ 5. Repeat until satisfied
262
+ 6. Commit
519
263
  ```
520
264
 
521
- ## TDD with Mocks and Test Doubles
522
-
523
- ### Types of Test Doubles
265
+ ---
524
266
 
525
- 1. **Dummy**: Objects passed but never used
526
- 2. **Stub**: Returns predefined values
527
- 3. **Spy**: Records information about calls
528
- 4. **Mock**: Verifies interactions
529
- 5. **Fake**: Working implementation (simplified)
267
+ ## TDD Anti-Patterns
530
268
 
531
- ### Example: Testing with Mocks
269
+ ### Testing Implementation Details
532
270
 
533
- **RED: Test user creation with email service**
534
271
  ```typescript
535
- describe('UserService', () => {
536
- it('should send welcome email when user created', async () => {
537
- const emailService = createMockEmailService();
538
- const userService = new UserService(emailService);
539
-
540
- await userService.createUser({ name: 'John', email: 'john@example.com' });
541
-
542
- expect(emailService.sendWelcomeEmail).toHaveBeenCalledWith('john@example.com');
543
- });
272
+ // BAD: Testing private method
273
+ it('should call _validateEmail internally', () => {
274
+ spyOn(service, '_validateEmail');
275
+ service.createUser({ email: 'test@example.com' });
276
+ expect(service._validateEmail).toHaveBeenCalled();
544
277
  });
545
- ```
546
278
 
547
- **GREEN: Implementation with dependency injection**
548
- ```typescript
549
- interface EmailService {
550
- sendWelcomeEmail(email: string): Promise<void>;
551
- }
552
-
553
- export class UserService {
554
- constructor(private emailService: EmailService) {}
555
-
556
- async createUser(userData: { name: string; email: string }): Promise<User> {
557
- const user = new User(userData);
558
- await this.emailService.sendWelcomeEmail(user.email);
559
- return user;
560
- }
561
- }
562
-
563
- // Test helper
564
- function createMockEmailService(): EmailService {
565
- return {
566
- sendWelcomeEmail: vi.fn().mockResolvedValue(undefined),
567
- };
568
- }
279
+ // GOOD: Testing behavior
280
+ it('should reject invalid email', () => {
281
+ expect(() => service.createUser({ email: 'invalid' }))
282
+ .toThrow('Invalid email');
283
+ });
569
284
  ```
570
285
 
571
- ## TDD and SOLID Principles
572
-
573
- TDD naturally leads to SOLID design:
574
-
575
- ### 1. Single Responsibility Principle (SRP)
576
-
577
- **TDD forces SRP** because classes with multiple responsibilities are hard to test.
286
+ ### Writing Tests After Code
578
287
 
579
288
  ```typescript
580
- // BAD: Multiple responsibilities (hard to test!)
581
- class UserManager {
582
- createUser() { /* ... */ }
583
- sendEmail() { /* ... */ }
584
- saveToDatabase() { /* ... */ }
585
- validateInput() { /* ... */ }
586
- }
289
+ // Wrong order!
290
+ 1. Write implementation
291
+ 2. Write tests
587
292
 
588
- // GOOD: Single responsibility (easy to test!)
589
- class UserCreator {
590
- createUser() { /* ... */ }
591
- }
592
-
593
- class EmailSender {
594
- sendEmail() { /* ... */ }
595
- }
596
-
597
- class UserRepository {
598
- save() { /* ... */ }
599
- }
600
-
601
- class UserValidator {
602
- validate() { /* ... */ }
603
- }
293
+ // Correct TDD:
294
+ 1. Write test (RED)
295
+ 2. Write implementation (GREEN)
296
+ 3. Refactor
604
297
  ```
605
298
 
606
- ### 2. Open/Closed Principle (OCP)
607
-
608
- **TDD encourages extension through abstraction**
299
+ ### Large Tests
609
300
 
610
301
  ```typescript
611
- // Extension through interfaces
612
- interface PaymentProcessor {
613
- process(amount: number): Promise<PaymentResult>;
614
- }
615
-
616
- class StripeProcessor implements PaymentProcessor {
617
- async process(amount: number): Promise<PaymentResult> {
618
- // Stripe implementation
619
- }
620
- }
621
-
622
- class PayPalProcessor implements PaymentProcessor {
623
- async process(amount: number): Promise<PaymentResult> {
624
- // PayPal implementation
625
- }
626
- }
627
-
628
- // Tests can easily mock PaymentProcessor
629
- describe('OrderService', () => {
630
- it('should process payment', async () => {
631
- const mockProcessor: PaymentProcessor = {
632
- process: vi.fn().mockResolvedValue({ success: true }),
633
- };
302
+ // BAD: Testing multiple behaviors
303
+ it('should handle user lifecycle', () => {
304
+ const user = createUser();
305
+ updateUser(user, { name: 'New Name' });
306
+ deleteUser(user);
307
+ // Too much in one test!
308
+ });
634
309
 
635
- const orderService = new OrderService(mockProcessor);
636
- await orderService.completeOrder(100);
310
+ // GOOD: One behavior per test
311
+ it('should create user', () => {
312
+ const user = createUser();
313
+ expect(user).toBeDefined();
314
+ });
637
315
 
638
- expect(mockProcessor.process).toHaveBeenCalledWith(100);
639
- });
316
+ it('should update user name', () => {
317
+ const user = createUser();
318
+ updateUser(user, { name: 'New Name' });
319
+ expect(user.name).toBe('New Name');
640
320
  });
641
321
  ```
642
322
 
643
- ### 3. Liskov Substitution Principle (LSP)
644
-
645
- **TDD ensures LSP through contract testing**
323
+ ### Skipping Refactor Phase
646
324
 
647
325
  ```typescript
648
- // Base class contract
649
- abstract class Shape {
650
- abstract area(): number;
651
- }
652
-
653
- // Implementations must satisfy contract
654
- class Rectangle extends Shape {
655
- constructor(private width: number, private height: number) {
656
- super();
657
- }
658
-
659
- area(): number {
660
- return this.width * this.height;
661
- }
662
- }
663
-
664
- class Circle extends Shape {
665
- constructor(private radius: number) {
666
- super();
667
- }
668
-
669
- area(): number {
670
- return Math.PI * this.radius ** 2;
671
- }
672
- }
673
-
674
- // Test contract for all shapes
675
- function testShapeContract(createShape: () => Shape) {
676
- it('should have positive area', () => {
677
- const shape = createShape();
678
- expect(shape.area()).toBeGreaterThan(0);
679
- });
680
-
681
- it('should return number', () => {
682
- const shape = createShape();
683
- expect(typeof shape.area()).toBe('number');
684
- });
685
- }
686
-
687
- describe('Rectangle', () => {
688
- testShapeContract(() => new Rectangle(10, 5));
689
- });
690
-
691
- describe('Circle', () => {
692
- testShapeContract(() => new Circle(5));
693
- });
326
+ // Don't skip refactoring!
327
+ RED GREEN → REFACTOR → RED → GREEN → REFACTOR
328
+ ↑________________↑
329
+ Always refactor!
694
330
  ```
695
331
 
696
- ### 4. Interface Segregation Principle (ISP)
332
+ ---
697
333
 
698
- **TDD reveals when interfaces are too large**
334
+ ## Mock-Driven TDD
699
335
 
700
- ```typescript
701
- // ❌ BAD: Fat interface (forces unnecessary mocking)
702
- interface Worker {
703
- work(): void;
704
- eat(): void;
705
- sleep(): void;
706
- }
336
+ **When testing with external dependencies**
707
337
 
708
- // Mocking is painful
709
- const mockWorker: Worker = {
710
- work: vi.fn(),
711
- eat: vi.fn(), // Not needed for this test!
712
- sleep: vi.fn(), // Not needed for this test!
713
- };
338
+ ### Strategy 1: Dependency Injection
714
339
 
715
- // ✅ GOOD: Segregated interfaces
716
- interface Workable {
717
- work(): void;
718
- }
340
+ ```typescript
341
+ class UserService {
342
+ constructor(private db: Database) {} // Inject dependency
719
343
 
720
- interface Eatable {
721
- eat(): void;
344
+ async getUser(id: string) {
345
+ return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
346
+ }
722
347
  }
723
348
 
724
- // Only mock what you need
725
- const mockWorkable: Workable = {
726
- work: vi.fn(),
727
- };
349
+ // Test with mock
350
+ const mockDb = { query: vi.fn().mockResolvedValue({ id: '123' }) };
351
+ const service = new UserService(mockDb);
728
352
  ```
729
353
 
730
- ### 5. Dependency Inversion Principle (DIP)
731
-
732
- **TDD requires dependency injection (essential for testing)**
354
+ ### Strategy 2: Interface-Based Mocking
733
355
 
734
356
  ```typescript
735
- // GOOD: Depends on abstraction
736
- interface Logger {
737
- log(message: string): void;
357
+ interface EmailService {
358
+ send(to: string, subject: string, body: string): Promise<void>;
738
359
  }
739
360
 
740
- class UserService {
741
- constructor(private logger: Logger) {} // Injected dependency
361
+ class MockEmailService implements EmailService {
362
+ sent: any[] = [];
742
363
 
743
- createUser(name: string) {
744
- this.logger.log(`Creating user: ${name}`);
745
- // ...
364
+ async send(to: string, subject: string, body: string) {
365
+ this.sent.push({ to, subject, body });
746
366
  }
747
367
  }
748
368
 
749
- // Easy to test with mock
750
- describe('UserService', () => {
751
- it('should log user creation', () => {
752
- const mockLogger: Logger = { log: vi.fn() };
753
- const service = new UserService(mockLogger);
754
-
755
- service.createUser('John');
756
-
757
- expect(mockLogger.log).toHaveBeenCalledWith('Creating user: John');
758
- });
759
- });
369
+ // Test with mock
370
+ const mockEmail = new MockEmailService();
371
+ const service = new UserService(mockEmail);
372
+ await service.registerUser({ email: 'test@example.com' });
373
+ expect(mockEmail.sent).toHaveLength(1);
760
374
  ```
761
375
 
762
- ## TDD Anti-Patterns to Avoid
763
-
764
- ### 1. The Liar (Passing test that doesn't test anything)
765
-
766
- **❌ BAD**:
767
- ```typescript
768
- it('should validate user', () => {
769
- const result = true; // Hardcoded!
770
- expect(result).toBe(true); // Always passes
771
- });
772
- ```
376
+ ---
773
377
 
774
- ### 2. Excessive Setup (Tests too complex)
378
+ ## SOLID Principles Through TDD
775
379
 
776
- **❌ BAD**:
777
- ```typescript
778
- beforeEach(() => {
779
- // 50 lines of setup code...
780
- database.connect();
781
- seedTestData();
782
- setupMocks();
783
- configureEnvironment();
784
- // ...
785
- });
786
- ```
380
+ **TDD naturally leads to SOLID design**
787
381
 
788
- **✅ GOOD**: Extract to helper or fixture
382
+ ### Single Responsibility (SRP)
383
+ Tests reveal when class does too much:
789
384
  ```typescript
790
- beforeEach(() => {
791
- testEnv = createTestEnvironment(); // Encapsulated setup
385
+ // Many tests for one class? Split it!
386
+ describe('UserManager', () => {
387
+ // 20+ tests here → Too many responsibilities
792
388
  });
793
- ```
794
-
795
- ### 3. The Giant (One test testing everything)
796
389
 
797
- **❌ BAD**:
798
- ```typescript
799
- it('should handle entire user lifecycle', () => {
800
- // 200 lines of test code testing create, update, delete, search...
801
- });
390
+ // Refactor to multiple classes
391
+ describe('UserCreator', () => { /* 5 tests */ });
392
+ describe('UserValidator', () => { /* 5 tests */ });
393
+ describe('UserNotifier', () => { /* 5 tests */ });
802
394
  ```
803
395
 
804
- **✅ GOOD**: One test per behavior
396
+ ### Open/Closed (OCP)
397
+ Tests enable extension without modification:
805
398
  ```typescript
806
- it('should create user', () => { /* ... */ });
807
- it('should update user', () => { /* ... */ });
808
- it('should delete user', () => { /* ... */ });
809
- ```
810
-
811
- ### 4. The Mockery (Over-mocking)
812
-
813
- **❌ BAD**:
814
- ```typescript
815
- vi.mock('./utils');
816
- vi.mock('./helpers');
817
- vi.mock('./validators');
818
- vi.mock('./formatters');
819
- // Testing nothing but mocks!
820
- ```
399
+ // Testable, extensible design
400
+ interface PaymentProcessor {
401
+ process(amount: number): Promise<void>;
402
+ }
821
403
 
822
- **✅ GOOD**: Mock only external dependencies
823
- ```typescript
824
- vi.mock('./externalApi'); // Mock external API only
825
- // Test real code integration
404
+ class StripeProcessor implements PaymentProcessor { }
405
+ class PayPalProcessor implements PaymentProcessor { }
826
406
  ```
827
407
 
828
- ### 5. The Slow Poke (Slow tests)
829
-
830
- **❌ BAD**:
408
+ ### Dependency Inversion (DIP)
409
+ TDD requires dependency injection:
831
410
  ```typescript
832
- it('should process data', async () => {
833
- await sleep(5000); // Hardcoded delays
834
- // ...
835
- });
836
- ```
411
+ // Testable: Depends on abstraction
412
+ class OrderService {
413
+ constructor(private payment: PaymentProcessor) {}
414
+ }
837
415
 
838
- **✅ GOOD**: Use fake timers
839
- ```typescript
840
- it('should process data', () => {
841
- vi.useFakeTimers();
842
- // ...
843
- vi.advanceTimersByTime(5000);
844
- vi.restoreAllTimers();
845
- });
416
+ // Easy to test with mocks
417
+ const mockPayment = new MockPaymentProcessor();
418
+ const service = new OrderService(mockPayment);
846
419
  ```
847
420
 
848
- ## TDD Metrics & Success Indicators
849
-
850
- **Good TDD indicators**:
851
- - ✅ 80%+ code coverage
852
- - ✅ Tests run in < 1 second (unit tests)
853
- - ✅ Tests are independent and can run in any order
854
- - ✅ Tests are readable and self-documenting
855
- - ✅ Red-Green-Refactor cycle followed consistently
856
- - ✅ Code is modular and testable
857
-
858
- **Warning signs**:
859
- - ❌ Tests take minutes to run
860
- - ❌ Tests fail randomly (flakiness)
861
- - ❌ Hard to write tests (indicates design issues)
862
- - ❌ Mocking everything (over-mocking)
863
- - ❌ Tests break on refactoring (testing implementation)
864
-
865
- ## TDD in Practice: Real-World Tips
866
-
867
- ### 1. Start Small
868
- Begin TDD on new features, not legacy code. Retrofit tests gradually.
869
-
870
- ### 2. Write Test First (Always!)
871
- Resist urge to code first. The test is your design tool.
872
-
873
- ### 3. Keep Tests Fast
874
- Unit tests should run in milliseconds. Slow tests kill TDD.
875
-
876
- ### 4. Commit Test + Code Together
877
- Tests are part of the implementation, not afterthought.
878
-
879
- ### 5. Refactor Relentlessly
880
- Green doesn't mean done. Refactor for clarity.
881
-
882
- ### 6. Test Behavior, Not Implementation
883
- Tests should survive refactoring.
884
-
885
- ### 7. Use TDD to Drive Design
886
- Let tests guide your architecture (dependency injection, SOLID).
887
-
888
- ## TDD Workflow Commands
889
-
890
- **Development cycle**:
891
- ```bash
892
- # 1. Write failing test (RED)
893
- npm test -- --watch
894
-
895
- # 2. Implement (GREEN)
896
- # Make changes, watch tests turn green
421
+ ---
897
422
 
898
- # 3. Refactor
899
- # Improve code, tests stay green
423
+ ## Quick Reference
900
424
 
901
- # 4. Commit
902
- git add .
903
- git commit -m "feat: add user validation"
425
+ ### TDD Workflow
904
426
  ```
905
-
906
- **Coverage check**:
907
- ```bash
908
- npm test -- --coverage
909
- # Ensure 80%+ coverage
427
+ 1. Write test (RED) → Fails ✅
428
+ 2. Minimal code (GREEN) → Passes ✅
429
+ 3. Refactor → Still passes ✅
430
+ 4. Repeat
910
431
  ```
911
432
 
912
- ## Resources
913
-
914
- - **Kent Beck**: "Test-Driven Development by Example"
915
- - **Robert C. Martin**: "Clean Code" (TDD chapter)
916
- - **Martin Fowler**: Refactoring patterns
917
- - **Growing Object-Oriented Software, Guided by Tests** (Freeman & Pryce)
433
+ ### Test Smells
434
+ - Test too long (>20 lines)
435
+ - Multiple assertions (>3)
436
+ - Testing implementation
437
+ - Unclear test name
438
+ - Slow tests (>100ms)
439
+ - Flaky tests
918
440
 
919
- ## Summary
441
+ ### When to Use TDD
442
+ ✅ New features
443
+ ✅ Bug fixes (add test first)
444
+ ✅ Refactoring
445
+ ✅ Complex logic
446
+ ✅ Public APIs
920
447
 
921
- **TDD Core Rules**:
922
- 1. Write test FIRST (red)
923
- 2. Make it pass MINIMALLY (green)
924
- 3. Refactor RELENTLESSLY (refactor)
925
- 4. Repeat
448
+ Throwaway prototypes
449
+ UI layout (use E2E instead)
450
+ Highly experimental code
926
451
 
927
- **Benefits**:
928
- - Better design (testable = modular)
929
- - Living documentation
930
- - Fearless refactoring
931
- - Fast feedback loop
932
- - Higher confidence
452
+ ---
933
453
 
934
- **Remember**: TDD is about **design**, not just testing. Tests are a tool to drive better architecture.
454
+ **This skill is self-contained and works in ANY user project.**