specweave 0.23.16 → 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 (227) hide show
  1. package/.claude-plugin/marketplace.json +93 -38
  2. package/CLAUDE.md +159 -11
  3. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  4. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +57 -0
  5. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  6. package/dist/src/cli/commands/sync-spec-content.js +3 -0
  7. package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
  8. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
  9. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
  10. package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
  11. package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
  12. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
  13. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
  14. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
  15. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
  16. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
  17. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  18. package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
  19. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  20. package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
  21. package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
  22. package/dist/src/cli/helpers/smart-filter.js +265 -0
  23. package/dist/src/cli/helpers/smart-filter.js.map +1 -0
  24. package/dist/src/core/progress/progress-tracker.d.ts +4 -1
  25. package/dist/src/core/progress/progress-tracker.d.ts.map +1 -1
  26. package/dist/src/core/progress/progress-tracker.js +33 -4
  27. package/dist/src/core/progress/progress-tracker.js.map +1 -1
  28. package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
  29. package/dist/src/core/qa/quality-gate-decider.js +2 -2
  30. package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
  31. package/dist/src/core/qa/risk-calculator.d.ts +2 -2
  32. package/dist/src/core/qa/risk-calculator.js +2 -2
  33. package/dist/src/core/spec-content-sync.d.ts +1 -1
  34. package/dist/src/core/spec-content-sync.d.ts.map +1 -1
  35. package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
  36. package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
  37. package/dist/src/core/validators/ac-presence-validator.js +149 -0
  38. package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
  39. package/dist/src/integrations/ado/ado-dependency-loader.d.ts +1 -1
  40. package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -1
  41. package/dist/src/integrations/ado/ado-dependency-loader.js +39 -7
  42. package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -1
  43. package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
  44. package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
  45. package/dist/src/integrations/ado/area-path-mapper.js +267 -0
  46. package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
  47. package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
  48. package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
  49. package/dist/src/integrations/jira/filter-processor.js +207 -0
  50. package/dist/src/integrations/jira/filter-processor.js.map +1 -0
  51. package/dist/src/integrations/jira/jira-client.d.ts +13 -0
  52. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  53. package/dist/src/integrations/jira/jira-client.js +33 -0
  54. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  55. package/dist/src/utils/ac-embedder.d.ts +63 -0
  56. package/dist/src/utils/ac-embedder.d.ts.map +1 -0
  57. package/dist/src/utils/ac-embedder.js +217 -0
  58. package/dist/src/utils/ac-embedder.js.map +1 -0
  59. package/dist/src/utils/env-manager.d.ts +86 -0
  60. package/dist/src/utils/env-manager.d.ts.map +1 -0
  61. package/dist/src/utils/env-manager.js +188 -0
  62. package/dist/src/utils/env-manager.js.map +1 -0
  63. package/package.json +1 -1
  64. package/plugins/specweave/.claude-plugin/plugin.json +1 -1
  65. package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
  66. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
  67. package/plugins/specweave/commands/specweave-do.md +37 -0
  68. package/plugins/specweave/commands/specweave-done.md +159 -0
  69. package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
  70. package/plugins/specweave/commands/specweave-next.md +148 -3
  71. package/plugins/specweave/commands/specweave-qa.md +2 -2
  72. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +1 -1
  73. package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +245 -0
  74. package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
  75. package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +149 -0
  76. package/plugins/specweave/hooks/lib/update-status-line.sh +34 -4
  77. package/plugins/specweave/hooks/lib/validate-spec-status.sh +1 -1
  78. package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +163 -0
  79. package/plugins/specweave/hooks/post-first-increment.sh +1 -1
  80. package/plugins/specweave/hooks/post-first-increment.sh.bak +61 -0
  81. package/plugins/specweave/hooks/post-spec-update.sh +1 -1
  82. package/plugins/specweave/hooks/post-spec-update.sh.bak +158 -0
  83. package/plugins/specweave/hooks/post-user-story-complete.sh +1 -1
  84. package/plugins/specweave/hooks/post-user-story-complete.sh.bak +179 -0
  85. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -1
  86. package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +83 -0
  87. package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
  88. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  89. package/plugins/specweave/hooks/user-prompt-submit.sh.bak +386 -0
  90. package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
  91. package/plugins/specweave/skills/specweave-framework/SKILL.md +1 -1
  92. package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
  93. package/plugins/specweave-ado/agents/ado-manager/AGENT.md +23 -0
  94. package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +23 -0
  95. package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +23 -0
  96. package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
  97. package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
  98. package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
  99. package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
  100. package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
  101. package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
  102. package/plugins/specweave-backend/agents/database-optimizer/AGENT.md +23 -0
  103. package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
  104. package/plugins/specweave-backend/commands/crud-generate.md +109 -0
  105. package/plugins/specweave-backend/commands/migration-generate.md +139 -0
  106. package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +23 -0
  107. package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
  108. package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
  109. package/plugins/specweave-confluent/commands/schema-register.md +123 -0
  110. package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
  111. package/plugins/specweave-core/commands/architecture-review.md +288 -0
  112. package/plugins/specweave-core/commands/code-review.md +213 -0
  113. package/plugins/specweave-core/commands/refactor-plan.md +249 -0
  114. package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
  115. package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
  116. package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
  117. package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
  118. package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
  119. package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
  120. package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
  121. package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
  122. package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
  123. package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
  124. package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +23 -0
  125. package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
  126. package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
  127. package/plugins/specweave-docs/commands/docs-generate.md +441 -0
  128. package/plugins/specweave-docs/commands/docs-init.md +334 -0
  129. package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
  130. package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
  131. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
  132. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
  133. package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
  134. package/plugins/specweave-figma/commands/figma-import.md +690 -0
  135. package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
  136. package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
  137. package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
  138. package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
  139. package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
  140. package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
  141. package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
  142. package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
  143. package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
  144. package/plugins/specweave-frontend/commands/component-generate.md +510 -0
  145. package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
  146. package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
  147. package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
  148. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
  149. package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
  150. package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
  151. package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
  152. package/plugins/specweave-github/agents/github-manager/AGENT.md +23 -0
  153. package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +25 -0
  154. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +25 -0
  155. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
  156. package/plugins/specweave-github/lib/github-spec-content-sync.js +49 -0
  157. package/plugins/specweave-github/lib/github-spec-content-sync.ts +67 -0
  158. package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
  159. package/plugins/specweave-infrastructure/agents/devops/AGENT.md +26 -0
  160. package/plugins/specweave-infrastructure/agents/network-engineer/AGENT.md +26 -0
  161. package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +26 -0
  162. package/plugins/specweave-infrastructure/agents/performance-engineer/AGENT.md +26 -0
  163. package/plugins/specweave-infrastructure/agents/sre/AGENT.md +26 -0
  164. package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
  165. package/plugins/specweave-jira/agents/jira-manager/AGENT.md +26 -0
  166. package/plugins/specweave-jira/commands/import-projects.js +183 -0
  167. package/plugins/specweave-jira/commands/import-projects.md +97 -0
  168. package/plugins/specweave-jira/commands/import-projects.ts +288 -0
  169. package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
  170. package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
  171. package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +26 -0
  172. package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +26 -0
  173. package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +26 -0
  174. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
  175. package/plugins/specweave-kubernetes/agents/kubernetes-architect/AGENT.md +26 -0
  176. package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
  177. package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
  178. package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
  179. package/plugins/specweave-ml/.claude-plugin/plugin.json +3 -3
  180. package/plugins/specweave-ml/agents/data-scientist/AGENT.md +26 -0
  181. package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +26 -0
  182. package/plugins/specweave-ml/agents/mlops-engineer/AGENT.md +26 -0
  183. package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +26 -0
  184. package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
  185. package/plugins/specweave-mobile/commands/build-config.md +256 -0
  186. package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
  187. package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
  188. package/plugins/specweave-payments/agents/payment-integration/AGENT.md +26 -0
  189. package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +20 -0
  190. package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
  191. package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
  192. package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
  193. package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
  194. package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
  195. package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
  196. package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
  197. package/plugins/specweave-release/agents/release-manager/AGENT.md +27 -0
  198. package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
  199. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
  200. package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
  201. package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
  202. package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
  203. package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
  204. package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
  205. package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
  206. package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
  207. package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
  208. package/plugins/specweave-testing/commands/test-coverage.md +979 -0
  209. package/plugins/specweave-testing/commands/test-generate.md +1156 -0
  210. package/plugins/specweave-testing/commands/test-init.md +409 -0
  211. package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
  212. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
  213. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
  214. package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
  215. package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
  216. package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
  217. package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
  218. package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
  219. package/plugins/specweave-ui/commands/ui-automate.md +199 -0
  220. package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
  221. package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
  222. package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
  223. package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
  224. package/plugins/specweave/commands/check-hooks.md +0 -257
  225. package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
  226. package/plugins/specweave/skills/plugin-expert/SKILL.md +0 -340
  227. /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
