specweave 0.23.18 → 0.24.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 (167) hide show
  1. package/.claude-plugin/marketplace.json +93 -49
  2. package/CLAUDE.md +137 -4
  3. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
  4. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
  5. package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
  6. package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
  7. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
  8. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
  9. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
  10. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
  11. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
  12. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  13. package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
  14. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  15. package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
  16. package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
  17. package/dist/src/cli/helpers/smart-filter.js +265 -0
  18. package/dist/src/cli/helpers/smart-filter.js.map +1 -0
  19. package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
  20. package/dist/src/core/qa/quality-gate-decider.js +2 -2
  21. package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
  22. package/dist/src/core/qa/risk-calculator.d.ts +2 -2
  23. package/dist/src/core/qa/risk-calculator.js +2 -2
  24. package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
  25. package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
  26. package/dist/src/core/validators/ac-presence-validator.js +149 -0
  27. package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
  28. package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
  29. package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
  30. package/dist/src/integrations/ado/area-path-mapper.js +267 -0
  31. package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
  32. package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
  33. package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
  34. package/dist/src/integrations/jira/filter-processor.js +207 -0
  35. package/dist/src/integrations/jira/filter-processor.js.map +1 -0
  36. package/dist/src/integrations/jira/jira-client.d.ts +13 -0
  37. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  38. package/dist/src/integrations/jira/jira-client.js +33 -0
  39. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  40. package/dist/src/utils/ac-embedder.d.ts +63 -0
  41. package/dist/src/utils/ac-embedder.d.ts.map +1 -0
  42. package/dist/src/utils/ac-embedder.js +217 -0
  43. package/dist/src/utils/ac-embedder.js.map +1 -0
  44. package/dist/src/utils/env-manager.d.ts +86 -0
  45. package/dist/src/utils/env-manager.d.ts.map +1 -0
  46. package/dist/src/utils/env-manager.js +188 -0
  47. package/dist/src/utils/env-manager.js.map +1 -0
  48. package/package.json +1 -1
  49. package/plugins/specweave/.claude-plugin/plugin.json +1 -1
  50. package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
  51. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
  52. package/plugins/specweave/commands/specweave-do.md +37 -0
  53. package/plugins/specweave/commands/specweave-done.md +159 -0
  54. package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
  55. package/plugins/specweave/commands/specweave-next.md +148 -3
  56. package/plugins/specweave/commands/specweave-qa.md +2 -2
  57. package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
  58. package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
  59. package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
  60. package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
  61. package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
  62. package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
  63. package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
  64. package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
  65. package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
  66. package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
  67. package/plugins/specweave-backend/commands/crud-generate.md +109 -0
  68. package/plugins/specweave-backend/commands/migration-generate.md +139 -0
  69. package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
  70. package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
  71. package/plugins/specweave-confluent/commands/schema-register.md +123 -0
  72. package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
  73. package/plugins/specweave-core/commands/architecture-review.md +288 -0
  74. package/plugins/specweave-core/commands/code-review.md +213 -0
  75. package/plugins/specweave-core/commands/refactor-plan.md +249 -0
  76. package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
  77. package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
  78. package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
  79. package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
  80. package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
  81. package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
  82. package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
  83. package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
  84. package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
  85. package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
  86. package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
  87. package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
  88. package/plugins/specweave-docs/commands/docs-generate.md +441 -0
  89. package/plugins/specweave-docs/commands/docs-init.md +334 -0
  90. package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
  91. package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
  92. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
  93. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
  94. package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
  95. package/plugins/specweave-figma/commands/figma-import.md +690 -0
  96. package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
  97. package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
  98. package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
  99. package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
  100. package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
  101. package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
  102. package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
  103. package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
  104. package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
  105. package/plugins/specweave-frontend/commands/component-generate.md +510 -0
  106. package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
  107. package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
  108. package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
  109. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
  110. package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
  111. package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
  112. package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
  113. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
  114. package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
  115. package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
  116. package/plugins/specweave-jira/commands/import-projects.js +183 -0
  117. package/plugins/specweave-jira/commands/import-projects.md +97 -0
  118. package/plugins/specweave-jira/commands/import-projects.ts +288 -0
  119. package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
  120. package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
  121. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
  122. package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
  123. package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
  124. package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
  125. package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
  126. package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
  127. package/plugins/specweave-mobile/commands/build-config.md +256 -0
  128. package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
  129. package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
  130. package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
  131. package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
  132. package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
  133. package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
  134. package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
  135. package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
  136. package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
  137. package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
  138. package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
  139. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
  140. package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
  141. package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
  142. package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
  143. package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
  144. package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
  145. package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
  146. package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
  147. package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
  148. package/plugins/specweave-testing/commands/test-coverage.md +979 -0
  149. package/plugins/specweave-testing/commands/test-generate.md +1156 -0
  150. package/plugins/specweave-testing/commands/test-init.md +409 -0
  151. package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
  152. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
  153. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
  154. package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
  155. package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
  156. package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
  157. package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
  158. package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
  159. package/plugins/specweave-ui/commands/ui-automate.md +199 -0
  160. package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
  161. package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
  162. package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
  163. package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
  164. package/plugins/specweave/commands/check-hooks.md +0 -257
  165. package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
  166. package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
  167. /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Vitest Unit Test Template
