@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,306 @@
1
+ ---
2
+ name: jest-unit
3
+ description: |
4
+ Jest unit testing patterns for this Next.js application.
5
+ Covers test structure, mocking strategies, React component testing, hook testing, and coverage targets.
6
+ Use this skill when writing unit tests for services, hooks, utilities, or components.
7
+ allowed-tools: Read, Glob, Grep
8
+ version: 1.1.0
9
+ ---
10
+
11
+ # Jest Unit Testing Skill
12
+
13
+ Patterns for writing effective unit tests with Jest and React Testing Library.
14
+
15
+ ## Architecture Overview
16
+
17
+ ```
18
+ TEST FILE STRUCTURE:
19
+
20
+ core/tests/jest/
21
+ ├── api/ # API route tests
22
+ ├── hooks/ # React hooks tests
23
+ ├── lib/ # Utility tests
24
+ ├── components/ # Component tests
25
+ ├── services/ # Service tests
26
+ ├── __mocks__/ # Mock utilities
27
+ │ ├── db-mocks.ts
28
+ │ ├── better-auth.js
29
+ │ └── next-server.js
30
+ └── setup.ts # Global configuration
31
+
32
+ contents/themes/default/tests/jest/
33
+ └── ... # Theme-specific tests
34
+
35
+ contents/plugins/*/__tests__/
36
+ └── ... # Plugin-specific tests
37
+ ```
38
+
39
+ ## When to Use This Skill
40
+
41
+ - Writing unit tests for services
42
+ - Testing React hooks
43
+ - Testing React components
44
+ - Mocking database operations
45
+ - Setting up test coverage
46
+
47
+ ---
48
+
49
+ ## Test File Structure
50
+
51
+ ### Naming Conventions
52
+
53
+ ```
54
+ *.test.ts - TypeScript unit tests
55
+ *.test.tsx - React component tests
56
+ ```
57
+
58
+ ### Standard Test Structure
59
+
60
+ ```typescript
61
+ import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals'
62
+ import { functionToTest } from '@/core/lib/module'
63
+
64
+ describe('ModuleName', () => {
65
+ beforeEach(() => {
66
+ jest.clearAllMocks()
67
+ })
68
+
69
+ afterEach(() => {
70
+ jest.clearAllMocks()
71
+ jest.resetModules()
72
+ })
73
+
74
+ describe('Feature Group', () => {
75
+ test('should do expected behavior when condition met', () => {
76
+ // Arrange
77
+ const input = 'test-data'
78
+
79
+ // Act
80
+ const result = functionToTest(input)
81
+
82
+ // Assert
83
+ expect(result).toBe('expected-output')
84
+ })
85
+ })
86
+ })
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Jest Configuration
92
+
93
+ ```typescript
94
+ // jest.config.ts
95
+ import type { Config } from 'jest'
96
+
97
+ export const baseConfig: Partial<Config> = {
98
+ preset: 'ts-jest',
99
+ testEnvironment: 'jsdom',
100
+
101
+ moduleNameMapper: {
102
+ '^@/(.*)$': '<rootDir>/$1',
103
+ '^@/core/(.*)$': '<rootDir>/core/$1',
104
+ 'next/server': '<rootDir>/core/tests/jest/__mocks__/next-server.js',
105
+ },
106
+
107
+ setupFilesAfterEnv: ['<rootDir>/core/tests/setup.ts'],
108
+ testTimeout: 10000,
109
+ }
110
+ ```
111
+
112
+ ### Test Scripts
113
+
114
+ ```bash
115
+ pnpm test # Run all core tests
116
+ pnpm test:theme # Run theme-specific tests
117
+ pnpm test:coverage # Generate coverage reports
118
+ pnpm test:watch # Watch mode
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Essential Mocking (Quick Reference)
124
+
125
+ ### Database (MANDATORY)
126
+
127
+ ```typescript
128
+ jest.mock('@/core/lib/db', () => ({
129
+ queryWithRLS: jest.fn(),
130
+ queryOneWithRLS: jest.fn(),
131
+ mutateWithRLS: jest.fn(),
132
+ }))
133
+
134
+ const mockQueryWithRLS = queryWithRLS as jest.MockedFunction<typeof queryWithRLS>
135
+
136
+ test('example', async () => {
137
+ mockQueryWithRLS.mockResolvedValue([{ id: '123' }])
138
+ // ...
139
+ })
140
+ ```
141
+
142
+ ### Next.js Navigation
143
+
144
+ ```typescript
145
+ jest.mock('next/navigation', () => ({
146
+ useRouter: () => ({ push: jest.fn(), replace: jest.fn() }),
147
+ usePathname: () => '/',
148
+ useSearchParams: () => new URLSearchParams(),
149
+ }))
150
+ ```
151
+
152
+ ### Translations
153
+
154
+ ```typescript
155
+ jest.mock('next-intl', () => ({
156
+ useTranslations: () => (key: string) => key,
157
+ useLocale: () => 'en',
158
+ }))
159
+ ```
160
+
161
+ → See `references/mocking-patterns.md` for complete mocking strategies
162
+
163
+ ---
164
+
165
+ ## Quick Testing Patterns
166
+
167
+ ### Component Test
168
+
169
+ ```typescript
170
+ import { render, screen } from '@testing-library/react'
171
+ import userEvent from '@testing-library/user-event'
172
+
173
+ test('form submission', async () => {
174
+ const user = userEvent.setup()
175
+ render(<MyForm />)
176
+
177
+ await user.type(screen.getByLabelText('Email'), 'test@example.com')
178
+ await user.click(screen.getByRole('button', { name: /submit/i }))
179
+
180
+ expect(await screen.findByText('Success')).toBeInTheDocument()
181
+ })
182
+ ```
183
+
184
+ → See `references/component-testing.md` for complete patterns
185
+
186
+ ### Hook Test
187
+
188
+ ```typescript
189
+ import { renderHook, act } from '@testing-library/react'
190
+
191
+ test('hook state update', () => {
192
+ const { result } = renderHook(() => useCounter())
193
+
194
+ act(() => {
195
+ result.current.increment()
196
+ })
197
+
198
+ expect(result.current.count).toBe(1)
199
+ })
200
+ ```
201
+
202
+ → See `references/service-hook-testing.md` for complete patterns
203
+
204
+ ---
205
+
206
+ ## Coverage Targets
207
+
208
+ | Category | Target | Notes |
209
+ |----------|--------|-------|
210
+ | **Critical Paths** | 90%+ | Auth, API, Validation |
211
+ | **Features** | 80%+ | UI, Business Logic |
212
+ | **Utilities** | 80%+ | Helpers, Services |
213
+ | **Components** | 70%+ | UI Components |
214
+
215
+ ---
216
+
217
+ ## Common Assertions
218
+
219
+ ```typescript
220
+ // Equality
221
+ expect(result).toBe('expected')
222
+ expect(result).toEqual({ id: '123' })
223
+
224
+ // Truthiness
225
+ expect(result).toBeTruthy()
226
+ expect(result).toBeNull()
227
+ expect(result).toBeDefined()
228
+
229
+ // Collections
230
+ expect(array).toHaveLength(2)
231
+ expect(array).toContain('item')
232
+ expect(obj).toHaveProperty('key')
233
+
234
+ // DOM (Testing Library)
235
+ expect(element).toBeInTheDocument()
236
+ expect(element).toHaveClass('className')
237
+ expect(element).toBeVisible()
238
+ expect(element).toBeDisabled()
239
+
240
+ // Mocks
241
+ expect(mockFn).toHaveBeenCalled()
242
+ expect(mockFn).toHaveBeenCalledWith('arg')
243
+ expect(mockFn).toHaveBeenCalledTimes(1)
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Anti-Patterns
249
+
250
+ ```typescript
251
+ // ❌ NEVER: Test implementation details
252
+ expect(result.current._internalState).toBe('value')
253
+
254
+ // ✅ CORRECT: Test observable behavior
255
+ expect(result.current.displayValue).toBe('value')
256
+
257
+ // ❌ NEVER: Skip cleanup
258
+ // No afterEach → leaks between tests
259
+
260
+ // ✅ CORRECT: Always cleanup
261
+ afterEach(() => jest.clearAllMocks())
262
+
263
+ // ❌ NEVER: Async without await
264
+ fetchData().then(data => expect(data).toBeDefined())
265
+
266
+ // ✅ CORRECT: Properly await
267
+ const data = await fetchData()
268
+ expect(data).toBeDefined()
269
+
270
+ // ❌ NEVER: Use fireEvent for user actions
271
+ fireEvent.change(input, { target: { value: 'text' } })
272
+
273
+ // ✅ CORRECT: Use userEvent
274
+ await user.type(input, 'text')
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Checklist
280
+
281
+ Before finalizing unit tests:
282
+
283
+ - [ ] All database calls mocked (queryWithRLS, mutateWithRLS)
284
+ - [ ] External APIs mocked (fetch)
285
+ - [ ] Next.js functions mocked (useRouter, useSearchParams)
286
+ - [ ] afterEach cleanup with jest.clearAllMocks()
287
+ - [ ] Async tests properly awaited
288
+ - [ ] Test both success and error paths
289
+ - [ ] Edge cases covered (null, empty, invalid input)
290
+ - [ ] Coverage targets met (90%+ critical, 80%+ features)
291
+ - [ ] Test names are descriptive
292
+
293
+ ---
294
+
295
+ ## References
296
+
297
+ - `references/mocking-patterns.md` - Complete mocking strategies
298
+ - `references/component-testing.md` - React component testing patterns
299
+ - `references/service-hook-testing.md` - Service and hook testing patterns
300
+
301
+ ## Related Skills
302
+
303
+ - `cypress-e2e` - Integration/E2E testing
304
+ - `cypress-api` - API testing with Cypress
305
+ - `zod-validation` - Schema validation testing
306
+ - `service-layer` - Service patterns to test
@@ -0,0 +1,371 @@
1
+ # Component Testing Reference
2
+
3
+ React component testing patterns with Testing Library and Jest.
4
+
5
+ ## Basic Component Test Structure
6
+
7
+ ```typescript
8
+ import { describe, test, expect, beforeEach, jest } from '@jest/globals'
9
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react'
10
+ import userEvent from '@testing-library/user-event'
11
+ import { LoginForm } from '@/core/components/auth/forms/LoginForm'
12
+
13
+ describe('LoginForm Component', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks()
16
+ })
17
+
18
+ describe('Rendering', () => {
19
+ test('should render login form with all essential elements', () => {
20
+ render(<LoginForm />)
21
+
22
+ expect(screen.getByText('Sign In')).toBeInTheDocument()
23
+ expect(screen.getByLabelText('Email')).toBeInTheDocument()
24
+ expect(screen.getByLabelText('Password')).toBeInTheDocument()
25
+ expect(screen.getByRole('button', { name: /sign in/i })).toBeInTheDocument()
26
+ })
27
+ })
28
+
29
+ describe('User Interactions', () => {
30
+ test('should call signIn with correct credentials', async () => {
31
+ const user = userEvent.setup()
32
+
33
+ render(<LoginForm />)
34
+
35
+ await user.type(screen.getByLabelText('Email'), 'test@example.com')
36
+ await user.type(screen.getByLabelText('Password'), 'password123')
37
+ await user.click(screen.getByRole('button', { name: /sign in/i }))
38
+
39
+ await waitFor(() => {
40
+ expect(mockSignIn).toHaveBeenCalledWith({
41
+ email: 'test@example.com',
42
+ password: 'password123'
43
+ })
44
+ })
45
+ })
46
+ })
47
+
48
+ describe('Error Handling', () => {
49
+ test('should display error message on failed login', async () => {
50
+ const user = userEvent.setup()
51
+ mockSignIn.mockRejectedValue(new Error('Invalid credentials'))
52
+
53
+ render(<LoginForm />)
54
+
55
+ await user.type(screen.getByLabelText('Email'), 'test@example.com')
56
+ await user.type(screen.getByLabelText('Password'), 'wrong')
57
+ await user.click(screen.getByRole('button', { name: /sign in/i }))
58
+
59
+ await waitFor(() => {
60
+ expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument()
61
+ })
62
+ })
63
+ })
64
+
65
+ describe('Accessibility', () => {
66
+ test('should have proper accessibility attributes', () => {
67
+ render(<LoginForm />)
68
+
69
+ const emailInput = screen.getByLabelText('Email')
70
+ expect(emailInput).toHaveAttribute('type', 'email')
71
+ expect(emailInput).toHaveAttribute('aria-required', 'true')
72
+ })
73
+ })
74
+ })
75
+ ```
76
+
77
+ ---
78
+
79
+ ## userEvent vs fireEvent
80
+
81
+ ### userEvent (Preferred)
82
+
83
+ ```typescript
84
+ import userEvent from '@testing-library/user-event'
85
+
86
+ // ✅ CORRECT - Realistic user interactions
87
+ test('user types in input field', async () => {
88
+ const user = userEvent.setup()
89
+
90
+ render(<MyComponent />)
91
+
92
+ await user.type(screen.getByRole('textbox'), 'Hello')
93
+ await user.click(screen.getByRole('button'))
94
+ await user.keyboard('{Enter}')
95
+ await user.tab()
96
+
97
+ expect(screen.getByText('Hello')).toBeInTheDocument()
98
+ })
99
+ ```
100
+
101
+ ### fireEvent (Only for Edge Cases)
102
+
103
+ ```typescript
104
+ // Use fireEvent only when userEvent doesn't work
105
+ // (e.g., custom events, scroll, resize)
106
+ test('handles scroll event', () => {
107
+ render(<ScrollComponent />)
108
+
109
+ const container = screen.getByTestId('scroll-container')
110
+ fireEvent.scroll(container, { target: { scrollY: 100 } })
111
+
112
+ expect(screen.getByText('Scrolled')).toBeInTheDocument()
113
+ })
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Common userEvent Actions
119
+
120
+ ```typescript
121
+ const user = userEvent.setup()
122
+
123
+ // Typing
124
+ await user.type(input, 'text') // Type text
125
+ await user.clear(input) // Clear input
126
+ await user.type(input, 'text{Enter}') // Type and submit
127
+
128
+ // Clicking
129
+ await user.click(element) // Single click
130
+ await user.dblClick(element) // Double click
131
+ await user.tripleClick(element) // Triple click (select all)
132
+
133
+ // Keyboard
134
+ await user.keyboard('{Enter}') // Press Enter
135
+ await user.keyboard('{Tab}') // Press Tab
136
+ await user.keyboard('{Escape}') // Press Escape
137
+ await user.keyboard('{ArrowDown}') // Arrow keys
138
+ await user.keyboard('{Shift>}A{/Shift}') // Shift+A
139
+
140
+ // Selection
141
+ await user.selectOptions(select, 'option-value')
142
+ await user.selectOptions(select, ['opt1', 'opt2']) // Multi-select
143
+
144
+ // Hover
145
+ await user.hover(element)
146
+ await user.unhover(element)
147
+
148
+ // Clipboard
149
+ await user.copy()
150
+ await user.paste()
151
+ await user.cut()
152
+
153
+ // Focus
154
+ await user.tab() // Move focus forward
155
+ await user.tab({ shift: true }) // Move focus backward
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Query Priority
161
+
162
+ Testing Library recommends queries in this priority order:
163
+
164
+ ### 1. Accessible to Everyone (Preferred)
165
+
166
+ ```typescript
167
+ // By Role (best)
168
+ screen.getByRole('button', { name: /submit/i })
169
+ screen.getByRole('textbox', { name: /email/i })
170
+ screen.getByRole('heading', { level: 1 })
171
+
172
+ // By Label Text
173
+ screen.getByLabelText('Email')
174
+
175
+ // By Placeholder Text
176
+ screen.getByPlaceholderText('Enter email...')
177
+
178
+ // By Text Content
179
+ screen.getByText('Submit')
180
+ screen.getByText(/submit/i) // Case insensitive
181
+ ```
182
+
183
+ ### 2. Semantic Queries
184
+
185
+ ```typescript
186
+ screen.getByAltText('Profile photo')
187
+ screen.getByTitle('Close dialog')
188
+ screen.getByDisplayValue('current value')
189
+ ```
190
+
191
+ ### 3. Test IDs (Last Resort)
192
+
193
+ ```typescript
194
+ // Only when other queries don't work
195
+ screen.getByTestId('complex-component')
196
+ screen.getByTestId('data-cy-selector')
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Async Testing Patterns
202
+
203
+ ### waitFor
204
+
205
+ ```typescript
206
+ test('should show loading then data', async () => {
207
+ render(<DataComponent />)
208
+
209
+ // Initially loading
210
+ expect(screen.getByText('Loading...')).toBeInTheDocument()
211
+
212
+ // Wait for data
213
+ await waitFor(() => {
214
+ expect(screen.getByText('Data loaded')).toBeInTheDocument()
215
+ }, { timeout: 3000 })
216
+ })
217
+ ```
218
+
219
+ ### findBy (Combines getBy + waitFor)
220
+
221
+ ```typescript
222
+ test('should load async content', async () => {
223
+ render(<AsyncComponent />)
224
+
225
+ // findBy waits for element to appear
226
+ const element = await screen.findByText('Loaded content', {}, { timeout: 5000 })
227
+ expect(element).toBeInTheDocument()
228
+ })
229
+ ```
230
+
231
+ ### waitForElementToBeRemoved
232
+
233
+ ```typescript
234
+ test('should hide loading spinner', async () => {
235
+ render(<DataComponent />)
236
+
237
+ await waitForElementToBeRemoved(() => screen.queryByText('Loading...'))
238
+
239
+ expect(screen.getByText('Data')).toBeInTheDocument()
240
+ })
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Form Testing
246
+
247
+ ### Basic Form
248
+
249
+ ```typescript
250
+ test('submits form with correct data', async () => {
251
+ const onSubmit = jest.fn()
252
+ const user = userEvent.setup()
253
+
254
+ render(<ContactForm onSubmit={onSubmit} />)
255
+
256
+ await user.type(screen.getByLabelText('Name'), 'John Doe')
257
+ await user.type(screen.getByLabelText('Email'), 'john@example.com')
258
+ await user.type(screen.getByLabelText('Message'), 'Hello!')
259
+ await user.click(screen.getByRole('button', { name: /submit/i }))
260
+
261
+ expect(onSubmit).toHaveBeenCalledWith({
262
+ name: 'John Doe',
263
+ email: 'john@example.com',
264
+ message: 'Hello!'
265
+ })
266
+ })
267
+ ```
268
+
269
+ ### Form Validation
270
+
271
+ ```typescript
272
+ test('shows validation errors', async () => {
273
+ const user = userEvent.setup()
274
+
275
+ render(<ContactForm />)
276
+
277
+ // Submit empty form
278
+ await user.click(screen.getByRole('button', { name: /submit/i }))
279
+
280
+ // Check for validation errors
281
+ expect(await screen.findByText('Name is required')).toBeInTheDocument()
282
+ expect(await screen.findByText('Email is required')).toBeInTheDocument()
283
+ })
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Testing with Context/Providers
289
+
290
+ ```typescript
291
+ const renderWithProviders = (ui: React.ReactElement, options = {}) => {
292
+ const AllProviders = ({ children }: { children: React.ReactNode }) => (
293
+ <QueryClientProvider client={queryClient}>
294
+ <ThemeProvider>
295
+ <AuthProvider>
296
+ {children}
297
+ </AuthProvider>
298
+ </ThemeProvider>
299
+ </QueryClientProvider>
300
+ )
301
+
302
+ return render(ui, { wrapper: AllProviders, ...options })
303
+ }
304
+
305
+ // Usage
306
+ test('renders with all providers', () => {
307
+ renderWithProviders(<MyComponent />)
308
+ // ...
309
+ })
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Testing Modals/Dialogs
315
+
316
+ ```typescript
317
+ test('opens and closes modal', async () => {
318
+ const user = userEvent.setup()
319
+
320
+ render(<ModalComponent />)
321
+
322
+ // Open modal
323
+ await user.click(screen.getByRole('button', { name: /open/i }))
324
+
325
+ // Check modal is visible
326
+ expect(screen.getByRole('dialog')).toBeInTheDocument()
327
+
328
+ // Close modal
329
+ await user.click(screen.getByRole('button', { name: /close/i }))
330
+
331
+ // Check modal is gone
332
+ await waitFor(() => {
333
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
334
+ })
335
+ })
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Common Assertions (DOM)
341
+
342
+ ```typescript
343
+ // Presence
344
+ expect(element).toBeInTheDocument()
345
+ expect(element).not.toBeInTheDocument()
346
+
347
+ // Visibility
348
+ expect(element).toBeVisible()
349
+ expect(element).not.toBeVisible()
350
+
351
+ // State
352
+ expect(element).toBeDisabled()
353
+ expect(element).toBeEnabled()
354
+ expect(element).toBeChecked()
355
+ expect(element).toHaveFocus()
356
+
357
+ // Content
358
+ expect(element).toHaveTextContent('text')
359
+ expect(element).toHaveTextContent(/regex/i)
360
+ expect(element).toBeEmpty()
361
+
362
+ // Attributes
363
+ expect(element).toHaveAttribute('href', '/path')
364
+ expect(element).toHaveClass('active')
365
+ expect(element).toHaveStyle({ color: 'red' })
366
+
367
+ // Form
368
+ expect(input).toHaveValue('value')
369
+ expect(input).toHaveDisplayValue('displayed')
370
+ expect(select).toHaveValue('option-value')
371
+ ```