@@ -0,0 +1,1156 @@
1
+ # /specweave-testing:test-generate
2
+
3
+ Generate comprehensive unit, integration, and E2E tests from components, functions, and API endpoints.
4
+
5
+ You are an expert test generation engineer who creates thorough, production-ready test suites.
6
+
7
+ ## Your Task
8
+
9
+ Analyze code and automatically generate complete test coverage with unit tests, integration tests, and E2E tests.
10
+
11
+ ### 1. Test Generation Strategy
12
+
13
+ **Unit Tests**:
14
+ - Test individual functions/methods in isolation
15
+ - Mock all external dependencies
16
+ - Cover edge cases and error conditions
17
+ - Achieve 100% branch coverage
18
+ - Fast execution (<10ms per test)
19
+
20
+ **Integration Tests**:
21
+ - Test component interactions
22
+ - Use real dependencies where practical
23
+ - Test data flow between modules
24
+ - Verify API contracts
25
+ - Database integration testing
26
+
27
+ **E2E Tests**:
28
+ - Test complete user workflows
29
+ - Verify real browser behavior
30
+ - Test authentication flows
31
+ - Check responsive design
32
+ - Validate accessibility
33
+
34
+ ### 2. Unit Test Generation
35
+
36
+ **Function Analysis Example**:
37
+ ```typescript
38
+ // Source: src/utils/formatters.ts
39
+ export function formatCurrency(
40
+ amount: number,
41
+ currency: string = 'USD',
42
+ locale: string = 'en-US'
43
+ ): string {
44
+ if (typeof amount !== 'number' || isNaN(amount)) {
45
+ throw new Error('Invalid amount: must be a number');
46
+ }
47
+
48
+ if (amount < 0) {
49
+ throw new Error('Invalid amount: must be non-negative');
50
+ }
51
+
52
+ return new Intl.NumberFormat(locale, {
53
+ style: 'currency',
54
+ currency,
55
+ }).format(amount);
56
+ }
57
+ ```
58
+
59
+ **Generated Unit Test**:
60
+ ```typescript
61
+ // Generated: tests/unit/utils/formatters.test.ts
62
+ import { describe, it, expect } from 'vitest';
63
+ import { formatCurrency } from '@/utils/formatters';
64
+
65
+ describe('formatCurrency', () => {
66
+ describe('valid inputs', () => {
67
+ it('should format USD currency with default locale', () => {
68
+ expect(formatCurrency(1234.56)).toBe('$1,234.56');
69
+ });
70
+
71
+ it('should format EUR currency', () => {
72
+ expect(formatCurrency(1234.56, 'EUR', 'de-DE')).toBe('1.234,56 €');
73
+ });
74
+
75
+ it('should format GBP currency', () => {
76
+ expect(formatCurrency(1234.56, 'GBP', 'en-GB')).toBe('£1,234.56');
77
+ });
78
+
79
+ it('should handle zero amount', () => {
80
+ expect(formatCurrency(0)).toBe('$0.00');
81
+ });
82
+
83
+ it('should handle large amounts', () => {
84
+ expect(formatCurrency(1000000)).toBe('$1,000,000.00');
85
+ });
86
+
87
+ it('should handle small amounts', () => {
88
+ expect(formatCurrency(0.01)).toBe('$0.01');
89
+ });
90
+
91
+ it('should handle floating point precision', () => {
92
+ expect(formatCurrency(0.1 + 0.2)).toBe('$0.30');
93
+ });
94
+ });
95
+
96
+ describe('edge cases', () => {
97
+ it('should throw error for NaN', () => {
98
+ expect(() => formatCurrency(NaN)).toThrow('Invalid amount: must be a number');
99
+ });
100
+
101
+ it('should throw error for non-number input', () => {
102
+ expect(() => formatCurrency('123' as any)).toThrow('Invalid amount: must be a number');
103
+ });
104
+
105
+ it('should throw error for negative amounts', () => {
106
+ expect(() => formatCurrency(-100)).toThrow('Invalid amount: must be non-negative');
107
+ });
108
+
109
+ it('should throw error for undefined', () => {
110
+ expect(() => formatCurrency(undefined as any)).toThrow('Invalid amount: must be a number');
111
+ });
112
+
113
+ it('should throw error for null', () => {
114
+ expect(() => formatCurrency(null as any)).toThrow('Invalid amount: must be a number');
115
+ });
116
+ });
117
+
118
+ describe('locale variations', () => {
119
+ it('should use French locale', () => {
120
+ expect(formatCurrency(1234.56, 'EUR', 'fr-FR')).toContain('1');
121
+ });
122
+
123
+ it('should use Japanese locale', () => {
124
+ expect(formatCurrency(1234, 'JPY', 'ja-JP')).toContain('¥');
125
+ });
126
+ });
127
+ });
128
+ ```
129
+
130
+ ### 3. Component Test Generation
131
+
132
+ **React Component Analysis**:
133
+ ```typescript
134
+ // Source: src/components/Button.tsx
135
+ interface ButtonProps {
136
+ children: React.ReactNode;
137
+ onClick?: () => void;
138
+ variant?: 'primary' | 'secondary' | 'danger';
139
+ disabled?: boolean;
140
+ loading?: boolean;
141
+ icon?: React.ReactNode;
142
+ fullWidth?: boolean;
143
+ }
144
+
145
+ export function Button({
146
+ children,
147
+ onClick,
148
+ variant = 'primary',
149
+ disabled = false,
150
+ loading = false,
151
+ icon,
152
+ fullWidth = false,
153
+ }: ButtonProps) {
154
+ return (
155
+ <button
156
+ onClick={onClick}
157
+ disabled={disabled || loading}
158
+ className={`btn btn-${variant} ${fullWidth ? 'w-full' : ''}`}
159
+ data-testid="button"
160
+ >
161
+ {loading && <Spinner />}
162
+ {icon && <span className="icon">{icon}</span>}
163
+ {children}
164
+ </button>
165
+ );
166
+ }
167
+ ```
168
+
169
+ **Generated Component Test**:
170
+ ```typescript
171
+ // Generated: tests/unit/components/Button.test.tsx
172
+ import { describe, it, expect, vi } from 'vitest';
173
+ import { render, screen, fireEvent } from '@/tests/utils/test-utils';
174
+ import { Button } from '@/components/Button';
175
+
176
+ describe('Button', () => {
177
+ describe('rendering', () => {
178
+ it('should render button with text', () => {
179
+ render(<Button>Click me</Button>);
180
+ expect(screen.getByText('Click me')).toBeInTheDocument();
181
+ });
182
+
183
+ it('should render primary variant by default', () => {
184
+ render(<Button>Click me</Button>);
185
+ expect(screen.getByTestId('button')).toHaveClass('btn-primary');
186
+ });
187
+
188
+ it('should render secondary variant', () => {
189
+ render(<Button variant="secondary">Click me</Button>);
190
+ expect(screen.getByTestId('button')).toHaveClass('btn-secondary');
191
+ });
192
+
193
+ it('should render danger variant', () => {
194
+ render(<Button variant="danger">Delete</Button>);
195
+ expect(screen.getByTestId('button')).toHaveClass('btn-danger');
196
+ });
197
+
198
+ it('should render with icon', () => {
199
+ const icon = <span data-testid="custom-icon">📧</span>;
200
+ render(<Button icon={icon}>Send</Button>);
201
+ expect(screen.getByTestId('custom-icon')).toBeInTheDocument();
202
+ });
203
+
204
+ it('should render full width', () => {
205
+ render(<Button fullWidth>Click me</Button>);
206
+ expect(screen.getByTestId('button')).toHaveClass('w-full');
207
+ });
208
+ });
209
+
210
+ describe('interactions', () => {
211
+ it('should call onClick when clicked', () => {
212
+ const handleClick = vi.fn();
213
+ render(<Button onClick={handleClick}>Click me</Button>);
214
+
215
+ fireEvent.click(screen.getByTestId('button'));
216
+ expect(handleClick).toHaveBeenCalledTimes(1);
217
+ });
218
+
219
+ it('should not call onClick when disabled', () => {
220
+ const handleClick = vi.fn();
221
+ render(<Button onClick={handleClick} disabled>Click me</Button>);
222
+
223
+ fireEvent.click(screen.getByTestId('button'));
224
+ expect(handleClick).not.toHaveBeenCalled();
225
+ });
226
+
227
+ it('should not call onClick when loading', () => {
228
+ const handleClick = vi.fn();
229
+ render(<Button onClick={handleClick} loading>Click me</Button>);
230
+
231
+ fireEvent.click(screen.getByTestId('button'));
232
+ expect(handleClick).not.toHaveBeenCalled();
233
+ });
234
+ });
235
+
236
+ describe('states', () => {
237
+ it('should be disabled when disabled prop is true', () => {
238
+ render(<Button disabled>Click me</Button>);
239
+ expect(screen.getByTestId('button')).toBeDisabled();
240
+ });
241
+
242
+ it('should be disabled when loading', () => {
243
+ render(<Button loading>Click me</Button>);
244
+ expect(screen.getByTestId('button')).toBeDisabled();
245
+ });
246
+
247
+ it('should show spinner when loading', () => {
248
+ render(<Button loading>Click me</Button>);
249
+ expect(screen.getByTestId('spinner')).toBeInTheDocument();
250
+ });
251
+ });
252
+
253
+ describe('accessibility', () => {
254
+ it('should have button role', () => {
255
+ render(<Button>Click me</Button>);
256
+ expect(screen.getByRole('button')).toBeInTheDocument();
257
+ });
258
+
259
+ it('should be keyboard accessible', () => {
260
+ const handleClick = vi.fn();
261
+ render(<Button onClick={handleClick}>Click me</Button>);
262
+
263
+ const button = screen.getByTestId('button');
264
+ button.focus();
265
+ expect(button).toHaveFocus();
266
+ });
267
+ });
268
+ });
269
+ ```
270
+
271
+ ### 4. API Endpoint Test Generation
272
+
273
+ **API Route Analysis**:
274
+ ```typescript
275
+ // Source: src/api/users.ts
276
+ export async function GET(request: Request) {
277
+ const { searchParams } = new URL(request.url);
278
+ const page = parseInt(searchParams.get('page') || '1');
279
+ const limit = parseInt(searchParams.get('limit') || '10');
280
+
281
+ if (page < 1 || limit < 1 || limit > 100) {
282
+ return Response.json(
283
+ { error: 'Invalid pagination parameters' },
284
+ { status: 400 }
285
+ );
286
+ }
287
+
288
+ const users = await db.users.findMany({
289
+ skip: (page - 1) * limit,
290
+ take: limit,
291
+ });
292
+
293
+ return Response.json({ users, page, limit });
294
+ }
295
+
296
+ export async function POST(request: Request) {
297
+ const body = await request.json();
298
+
299
+ const { email, name, role } = body;
300
+
301
+ if (!email || !name) {
302
+ return Response.json(
303
+ { error: 'Email and name are required' },
304
+ { status: 400 }
305
+ );
306
+ }
307
+
308
+ const existingUser = await db.users.findUnique({ where: { email } });
309
+ if (existingUser) {
310
+ return Response.json(
311
+ { error: 'User already exists' },
312
+ { status: 409 }
313
+ );
314
+ }
315
+
316
+ const user = await db.users.create({
317
+ data: { email, name, role: role || 'user' },
318
+ });
319
+
320
+ return Response.json({ user }, { status: 201 });
321
+ }
322
+ ```
323
+
324
+ **Generated API Test**:
325
+ ```typescript
326
+ // Generated: tests/integration/api/users.test.ts
327
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
328
+ import { GET, POST } from '@/api/users';
329
+ import { db } from '@/lib/db';
330
+
331
+ // Mock database
332
+ vi.mock('@/lib/db', () => ({
333
+ db: {
334
+ users: {
335
+ findMany: vi.fn(),
336
+ findUnique: vi.fn(),
337
+ create: vi.fn(),
338
+ },
339
+ },
340
+ }));
341
+
342
+ describe('Users API', () => {
343
+ beforeEach(() => {
344
+ vi.clearAllMocks();
345
+ });
346
+
347
+ describe('GET /api/users', () => {
348
+ it('should return users with default pagination', async () => {
349
+ const mockUsers = [
350
+ { id: '1', email: 'user1@example.com', name: 'User 1' },
351
+ { id: '2', email: 'user2@example.com', name: 'User 2' },
352
+ ];
353
+
354
+ vi.mocked(db.users.findMany).mockResolvedValue(mockUsers);
355
+
356
+ const request = new Request('http://localhost/api/users');
357
+ const response = await GET(request);
358
+ const data = await response.json();
359
+
360
+ expect(response.status).toBe(200);
361
+ expect(data.users).toEqual(mockUsers);
362
+ expect(data.page).toBe(1);
363
+ expect(data.limit).toBe(10);
364
+ expect(db.users.findMany).toHaveBeenCalledWith({
365
+ skip: 0,
366
+ take: 10,
367
+ });
368
+ });
369
+
370
+ it('should handle custom pagination', async () => {
371
+ vi.mocked(db.users.findMany).mockResolvedValue([]);
372
+
373
+ const request = new Request('http://localhost/api/users?page=2&limit=20');
374
+ const response = await GET(request);
375
+ const data = await response.json();
376
+
377
+ expect(data.page).toBe(2);
378
+ expect(data.limit).toBe(20);
379
+ expect(db.users.findMany).toHaveBeenCalledWith({
380
+ skip: 20,
381
+ take: 20,
382
+ });
383
+ });
384
+
385
+ it('should reject invalid page number', async () => {
386
+ const request = new Request('http://localhost/api/users?page=0');
387
+ const response = await GET(request);
388
+ const data = await response.json();
389
+
390
+ expect(response.status).toBe(400);
391
+ expect(data.error).toBe('Invalid pagination parameters');
392
+ });
393
+
394
+ it('should reject limit over 100', async () => {
395
+ const request = new Request('http://localhost/api/users?limit=101');
396
+ const response = await GET(request);
397
+ const data = await response.json();
398
+
399
+ expect(response.status).toBe(400);
400
+ expect(data.error).toBe('Invalid pagination parameters');
401
+ });
402
+
403
+ it('should handle database errors', async () => {
404
+ vi.mocked(db.users.findMany).mockRejectedValue(new Error('DB error'));
405
+
406
+ const request = new Request('http://localhost/api/users');
407
+
408
+ await expect(GET(request)).rejects.toThrow('DB error');
409
+ });
410
+ });
411
+
412
+ describe('POST /api/users', () => {
413
+ it('should create new user', async () => {
414
+ const newUser = {
415
+ email: 'new@example.com',
416
+ name: 'New User',
417
+ role: 'user',
418
+ };
419
+
420
+ vi.mocked(db.users.findUnique).mockResolvedValue(null);
421
+ vi.mocked(db.users.create).mockResolvedValue({
422
+ id: '123',
423
+ ...newUser,
424
+ });
425
+
426
+ const request = new Request('http://localhost/api/users', {
427
+ method: 'POST',
428
+ body: JSON.stringify(newUser),
429
+ });
430
+
431
+ const response = await POST(request);
432
+ const data = await response.json();
433
+
434
+ expect(response.status).toBe(201);
435
+ expect(data.user).toMatchObject(newUser);
436
+ expect(db.users.create).toHaveBeenCalledWith({
437
+ data: newUser,
438
+ });
439
+ });
440
+
441
+ it('should default role to user', async () => {
442
+ vi.mocked(db.users.findUnique).mockResolvedValue(null);
443
+ vi.mocked(db.users.create).mockResolvedValue({
444
+ id: '123',
445
+ email: 'test@example.com',
446
+ name: 'Test',
447
+ role: 'user',
448
+ });
449
+
450
+ const request = new Request('http://localhost/api/users', {
451
+ method: 'POST',
452
+ body: JSON.stringify({ email: 'test@example.com', name: 'Test' }),
453
+ });
454
+
455
+ await POST(request);
456
+
457
+ expect(db.users.create).toHaveBeenCalledWith({
458
+ data: {
459
+ email: 'test@example.com',
460
+ name: 'Test',
461
+ role: 'user',
462
+ },
463
+ });
464
+ });
465
+
466
+ it('should reject missing email', async () => {
467
+ const request = new Request('http://localhost/api/users', {
468
+ method: 'POST',
469
+ body: JSON.stringify({ name: 'Test' }),
470
+ });
471
+
472
+ const response = await POST(request);
473
+ const data = await response.json();
474
+
475
+ expect(response.status).toBe(400);
476
+ expect(data.error).toBe('Email and name are required');
477
+ });
478
+
479
+ it('should reject missing name', async () => {
480
+ const request = new Request('http://localhost/api/users', {
481
+ method: 'POST',
482
+ body: JSON.stringify({ email: 'test@example.com' }),
483
+ });
484
+
485
+ const response = await POST(request);
486
+ const data = await response.json();
487
+
488
+ expect(response.status).toBe(400);
489
+ expect(data.error).toBe('Email and name are required');
490
+ });
491
+
492
+ it('should reject duplicate email', async () => {
493
+ vi.mocked(db.users.findUnique).mockResolvedValue({
494
+ id: '123',
495
+ email: 'existing@example.com',
496
+ name: 'Existing',
497
+ role: 'user',
498
+ });
499
+
500
+ const request = new Request('http://localhost/api/users', {
501
+ method: 'POST',
502
+ body: JSON.stringify({
503
+ email: 'existing@example.com',
504
+ name: 'Test',
505
+ }),
506
+ });
507
+
508
+ const response = await POST(request);
509
+ const data = await response.json();
510
+
511
+ expect(response.status).toBe(409);
512
+ expect(data.error).toBe('User already exists');
513
+ });
514
+ });
515
+ });
516
+ ```
517
+
518
+ ### 5. Custom Hook Test Generation
519
+
520
+ **Hook Analysis**:
521
+ ```typescript
522
+ // Source: src/hooks/useDebounce.ts
523
+ import { useState, useEffect } from 'react';
524
+
525
+ export function useDebounce<T>(value: T, delay: number): T {
526
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
527
+
528
+ useEffect(() => {
529
+ const handler = setTimeout(() => {
530
+ setDebouncedValue(value);
531
+ }, delay);
532
+
533
+ return () => {
534
+ clearTimeout(handler);
535
+ };
536
+ }, [value, delay]);
537
+
538
+ return debouncedValue;
539
+ }
540
+ ```
541
+
542
+ **Generated Hook Test**:
543
+ ```typescript
544
+ // Generated: tests/unit/hooks/useDebounce.test.ts
545
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
546
+ import { renderHook, waitFor } from '@testing-library/react';
547
+ import { useDebounce } from '@/hooks/useDebounce';
548
+
549
+ describe('useDebounce', () => {
550
+ beforeEach(() => {
551
+ vi.useFakeTimers();
552
+ });
553
+
554
+ afterEach(() => {
555
+ vi.restoreAllMocks();
556
+ });
557
+
558
+ it('should return initial value immediately', () => {
559
+ const { result } = renderHook(() => useDebounce('initial', 500));
560
+ expect(result.current).toBe('initial');
561
+ });
562
+
563
+ it('should debounce value changes', async () => {
564
+ const { result, rerender } = renderHook(
565
+ ({ value, delay }) => useDebounce(value, delay),
566
+ { initialProps: { value: 'initial', delay: 500 } }
567
+ );
568
+
569
+ expect(result.current).toBe('initial');
570
+
571
+ // Update value
572
+ rerender({ value: 'updated', delay: 500 });
573
+
574
+ // Value should not change immediately
575
+ expect(result.current).toBe('initial');
576
+
577
+ // Fast-forward time
578
+ vi.advanceTimersByTime(500);
579
+
580
+ // Value should update after delay
581
+ await waitFor(() => {
582
+ expect(result.current).toBe('updated');
583
+ });
584
+ });
585
+
586
+ it('should cancel previous timeout on rapid changes', async () => {
587
+ const { result, rerender } = renderHook(
588
+ ({ value }) => useDebounce(value, 500),
589
+ { initialProps: { value: 'initial' } }
590
+ );
591
+
592
+ // Rapid updates
593
+ rerender({ value: 'update1' });
594
+ vi.advanceTimersByTime(200);
595
+
596
+ rerender({ value: 'update2' });
597
+ vi.advanceTimersByTime(200);
598
+
599
+ rerender({ value: 'final' });
600
+ vi.advanceTimersByTime(500);
601
+
602
+ await waitFor(() => {
603
+ expect(result.current).toBe('final');
604
+ });
605
+ });
606
+
607
+ it('should handle delay changes', async () => {
608
+ const { result, rerender } = renderHook(
609
+ ({ value, delay }) => useDebounce(value, delay),
610
+ { initialProps: { value: 'initial', delay: 500 } }
611
+ );
612
+
613
+ rerender({ value: 'updated', delay: 1000 });
614
+ vi.advanceTimersByTime(500);
615
+
616
+ // Should not update yet (new delay is 1000ms)
617
+ expect(result.current).toBe('initial');
618
+
619
+ vi.advanceTimersByTime(500);
620
+
621
+ await waitFor(() => {
622
+ expect(result.current).toBe('updated');
623
+ });
624
+ });
625
+
626
+ it('should handle different value types', async () => {
627
+ // Number
628
+ const { result: numberResult } = renderHook(() => useDebounce(42, 100));
629
+ expect(numberResult.current).toBe(42);
630
+
631
+ // Object
632
+ const obj = { name: 'test' };
633
+ const { result: objResult } = renderHook(() => useDebounce(obj, 100));
634
+ expect(objResult.current).toBe(obj);
635
+
636
+ // Array
637
+ const arr = [1, 2, 3];
638
+ const { result: arrResult } = renderHook(() => useDebounce(arr, 100));
639
+ expect(arrResult.current).toBe(arr);
640
+ });
641
+ });
642
+ ```
643
+
644
+ ### 6. Store/State Management Test Generation
645
+
646
+ **Zustand Store Analysis**:
647
+ ```typescript
648
+ // Source: src/stores/useCartStore.ts
649
+ import { create } from 'zustand';
650
+
651
+ interface CartItem {
652
+ id: string;
653
+ name: string;
654
+ price: number;
655
+ quantity: number;
656
+ }
657
+
658
+ interface CartStore {
659
+ items: CartItem[];
660
+ addItem: (item: Omit<CartItem, 'quantity'>) => void;
661
+ removeItem: (id: string) => void;
662
+ updateQuantity: (id: string, quantity: number) => void;
663
+ clearCart: () => void;
664
+ total: () => number;
665
+ }
666
+
667
+ export const useCartStore = create<CartStore>((set, get) => ({
668
+ items: [],
669
+
670
+ addItem: (item) => {
671
+ set((state) => {
672
+ const existing = state.items.find((i) => i.id === item.id);
673
+ if (existing) {
674
+ return {
675
+ items: state.items.map((i) =>
676
+ i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
677
+ ),
678
+ };
679
+ }
680
+ return { items: [...state.items, { ...item, quantity: 1 }] };
681
+ });
682
+ },
683
+
684
+ removeItem: (id) => {
685
+ set((state) => ({
686
+ items: state.items.filter((i) => i.id !== id),
687
+ }));
688
+ },
689
+
690
+ updateQuantity: (id, quantity) => {
691
+ if (quantity <= 0) {
692
+ get().removeItem(id);
693
+ return;
694
+ }
695
+ set((state) => ({
696
+ items: state.items.map((i) =>
697
+ i.id === id ? { ...i, quantity } : i
698
+ ),
699
+ }));
700
+ },
701
+
702
+ clearCart: () => {
703
+ set({ items: [] });
704
+ },
705
+
706
+ total: () => {
707
+ return get().items.reduce((sum, item) => sum + item.price * item.quantity, 0);
708
+ },
709
+ }));
710
+ ```
711
+
712
+ **Generated Store Test**:
713
+ ```typescript
714
+ // Generated: tests/unit/stores/useCartStore.test.ts
715
+ import { describe, it, expect, beforeEach } from 'vitest';
716
+ import { renderHook, act } from '@testing-library/react';
717
+ import { useCartStore } from '@/stores/useCartStore';
718
+
719
+ describe('useCartStore', () => {
720
+ beforeEach(() => {
721
+ // Reset store before each test
722
+ const { result } = renderHook(() => useCartStore());
723
+ act(() => {
724
+ result.current.clearCart();
725
+ });
726
+ });
727
+
728
+ describe('addItem', () => {
729
+ it('should add new item to cart', () => {
730
+ const { result } = renderHook(() => useCartStore());
731
+
732
+ act(() => {
733
+ result.current.addItem({
734
+ id: '1',
735
+ name: 'Product A',
736
+ price: 29.99,
737
+ });
738
+ });
739
+
740
+ expect(result.current.items).toHaveLength(1);
741
+ expect(result.current.items[0]).toMatchObject({
742
+ id: '1',
743
+ name: 'Product A',
744
+ price: 29.99,
745
+ quantity: 1,
746
+ });
747
+ });
748
+
749
+ it('should increment quantity for existing item', () => {
750
+ const { result } = renderHook(() => useCartStore());
751
+
752
+ act(() => {
753
+ result.current.addItem({
754
+ id: '1',
755
+ name: 'Product A',
756
+ price: 29.99,
757
+ });
758
+ result.current.addItem({
759
+ id: '1',
760
+ name: 'Product A',
761
+ price: 29.99,
762
+ });
763
+ });
764
+
765
+ expect(result.current.items).toHaveLength(1);
766
+ expect(result.current.items[0].quantity).toBe(2);
767
+ });
768
+
769
+ it('should add multiple different items', () => {
770
+ const { result } = renderHook(() => useCartStore());
771
+
772
+ act(() => {
773
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
774
+ result.current.addItem({ id: '2', name: 'Product B', price: 39.99 });
775
+ });
776
+
777
+ expect(result.current.items).toHaveLength(2);
778
+ });
779
+ });
780
+
781
+ describe('removeItem', () => {
782
+ it('should remove item from cart', () => {
783
+ const { result } = renderHook(() => useCartStore());
784
+
785
+ act(() => {
786
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
787
+ result.current.removeItem('1');
788
+ });
789
+
790
+ expect(result.current.items).toHaveLength(0);
791
+ });
792
+
793
+ it('should not affect other items', () => {
794
+ const { result } = renderHook(() => useCartStore());
795
+
796
+ act(() => {
797
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
798
+ result.current.addItem({ id: '2', name: 'Product B', price: 39.99 });
799
+ result.current.removeItem('1');
800
+ });
801
+
802
+ expect(result.current.items).toHaveLength(1);
803
+ expect(result.current.items[0].id).toBe('2');
804
+ });
805
+
806
+ it('should handle removing non-existent item', () => {
807
+ const { result } = renderHook(() => useCartStore());
808
+
809
+ act(() => {
810
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
811
+ result.current.removeItem('999');
812
+ });
813
+
814
+ expect(result.current.items).toHaveLength(1);
815
+ });
816
+ });
817
+
818
+ describe('updateQuantity', () => {
819
+ it('should update item quantity', () => {
820
+ const { result } = renderHook(() => useCartStore());
821
+
822
+ act(() => {
823
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
824
+ result.current.updateQuantity('1', 5);
825
+ });
826
+
827
+ expect(result.current.items[0].quantity).toBe(5);
828
+ });
829
+
830
+ it('should remove item when quantity is 0', () => {
831
+ const { result } = renderHook(() => useCartStore());
832
+
833
+ act(() => {
834
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
835
+ result.current.updateQuantity('1', 0);
836
+ });
837
+
838
+ expect(result.current.items).toHaveLength(0);
839
+ });
840
+
841
+ it('should remove item when quantity is negative', () => {
842
+ const { result } = renderHook(() => useCartStore());
843
+
844
+ act(() => {
845
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
846
+ result.current.updateQuantity('1', -1);
847
+ });
848
+
849
+ expect(result.current.items).toHaveLength(0);
850
+ });
851
+ });
852
+
853
+ describe('clearCart', () => {
854
+ it('should remove all items', () => {
855
+ const { result } = renderHook(() => useCartStore());
856
+
857
+ act(() => {
858
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
859
+ result.current.addItem({ id: '2', name: 'Product B', price: 39.99 });
860
+ result.current.clearCart();
861
+ });
862
+
863
+ expect(result.current.items).toHaveLength(0);
864
+ });
865
+ });
866
+
867
+ describe('total', () => {
868
+ it('should calculate total for single item', () => {
869
+ const { result } = renderHook(() => useCartStore());
870
+
871
+ act(() => {
872
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
873
+ });
874
+
875
+ expect(result.current.total()).toBe(29.99);
876
+ });
877
+
878
+ it('should calculate total for multiple items', () => {
879
+ const { result } = renderHook(() => useCartStore());
880
+
881
+ act(() => {
882
+ result.current.addItem({ id: '1', name: 'Product A', price: 29.99 });
883
+ result.current.addItem({ id: '2', name: 'Product B', price: 39.99 });
884
+ });
885
+
886
+ expect(result.current.total()).toBe(69.98);
887
+ });
888
+
889
+ it('should calculate total with quantities', () => {
890
+ const { result } = renderHook(() => useCartStore());
891
+
892
+ act(() => {
893
+ result.current.addItem({ id: '1', name: 'Product A', price: 10 });
894
+ result.current.updateQuantity('1', 3);
895
+ });
896
+
897
+ expect(result.current.total()).toBe(30);
898
+ });
899
+
900
+ it('should return 0 for empty cart', () => {
901
+ const { result } = renderHook(() => useCartStore());
902
+ expect(result.current.total()).toBe(0);
903
+ });
904
+ });
905
+ });
906
+ ```
907
+
908
+ ### 7. Test Generation CLI
909
+
910
+ **Test Generator Script**:
911
+ ```typescript
912
+ // scripts/generate-tests.ts
913
+ import fs from 'fs';
914
+ import path from 'path';
915
+ import { parse } from '@babel/parser';
916
+ import traverse from '@babel/traverse';
917
+
918
+ interface TestConfig {
919
+ sourceFile: string;
920
+ testType: 'unit' | 'integration' | 'e2e';
921
+ outputFile?: string;
922
+ }
923
+
924
+ export async function generateTests(config: TestConfig) {
925
+ const code = fs.readFileSync(config.sourceFile, 'utf-8');
926
+ const ast = parse(code, {
927
+ sourceType: 'module',
928
+ plugins: ['typescript', 'jsx'],
929
+ });
930
+
931
+ const tests: string[] = [];
932
+
933
+ traverse(ast, {
934
+ FunctionDeclaration(path) {
935
+ const name = path.node.id?.name;
936
+ if (name) {
937
+ tests.push(generateFunctionTest(name, path.node));
938
+ }
939
+ },
940
+ ExportNamedDeclaration(path) {
941
+ if (path.node.declaration?.type === 'FunctionDeclaration') {
942
+ const name = path.node.declaration.id?.name;
943
+ if (name) {
944
+ tests.push(generateFunctionTest(name, path.node.declaration));
945
+ }
946
+ }
947
+ },
948
+ });
949
+
950
+ const outputPath = config.outputFile || generateOutputPath(config.sourceFile);
951
+ const testContent = generateTestFile(tests, config.sourceFile);
952
+
953
+ fs.writeFileSync(outputPath, testContent, 'utf-8');
954
+ console.log(`✓ Generated tests: ${outputPath}`);
955
+ }
956
+
957
+ function generateOutputPath(sourceFile: string): string {
958
+ const relativePath = path.relative('src', sourceFile);
959
+ return path.join('tests', 'unit', relativePath.replace(/\.ts$/, '.test.ts'));
960
+ }
961
+
962
+ function generateFunctionTest(name: string, node: any): string {
963
+ return `
964
+ describe('${name}', () => {
965
+ it('should work correctly', () => {
966
+ // TODO: Implement test
967
+ expect(${name}).toBeDefined();
968
+ });
969
+ });
970
+ `;
971
+ }
972
+
973
+ function generateTestFile(tests: string[], sourceFile: string): string {
974
+ const importPath = path.relative(
975
+ path.dirname(generateOutputPath(sourceFile)),
976
+ sourceFile
977
+ ).replace(/\.ts$/, '');
978
+
979
+ return `
980
+ import { describe, it, expect } from 'vitest';
981
+ import { } from '${importPath}';
982
+
983
+ describe('${path.basename(sourceFile)}', () => {
984
+ ${tests.join('\n')}
985
+ });
986
+ `.trim();
987
+ }
988
+ ```
989
+
990
+ ### 8. Test Templates
991
+
992
+ **Template Registry** (`tests/templates/`):
993
+ ```typescript
994
+ // tests/templates/component.template.ts
995
+ export const componentTestTemplate = `
996
+ import { describe, it, expect, vi } from 'vitest';
997
+ import { render, screen } from '@/tests/utils/test-utils';
998
+ import { {{ComponentName}} } from '@/components/{{ComponentName}}';
999
+
1000
+ describe('{{ComponentName}}', () => {
1001
+ it('should render correctly', () => {
1002
+ render(<{{ComponentName}} />);
1003
+ expect(screen.getByTestId('{{testId}}')).toBeInTheDocument();
1004
+ });
1005
+
1006
+ it('should handle user interactions', () => {
1007
+ const handleClick = vi.fn();
1008
+ render(<{{ComponentName}} onClick={handleClick} />);
1009
+
1010
+ fireEvent.click(screen.getByTestId('{{testId}}'));
1011
+ expect(handleClick).toHaveBeenCalled();
1012
+ });
1013
+ });
1014
+ `;
1015
+
1016
+ // tests/templates/api.template.ts
1017
+ export const apiTestTemplate = `
1018
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
1019
+ import { {{functionName}} } from '@/api/{{fileName}}';
1020
+
1021
+ describe('{{functionName}}', () => {
1022
+ beforeEach(() => {
1023
+ vi.clearAllMocks();
1024
+ });
1025
+
1026
+ it('should return success response', async () => {
1027
+ const request = new Request('http://localhost/api/{{endpoint}}');
1028
+ const response = await {{functionName}}(request);
1029
+
1030
+ expect(response.status).toBe(200);
1031
+ });
1032
+
1033
+ it('should handle errors', async () => {
1034
+ // TODO: Implement error case
1035
+ });
1036
+ });
1037
+ `;
1038
+ ```
1039
+
1040
+ ### 9. Coverage Gaps Detection
1041
+
1042
+ **Coverage Analyzer**:
1043
+ ```typescript
1044
+ // scripts/analyze-coverage-gaps.ts
1045
+ import fs from 'fs';
1046
+ import path from 'path';
1047
+
1048
+ interface CoverageSummary {
1049
+ total: {
1050
+ lines: { pct: number };
1051
+ statements: { pct: number };
1052
+ functions: { pct: number };
1053
+ branches: { pct: number };
1054
+ };
1055
+ }
1056
+
1057
+ export function analyzeCoverageGaps(coverageFile: string) {
1058
+ const coverage: CoverageSummary = JSON.parse(
1059
+ fs.readFileSync(coverageFile, 'utf-8')
1060
+ );
1061
+
1062
+ const gaps = [];
1063
+
1064
+ Object.entries(coverage).forEach(([file, data]) => {
1065
+ if (file === 'total') return;
1066
+
1067
+ const { lines, functions, branches } = data as any;
1068
+
1069
+ if (lines.pct < 80) {
1070
+ gaps.push({
1071
+ file,
1072
+ type: 'lines',
1073
+ current: lines.pct,
1074
+ target: 80,
1075
+ gap: 80 - lines.pct,
1076
+ });
1077
+ }
1078
+
1079
+ if (functions.pct < 80) {
1080
+ gaps.push({
1081
+ file,
1082
+ type: 'functions',
1083
+ current: functions.pct,
1084
+ target: 80,
1085
+ gap: 80 - functions.pct,
1086
+ });
1087
+ }
1088
+
1089
+ if (branches.pct < 80) {
1090
+ gaps.push({
1091
+ file,
1092
+ type: 'branches',
1093
+ current: branches.pct,
1094
+ target: 80,
1095
+ gap: 80 - branches.pct,
1096
+ });
1097
+ }
1098
+ });
1099
+
1100
+ return gaps.sort((a, b) => b.gap - a.gap);
1101
+ }
1102
+
1103
+ // Generate test stubs for gaps
1104
+ export function generateGapTests(gaps: any[]) {
1105
+ gaps.slice(0, 10).forEach((gap) => {
1106
+ console.log(`
1107
+ Priority: ${gap.file}
1108
+ Coverage: ${gap.current}% ${gap.type}
1109
+ Gap: ${gap.gap}%
1110
+
1111
+ Suggested tests:
1112
+ - Add ${gap.type} coverage for uncovered paths
1113
+ - Focus on edge cases and error handling
1114
+ `);
1115
+ });
1116
+ }
1117
+ ```
1118
+
1119
+ ### 10. Package Scripts
1120
+
1121
+ ```json
1122
+ {
1123
+ "scripts": {
1124
+ "test:generate": "tsx scripts/generate-tests.ts",
1125
+ "test:generate:component": "tsx scripts/generate-tests.ts --type component",
1126
+ "test:generate:api": "tsx scripts/generate-tests.ts --type api",
1127
+ "test:coverage:gaps": "tsx scripts/analyze-coverage-gaps.ts coverage/coverage-summary.json",
1128
+ "test:stub": "tsx scripts/generate-test-stubs.ts"
1129
+ }
1130
+ }
1131
+ ```
1132
+
1133
+ ## Workflow
1134
+
1135
+ 1. Ask about code to generate tests for (file path or pattern)
1136
+ 2. Analyze code structure and identify test targets
1137
+ 3. Determine appropriate test types (unit/integration/E2E)
1138
+ 4. Generate test scaffolding with proper imports
1139
+ 5. Create test cases for happy paths
1140
+ 6. Add edge case and error handling tests
1141
+ 7. Generate mocks and fixtures
1142
+ 8. Add accessibility and performance tests (if applicable)
1143
+ 9. Run tests to verify they work
1144
+ 10. Analyze coverage and suggest improvements
1145
+
1146
+ ## When to Use
1147
+
1148
+ - Adding tests to existing code
1149
+ - Achieving test coverage targets
1150
+ - Creating test stubs for new features
1151
+ - Migrating to new testing framework
1152
+ - Identifying coverage gaps
1153
+ - Implementing TDD workflow
1154
+ - Standardizing test patterns
1155
+
1156
+ Generate comprehensive test coverage automatically!