3
+ *
4
+ * This template demonstrates best practices for writing unit tests
5
+ * with Vitest for TypeScript projects.
6
+ *
7
+ * Features:
8
+ * - AAA pattern (Arrange-Act-Assert)
9
+ * - Test isolation
10
+ * - Mocking dependencies
11
+ * - Parametric testing
12
+ * - Error handling tests
13
+ */
14
+
15
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
16
+
17
+ // Import the module under test
18
+ import { YourModule } from './YourModule';
19
+
20
+ // Import dependencies (to be mocked)
21
+ import { ExternalDependency } from './ExternalDependency';
22
+
23
+ // ============================================================================
24
+ // MOCKS
25
+ // ============================================================================
26
+
27
+ // Mock external dependencies
28
+ vi.mock('./ExternalDependency', () => ({
29
+ ExternalDependency: vi.fn().mockImplementation(() => ({
30
+ fetchData: vi.fn().mockResolvedValue({ data: 'mocked' }),
31
+ processData: vi.fn(),
32
+ })),
33
+ }));
34
+
35
+ // ============================================================================
36
+ // TEST SUITE
37
+ // ============================================================================
38
+
39
+ describe('YourModule', () => {
40
+ // ========================================================================
41
+ // SETUP & TEARDOWN
42
+ // ========================================================================
43
+
44
+ let instance: YourModule;
45
+ let mockDependency: any;
46
+
47
+ beforeEach(() => {
48
+ // ARRANGE: Set up fresh instance for each test
49
+ mockDependency = new ExternalDependency();
50
+ instance = new YourModule(mockDependency);
51
+ });
52
+
53
+ afterEach(() => {
54
+ // TEARDOWN: Clean up mocks
55
+ vi.clearAllMocks();
56
+ });
57
+
58
+ // ========================================================================
59
+ // HAPPY PATH TESTS
60
+ // ========================================================================
61
+
62
+ describe('methodName', () => {
63
+ it('should perform expected operation with valid input', () => {
64
+ // ARRANGE
65
+ const input = 'test-input';
66
+ const expectedOutput = 'test-output';
67
+
68
+ // ACT
69
+ const result = instance.methodName(input);
70
+
71
+ // ASSERT
72
+ expect(result).toBe(expectedOutput);
73
+ });
74
+
75
+ it('should call dependency with correct parameters', () => {
76
+ // ARRANGE
77
+ const input = 'test-input';
78
+
79
+ // ACT
80
+ instance.methodName(input);
81
+
82
+ // ASSERT
83
+ expect(mockDependency.processData).toHaveBeenCalledWith(input);
84
+ expect(mockDependency.processData).toHaveBeenCalledTimes(1);
85
+ });
86
+ });
87
+
88
+ // ========================================================================
89
+ // ASYNC TESTS
90
+ // ========================================================================
91
+
92
+ describe('asyncMethod', () => {
93
+ it('should resolve with data on success', async () => {
94
+ // ARRANGE
95
+ const mockData = { id: 1, value: 'test' };
96
+ mockDependency.fetchData.mockResolvedValue(mockData);
97
+
98
+ // ACT
99
+ const result = await instance.asyncMethod();
100
+
101
+ // ASSERT
102
+ expect(result).toEqual(mockData);
103
+ expect(mockDependency.fetchData).toHaveBeenCalled();
104
+ });
105
+
106
+ it('should handle async errors gracefully', async () => {
107
+ // ARRANGE
108
+ const error = new Error('Network error');
109
+ mockDependency.fetchData.mockRejectedValue(error);
110
+
111
+ // ACT & ASSERT
112
+ await expect(instance.asyncMethod()).rejects.toThrow('Network error');
113
+ });
114
+ });
115
+
116
+ // ========================================================================
117
+ // EDGE CASES
118
+ // ========================================================================
119
+
120
+ describe('edge cases', () => {
121
+ it('should handle empty input', () => {
122
+ // ACT
123
+ const result = instance.methodName('');
124
+
125
+ // ASSERT
126
+ expect(result).toBe('');
127
+ });
128
+
129
+ it('should handle null input', () => {
130
+ // ACT
131
+ const result = instance.methodName(null as any);
132
+
133
+ // ASSERT
134
+ expect(result).toBeNull();
135
+ });
136
+
137
+ it('should handle undefined input', () => {
138
+ // ACT
139
+ const result = instance.methodName(undefined as any);
140
+
141
+ // ASSERT
142
+ expect(result).toBeUndefined();
143
+ });
144
+
145
+ it('should handle very large input', () => {
146
+ // ARRANGE
147
+ const largeInput = 'x'.repeat(1000000);
148
+
149
+ // ACT
150
+ const result = instance.methodName(largeInput);
151
+
152
+ // ASSERT
153
+ expect(result).toBeDefined();
154
+ });
155
+ });
156
+
157
+ // ========================================================================
158
+ // ERROR HANDLING
159
+ // ========================================================================
160
+
161
+ describe('error handling', () => {
162
+ it('should throw error for invalid input', () => {
163
+ // ARRANGE
164
+ const invalidInput = -1;
165
+
166
+ // ACT & ASSERT
167
+ expect(() => instance.methodName(invalidInput)).toThrow(
168
+ 'Input must be positive'
169
+ );
170
+ });
171
+
172
+ it('should throw specific error type', () => {
173
+ // ARRANGE
174
+ const invalidInput = 'invalid';
175
+
176
+ // ACT & ASSERT
177
+ expect(() => instance.methodName(invalidInput)).toThrow(ValidationError);
178
+ });
179
+
180
+ it('should include error details', () => {
181
+ // ARRANGE
182
+ const invalidInput = 'invalid';
183
+
184
+ // ACT
185
+ try {
186
+ instance.methodName(invalidInput);
187
+ fail('Expected error to be thrown');
188
+ } catch (error) {
189
+ // ASSERT
190
+ expect(error).toBeInstanceOf(ValidationError);
191
+ expect(error.message).toContain('invalid');
192
+ expect(error.code).toBe('INVALID_INPUT');
193
+ }
194
+ });
195
+ });
196
+
197
+ // ========================================================================
198
+ // PARAMETRIC TESTS (Table-Driven)
199
+ // ========================================================================
200
+
201
+ describe.each([
202
+ { input: 1, expected: 2 },
203
+ { input: 2, expected: 4 },
204
+ { input: 3, expected: 6 },
205
+ { input: 4, expected: 8 },
206
+ ])('methodName($input)', ({ input, expected }) => {
207
+ it(`should return ${expected}`, () => {
208
+ // ACT
209
+ const result = instance.methodName(input);
210
+
211
+ // ASSERT
212
+ expect(result).toBe(expected);
213
+ });
214
+ });
215
+
216
+ // ========================================================================
217
+ // STATE MANAGEMENT TESTS
218
+ // ========================================================================
219
+
220
+ describe('state management', () => {
221
+ it('should update internal state correctly', () => {
222
+ // ARRANGE
223
+ const initialState = instance.getState();
224
+
225
+ // ACT
226
+ instance.updateState('new-value');
227
+
228
+ // ASSERT
229
+ const newState = instance.getState();
230
+ expect(newState).not.toBe(initialState);
231
+ expect(newState).toBe('new-value');
232
+ });
233
+
234
+ it('should emit events on state change', () => {
235
+ // ARRANGE
236
+ const eventHandler = vi.fn();
237
+ instance.on('stateChanged', eventHandler);
238
+
239
+ // ACT
240
+ instance.updateState('new-value');
241
+
242
+ // ASSERT
243
+ expect(eventHandler).toHaveBeenCalledWith('new-value');
244
+ });
245
+ });
246
+
247
+ // ========================================================================
248
+ // PERFORMANCE TESTS
249
+ // ========================================================================
250
+
251
+ describe('performance', () => {
252
+ it('should complete within reasonable time', () => {
253
+ // ARRANGE
254
+ const startTime = Date.now();
255
+
256
+ // ACT
257
+ instance.methodName('test');
258
+
259
+ // ASSERT
260
+ const endTime = Date.now();
261
+ const executionTime = endTime - startTime;
262
+ expect(executionTime).toBeLessThan(100); // 100ms threshold
263
+ });
264
+
265
+ it('should handle large datasets efficiently', () => {
266
+ // ARRANGE
267
+ const largeDataset = Array.from({ length: 10000 }, (_, i) => i);
268
+
269
+ // ACT
270
+ const startTime = Date.now();
271
+ const result = instance.processBatch(largeDataset);
272
+ const endTime = Date.now();
273
+
274
+ // ASSERT
275
+ expect(result).toHaveLength(10000);
276
+ expect(endTime - startTime).toBeLessThan(1000); // 1s threshold
277
+ });
278
+ });
279
+
280
+ // ========================================================================
281
+ // SNAPSHOT TESTS
282
+ // ========================================================================
283
+
284
+ describe('snapshots', () => {
285
+ it('should match snapshot for complex output', () => {
286
+ // ARRANGE
287
+ const input = { id: 1, name: 'Test', nested: { value: 42 } };
288
+
289
+ // ACT
290
+ const result = instance.transform(input);
291
+
292
+ // ASSERT
293
+ expect(result).toMatchSnapshot();
294
+ });
295
+
296
+ it('should match inline snapshot for simple output', () => {
297
+ // ARRANGE
298
+ const input = 'test';
299
+
300
+ // ACT
301
+ const result = instance.methodName(input);
302
+
303
+ // ASSERT
304
+ expect(result).toMatchInlineSnapshot('"test-output"');
305
+ });
306
+ });
307
+
308
+ // ========================================================================
309
+ // TIMER/DEBOUNCE TESTS
310
+ // ========================================================================
311
+
312
+ describe('timers', () => {
313
+ beforeEach(() => {
314
+ vi.useFakeTimers();
315
+ });
316
+
317
+ afterEach(() => {
318
+ vi.restoreAllTimers();
319
+ });
320
+
321
+ it('should debounce function calls', () => {
322
+ // ARRANGE
323
+ const callback = vi.fn();
324
+ const debounced = instance.debounce(callback, 1000);
325
+
326
+ // ACT
327
+ debounced();
328
+ debounced();
329
+ debounced();
330
+
331
+ // ASSERT (not called yet)
332
+ expect(callback).not.toHaveBeenCalled();
333
+
334
+ // Fast-forward time
335
+ vi.advanceTimersByTime(1000);
336
+
337
+ // ASSERT (called once)
338
+ expect(callback).toHaveBeenCalledTimes(1);
339
+ });
340
+
341
+ it('should throttle function calls', () => {
342
+ // ARRANGE
343
+ const callback = vi.fn();
344
+ const throttled = instance.throttle(callback, 1000);
345
+
346
+ // ACT
347
+ throttled(); // Called immediately
348
+ throttled(); // Ignored (within throttle window)
349
+ throttled(); // Ignored (within throttle window)
350
+
351
+ // ASSERT
352
+ expect(callback).toHaveBeenCalledTimes(1);
353
+
354
+ // Fast-forward time
355
+ vi.advanceTimersByTime(1000);
356
+
357
+ // ACT
358
+ throttled(); // Called after throttle window
359
+
360
+ // ASSERT
361
+ expect(callback).toHaveBeenCalledTimes(2);
362
+ });
363
+ });
364
+ });
365
+
366
+ // ============================================================================
367
+ // HELPER TYPES & CLASSES (For Examples Above)
368
+ // ============================================================================
369
+
370
+ class ValidationError extends Error {
371
+ constructor(
372
+ message: string,
373
+ public code: string
374
+ ) {
375
+ super(message);
376
+ this.name = 'ValidationError';
377
+ }
378
+ }
379
+
380
+ // ============================================================================
381
+ // BEST PRACTICES CHECKLIST
382
+ // ============================================================================
383
+
384
+ /*
385
+ ✅ AAA Pattern (Arrange-Act-Assert)
386
+ ✅ Test Isolation (beforeEach creates fresh instance)
387
+ ✅ Descriptive Test Names (should do X when Y)
388
+ ✅ One Assertion Per Test (when possible)
389
+ ✅ Mock External Dependencies
390
+ ✅ Test Happy Path
391
+ ✅ Test Edge Cases (null, undefined, empty, large)
392
+ ✅ Test Error Handling
393
+ ✅ Parametric Tests (test.each)
394
+ ✅ Async Testing (async/await, rejects, resolves)
395
+ ✅ Timer Testing (useFakeTimers)
396
+ ✅ Performance Testing (execution time)
397
+ ✅ Snapshot Testing (complex outputs)
398
+ ✅ No Shared State Between Tests
399
+ ✅ Fast Execution (< 1s per test)
400
+ */