@pageai/ralph-loop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/PROMPT.md +58 -0
- package/.agent/STEERING.md +3 -0
- package/.agent/logs/LOG.md +13 -0
- package/.agent/prd/.gitkeep +0 -0
- package/.agent/screenshots/.gitkeep +0 -0
- package/.agent/skills/component-refactoring/SKILL.md +247 -0
- package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
- package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
- package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
- package/.agent/skills/e2e-tester/SKILL.md +595 -0
- package/.agent/skills/frontend-code-review/SKILL.md +73 -0
- package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
- package/.agent/skills/frontend-code-review/references/performance.md +36 -0
- package/.agent/skills/frontend-testing/SKILL.md +316 -0
- package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
- package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
- package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
- package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
- package/.agent/skills/frontend-testing/references/checklist.md +188 -0
- package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
- package/.agent/skills/frontend-testing/references/mocking.md +289 -0
- package/.agent/skills/frontend-testing/references/workflow.md +265 -0
- package/.agent/skills/prd-creator/JSON.md +613 -0
- package/.agent/skills/prd-creator/PRD.md +196 -0
- package/.agent/skills/prd-creator/SKILL.md +143 -0
- package/.agent/skills/skill-creator/SKILL.md +355 -0
- package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
- package/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
- package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
- package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
- package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
- package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
- package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
- package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
- package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
- package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
- package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
- package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
- package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
- package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agent/tasks/.gitkeep +0 -0
- package/.agent/tasks.json +1 -0
- package/.claude/agents/code-reviewer.md +172 -0
- package/.claude/commands/aw.md +50 -0
- package/.claude/hooks/play-sound.js +87 -0
- package/.claude/hooks/pre-tool-use.js +40 -0
- package/.claude/settings.json +54 -0
- package/.claude/settings.local.json +13 -0
- package/.mcp.json +31 -0
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/README.md +236 -0
- package/bin/cli.js +156 -0
- package/bin/lib/copy.js +149 -0
- package/bin/lib/display.js +137 -0
- package/package.json +65 -0
- package/ralph.sh +333 -0
- package/scripts/lib/args.sh +44 -0
- package/scripts/lib/cleanup.sh +53 -0
- package/scripts/lib/constants.sh +25 -0
- package/scripts/lib/display.sh +196 -0
- package/scripts/lib/logging.sh +30 -0
- package/scripts/lib/notify.sh +41 -0
- package/scripts/lib/output.sh +147 -0
- package/scripts/lib/preflight.sh +57 -0
- package/scripts/lib/preview.sh +77 -0
- package/scripts/lib/promise.sh +76 -0
- package/scripts/lib/spinner.sh +85 -0
- package/scripts/lib/terminal.sh +57 -0
- package/scripts/lib/timing.sh +223 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Mocking Guide for Frontend Tests
|
|
2
|
+
|
|
3
|
+
## ⚠️ Important: What NOT to Mock
|
|
4
|
+
|
|
5
|
+
### What TO Mock
|
|
6
|
+
|
|
7
|
+
Only mock these categories:
|
|
8
|
+
|
|
9
|
+
1. **API services** - Network calls
|
|
10
|
+
2. **Complex context providers** - When setup is too difficult
|
|
11
|
+
3. **Third-party libraries with side effects** - `next/navigation`, external SDKs
|
|
12
|
+
4. **i18n** - Always mock to return keys
|
|
13
|
+
|
|
14
|
+
Modules are not mocked automatically. Use `vi.mock` in test files, or add global mocks in `web/vitest.setup.ts`.
|
|
15
|
+
|
|
16
|
+
## Essential Mocks
|
|
17
|
+
|
|
18
|
+
### 1. Next.js Router
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
const mockPush = vi.fn()
|
|
22
|
+
const mockReplace = vi.fn()
|
|
23
|
+
|
|
24
|
+
vi.mock('next/navigation', () => ({
|
|
25
|
+
useRouter: () => ({
|
|
26
|
+
push: mockPush,
|
|
27
|
+
replace: mockReplace,
|
|
28
|
+
back: vi.fn(),
|
|
29
|
+
prefetch: vi.fn(),
|
|
30
|
+
}),
|
|
31
|
+
usePathname: () => '/current-path',
|
|
32
|
+
useSearchParams: () => new URLSearchParams('?key=value'),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
describe('Component', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should navigate on click', () => {
|
|
41
|
+
render(<Component />)
|
|
42
|
+
fireEvent.click(screen.getByRole('button'))
|
|
43
|
+
expect(mockPush).toHaveBeenCalledWith('/expected-path')
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Portal Components (with Shared State)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// ⚠️ Important: Use shared state for components that depend on each other
|
|
52
|
+
let mockPortalOpenState = false
|
|
53
|
+
|
|
54
|
+
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
|
55
|
+
PortalToFollowElem: ({ children, open, ...props }: any) => {
|
|
56
|
+
mockPortalOpenState = open || false // Update shared state
|
|
57
|
+
return <div data-testid="portal" data-open={open}>{children}</div>
|
|
58
|
+
},
|
|
59
|
+
PortalToFollowElemContent: ({ children }: any) => {
|
|
60
|
+
// ✅ Matches actual: returns null when portal is closed
|
|
61
|
+
if (!mockPortalOpenState) return null
|
|
62
|
+
return <div data-testid="portal-content">{children}</div>
|
|
63
|
+
},
|
|
64
|
+
PortalToFollowElemTrigger: ({ children }: any) => (
|
|
65
|
+
<div data-testid="portal-trigger">{children}</div>
|
|
66
|
+
),
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
describe('Component', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
vi.clearAllMocks()
|
|
72
|
+
mockPortalOpenState = false // ✅ Reset shared state
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. API Service Mocks
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import * as api from '@/service/api'
|
|
81
|
+
|
|
82
|
+
vi.mock('@/service/api')
|
|
83
|
+
|
|
84
|
+
const mockedApi = vi.mocked(api)
|
|
85
|
+
|
|
86
|
+
describe('Component', () => {
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
vi.clearAllMocks()
|
|
89
|
+
|
|
90
|
+
// Setup default mock implementation
|
|
91
|
+
mockedApi.fetchData.mockResolvedValue({ data: [] })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should show data on success', async () => {
|
|
95
|
+
mockedApi.fetchData.mockResolvedValue({ data: [{ id: 1 }] })
|
|
96
|
+
|
|
97
|
+
render(<Component />)
|
|
98
|
+
|
|
99
|
+
await waitFor(() => {
|
|
100
|
+
expect(screen.getByText('1')).toBeInTheDocument()
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should show error on failure', async () => {
|
|
105
|
+
mockedApi.fetchData.mockRejectedValue(new Error('Network error'))
|
|
106
|
+
|
|
107
|
+
render(<Component />)
|
|
108
|
+
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
expect(screen.getByText(/error/i)).toBeInTheDocument()
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 4. HTTP Mocking with Nock
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import nock from 'nock'
|
|
120
|
+
|
|
121
|
+
const GITHUB_HOST = 'https://api.github.com'
|
|
122
|
+
const GITHUB_PATH = '/repos/owner/repo'
|
|
123
|
+
|
|
124
|
+
const mockGithubApi = (status: number, body: Record<string, unknown>, delayMs = 0) => {
|
|
125
|
+
return nock(GITHUB_HOST)
|
|
126
|
+
.get(GITHUB_PATH)
|
|
127
|
+
.delay(delayMs)
|
|
128
|
+
.reply(status, body)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
describe('GithubComponent', () => {
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
nock.cleanAll()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should display repo info', async () => {
|
|
137
|
+
mockGithubApi(200, { name: 'repo', stars: 1000 })
|
|
138
|
+
|
|
139
|
+
render(<GithubComponent />)
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(screen.getByText('repo')).toBeInTheDocument()
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should handle API error', async () => {
|
|
147
|
+
mockGithubApi(500, { message: 'Server error' })
|
|
148
|
+
|
|
149
|
+
render(<GithubComponent />)
|
|
150
|
+
|
|
151
|
+
await waitFor(() => {
|
|
152
|
+
expect(screen.getByText(/error/i)).toBeInTheDocument()
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 5. Context Providers
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { ProviderContext } from '@/context/provider-context'
|
|
162
|
+
import { createMockProviderContextValue, createMockPlan } from '@/__mocks__/provider-context'
|
|
163
|
+
|
|
164
|
+
describe('Component with Context', () => {
|
|
165
|
+
it('should render for free plan', () => {
|
|
166
|
+
const mockContext = createMockPlan('sandbox')
|
|
167
|
+
|
|
168
|
+
render(
|
|
169
|
+
<ProviderContext.Provider value={mockContext}>
|
|
170
|
+
<Component />
|
|
171
|
+
</ProviderContext.Provider>
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
expect(screen.getByText('Upgrade')).toBeInTheDocument()
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('should render for pro plan', () => {
|
|
178
|
+
const mockContext = createMockPlan('professional')
|
|
179
|
+
|
|
180
|
+
render(
|
|
181
|
+
<ProviderContext.Provider value={mockContext}>
|
|
182
|
+
<Component />
|
|
183
|
+
</ProviderContext.Provider>
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
expect(screen.queryByText('Upgrade')).not.toBeInTheDocument()
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 6. React Query
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
195
|
+
|
|
196
|
+
const createTestQueryClient = () => new QueryClient({
|
|
197
|
+
defaultOptions: {
|
|
198
|
+
queries: { retry: false },
|
|
199
|
+
mutations: { retry: false },
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const renderWithQueryClient = (ui: React.ReactElement) => {
|
|
204
|
+
const queryClient = createTestQueryClient()
|
|
205
|
+
return render(
|
|
206
|
+
<QueryClientProvider client={queryClient}>
|
|
207
|
+
{ui}
|
|
208
|
+
</QueryClientProvider>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Mock Best Practices
|
|
214
|
+
|
|
215
|
+
### ✅ DO
|
|
216
|
+
|
|
217
|
+
1. **Use real base components** - Import from `@/app/components/base/` directly
|
|
218
|
+
1. **Use real project components** - Prefer importing over mocking
|
|
219
|
+
1. **Reset mocks in `beforeEach`**, not `afterEach`
|
|
220
|
+
1. **Match actual component behavior** in mocks (when mocking is necessary)
|
|
221
|
+
1. **Use factory functions** for complex mock data
|
|
222
|
+
1. **Import actual types** for type safety
|
|
223
|
+
1. **Reset shared mock state** in `beforeEach`
|
|
224
|
+
|
|
225
|
+
### ❌ DON'T
|
|
226
|
+
|
|
227
|
+
1. **Don't mock base components** (`Loading`, `Button`, `Tooltip`, etc.)
|
|
228
|
+
1. Don't mock components you can import directly
|
|
229
|
+
1. Don't create overly simplified mocks that miss conditional logic
|
|
230
|
+
1. Don't forget to clean up nock after each test
|
|
231
|
+
1. Don't use `any` types in mocks without necessity
|
|
232
|
+
|
|
233
|
+
### Mock Decision Tree
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
Need to use a component in test?
|
|
237
|
+
│
|
|
238
|
+
├─ Is it from @/app/components/base/*?
|
|
239
|
+
│ └─ YES → Import real component, DO NOT mock
|
|
240
|
+
│
|
|
241
|
+
├─ Is it a project component?
|
|
242
|
+
│ └─ YES → Prefer importing real component
|
|
243
|
+
│ Only mock if setup is extremely complex
|
|
244
|
+
│
|
|
245
|
+
├─ Is it an API service (@/service/*)?
|
|
246
|
+
│ └─ YES → Mock it
|
|
247
|
+
│
|
|
248
|
+
├─ Is it a third-party lib with side effects?
|
|
249
|
+
│ └─ YES → Mock it (next/navigation, external SDKs)
|
|
250
|
+
│
|
|
251
|
+
└─ Is it i18n?
|
|
252
|
+
└─ YES → Uses shared mock (auto-loaded). Override only for custom translations
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Factory Function Pattern
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// __mocks__/data-factories.ts
|
|
259
|
+
import type { User, Project } from '@/types'
|
|
260
|
+
|
|
261
|
+
export const createMockUser = (overrides: Partial<User> = {}): User => ({
|
|
262
|
+
id: 'user-1',
|
|
263
|
+
name: 'Test User',
|
|
264
|
+
email: 'test@example.com',
|
|
265
|
+
role: 'member',
|
|
266
|
+
createdAt: new Date().toISOString(),
|
|
267
|
+
...overrides,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
export const createMockProject = (overrides: Partial<Project> = {}): Project => ({
|
|
271
|
+
id: 'project-1',
|
|
272
|
+
name: 'Test Project',
|
|
273
|
+
description: 'A test project',
|
|
274
|
+
owner: createMockUser(),
|
|
275
|
+
members: [],
|
|
276
|
+
createdAt: new Date().toISOString(),
|
|
277
|
+
...overrides,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Usage in tests
|
|
281
|
+
it('should display project owner', () => {
|
|
282
|
+
const project = createMockProject({
|
|
283
|
+
owner: createMockUser({ name: 'John Doe' }),
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
render(<ProjectCard project={project} />)
|
|
287
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
|
288
|
+
})
|
|
289
|
+
```
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Testing Workflow Guide
|
|
2
|
+
|
|
3
|
+
This guide defines the workflow for generating tests, especially for complex components or directories with multiple files.
|
|
4
|
+
|
|
5
|
+
## Scope Clarification
|
|
6
|
+
|
|
7
|
+
This guide addresses **multi-file workflow** (how to process multiple test files).
|
|
8
|
+
|
|
9
|
+
| Scope | Rule |
|
|
10
|
+
| ------------------------ | ---------------------------------------------------------------- |
|
|
11
|
+
| **Single file** | Complete coverage in one generation (100% function, >95% branch) |
|
|
12
|
+
| **Multi-file directory** | Process one file at a time, verify each before proceeding |
|
|
13
|
+
|
|
14
|
+
## ⚠️ Critical Rule: Incremental Approach for Multi-File Testing
|
|
15
|
+
|
|
16
|
+
When testing a **directory with multiple files**, **NEVER generate all test files at once.** Use an incremental, verify-as-you-go approach.
|
|
17
|
+
|
|
18
|
+
### Why Incremental?
|
|
19
|
+
|
|
20
|
+
| Batch Approach (❌) | Incremental Approach (✅) |
|
|
21
|
+
| ----------------------------- | -------------------------------------- |
|
|
22
|
+
| Generate 5+ tests at once | Generate 1 test at a time |
|
|
23
|
+
| Run tests only at the end | Run test immediately after each file |
|
|
24
|
+
| Multiple failures compound | Single point of failure, easy to debug |
|
|
25
|
+
| Hard to identify root cause | Clear cause-effect relationship |
|
|
26
|
+
| Mock issues affect many files | Mock issues caught early |
|
|
27
|
+
| Messy git history | Clean, atomic commits possible |
|
|
28
|
+
|
|
29
|
+
## Single File Workflow
|
|
30
|
+
|
|
31
|
+
When testing a **single component, hook, or utility**:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
1. Read source code completely
|
|
35
|
+
2. Analyze complexity first to identify required test scenarios
|
|
36
|
+
3. Check complexity score and features detected
|
|
37
|
+
4. Write the test file
|
|
38
|
+
5. Run test: `pnpm test <file>.spec.tsx`
|
|
39
|
+
6. Fix any failures
|
|
40
|
+
7. Verify coverage meets goals (100% function, >95% branch)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Directory/Multi-File Workflow (MUST FOLLOW)
|
|
44
|
+
|
|
45
|
+
When testing a **directory or multiple files**, follow this strict workflow:
|
|
46
|
+
|
|
47
|
+
### Step 1: Analyze and Plan
|
|
48
|
+
|
|
49
|
+
1. **List all files** that need tests in the directory
|
|
50
|
+
1. **Categorize by complexity**:
|
|
51
|
+
- 🟢 **Simple**: Utility functions, simple hooks, presentational components
|
|
52
|
+
- 🟡 **Medium**: Components with state, effects, or event handlers
|
|
53
|
+
- 🔴 **Complex**: Components with API calls, routing, or many dependencies
|
|
54
|
+
1. **Order by dependency**: Test dependencies before dependents
|
|
55
|
+
1. **Create a todo list** to track progress
|
|
56
|
+
|
|
57
|
+
### Step 2: Determine Processing Order
|
|
58
|
+
|
|
59
|
+
Process files in this recommended order:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
1. Utility functions (simplest, no React)
|
|
63
|
+
2. Custom hooks (isolated logic)
|
|
64
|
+
3. Simple presentational components (few/no props)
|
|
65
|
+
4. Medium complexity components (state, effects)
|
|
66
|
+
5. Complex components (API, routing, many deps)
|
|
67
|
+
6. Container/index components (integration tests - last)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Rationale**:
|
|
71
|
+
|
|
72
|
+
- Simpler files help establish mock patterns
|
|
73
|
+
- Hooks used by components should be tested first
|
|
74
|
+
- Integration tests (index files) depend on child components working
|
|
75
|
+
|
|
76
|
+
### Step 3: Process Each File Incrementally
|
|
77
|
+
|
|
78
|
+
**For EACH file in the ordered list:**
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
┌─────────────────────────────────────────────┐
|
|
82
|
+
│ 1. Write test file │
|
|
83
|
+
│ 2. Run: npm run test-unit <file>.spec.tsx │
|
|
84
|
+
│ 3. If FAIL → Fix immediately, re-run │
|
|
85
|
+
│ 4. If PASS → Mark complete in todo list │
|
|
86
|
+
│ 5. ONLY THEN proceed to next file │
|
|
87
|
+
└─────────────────────────────────────────────┘
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**DO NOT proceed to the next file until the current one passes.**
|
|
91
|
+
|
|
92
|
+
### Step 4: Final Verification
|
|
93
|
+
|
|
94
|
+
After all individual tests pass:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Run all tests in the directory together
|
|
98
|
+
npm run test-unit path/to/directory/
|
|
99
|
+
|
|
100
|
+
# Check coverage
|
|
101
|
+
npm run test-unit:coverage path/to/directory/
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 🔴 Very Complex Components
|
|
105
|
+
|
|
106
|
+
**Consider refactoring BEFORE testing:**
|
|
107
|
+
|
|
108
|
+
- Break component into smaller, testable pieces
|
|
109
|
+
- Extract complex logic into custom hooks
|
|
110
|
+
- Separate container and presentational layers
|
|
111
|
+
|
|
112
|
+
**If testing as-is:**
|
|
113
|
+
|
|
114
|
+
- Use integration tests for complex workflows
|
|
115
|
+
- Use `test.each()` for data-driven testing
|
|
116
|
+
- Multiple `describe` blocks for organization
|
|
117
|
+
- Consider testing major sections separately
|
|
118
|
+
|
|
119
|
+
### 🟡 Medium Complexity
|
|
120
|
+
|
|
121
|
+
- Group related tests in `describe` blocks
|
|
122
|
+
- Test integration scenarios between internal parts
|
|
123
|
+
- Focus on state transitions and side effects
|
|
124
|
+
- Use helper functions to reduce test complexity
|
|
125
|
+
|
|
126
|
+
### 🟢 Simple Components
|
|
127
|
+
|
|
128
|
+
- Standard test structure
|
|
129
|
+
- Focus on props, rendering, and edge cases
|
|
130
|
+
- Usually straightforward to test
|
|
131
|
+
|
|
132
|
+
### 📏 Large Files (500+ lines)
|
|
133
|
+
|
|
134
|
+
Regardless of complexity score:
|
|
135
|
+
|
|
136
|
+
- **Strongly consider refactoring** before testing
|
|
137
|
+
- If testing as-is, test major sections separately
|
|
138
|
+
- Create helper functions for test setup
|
|
139
|
+
- May need multiple test files
|
|
140
|
+
|
|
141
|
+
## Todo List Format
|
|
142
|
+
|
|
143
|
+
When testing multiple files, use a todo list like this:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Testing: path/to/directory/
|
|
147
|
+
|
|
148
|
+
Ordered by complexity (simple → complex):
|
|
149
|
+
|
|
150
|
+
☐ utils/helper.ts [utility, simple]
|
|
151
|
+
☐ hooks/use-custom-hook.ts [hook, simple]
|
|
152
|
+
☐ empty-state.tsx [component, simple]
|
|
153
|
+
☐ item-card.tsx [component, medium]
|
|
154
|
+
☐ list.tsx [component, complex]
|
|
155
|
+
☐ index.tsx [integration]
|
|
156
|
+
|
|
157
|
+
Progress: 0/6 complete
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Update status as you complete each:
|
|
161
|
+
|
|
162
|
+
- ☐ → ⏳ (in progress)
|
|
163
|
+
- ⏳ → ✅ (complete and verified)
|
|
164
|
+
- ⏳ → ❌ (blocked, needs attention)
|
|
165
|
+
|
|
166
|
+
## When to Stop and Verify
|
|
167
|
+
|
|
168
|
+
**Always run tests after:**
|
|
169
|
+
|
|
170
|
+
- Completing a test file
|
|
171
|
+
- Making changes to fix a failure
|
|
172
|
+
- Modifying shared mocks
|
|
173
|
+
- Updating test utilities or helpers
|
|
174
|
+
|
|
175
|
+
**Signs you should pause:**
|
|
176
|
+
|
|
177
|
+
- More than 2 consecutive test failures
|
|
178
|
+
- Mock-related errors appearing
|
|
179
|
+
- Unclear why a test is failing
|
|
180
|
+
- Test passing but coverage unexpectedly low
|
|
181
|
+
|
|
182
|
+
## Common Pitfalls to Avoid
|
|
183
|
+
|
|
184
|
+
### ❌ Don't: Generate Everything First
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
# BAD: Writing all files then testing
|
|
188
|
+
Write component-a.spec.tsx
|
|
189
|
+
Write component-b.spec.tsx
|
|
190
|
+
Write component-c.spec.tsx
|
|
191
|
+
Write component-d.spec.tsx
|
|
192
|
+
Run pnpm test ← Multiple failures, hard to debug
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### ✅ Do: Verify Each Step
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
# GOOD: Incremental with verification
|
|
199
|
+
Write component-a.spec.tsx
|
|
200
|
+
Run pnpm test component-a.spec.tsx ✅
|
|
201
|
+
Write component-b.spec.tsx
|
|
202
|
+
Run pnpm test component-b.spec.tsx ✅
|
|
203
|
+
...continue...
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### ❌ Don't: Skip Verification for "Simple" Components
|
|
207
|
+
|
|
208
|
+
Even simple components can have:
|
|
209
|
+
|
|
210
|
+
- Import errors
|
|
211
|
+
- Missing mock setup
|
|
212
|
+
- Incorrect assumptions about props
|
|
213
|
+
|
|
214
|
+
**Always verify, regardless of perceived simplicity.**
|
|
215
|
+
|
|
216
|
+
### ❌ Don't: Continue When Tests Fail
|
|
217
|
+
|
|
218
|
+
Failing tests compound:
|
|
219
|
+
|
|
220
|
+
- A mock issue in file A affects files B, C, D
|
|
221
|
+
- Fixing A later requires revisiting all dependent tests
|
|
222
|
+
- Time wasted on debugging cascading failures
|
|
223
|
+
|
|
224
|
+
**Fix failures immediately before proceeding.**
|
|
225
|
+
|
|
226
|
+
## Integration with Claude's Todo Feature
|
|
227
|
+
|
|
228
|
+
When using Claude for multi-file testing:
|
|
229
|
+
|
|
230
|
+
1. **Ask Claude to create a todo list** before starting
|
|
231
|
+
1. **Request one file at a time** or ensure Claude processes incrementally
|
|
232
|
+
1. **Verify each test passes** before asking for the next
|
|
233
|
+
1. **Mark todos complete** as you progress
|
|
234
|
+
|
|
235
|
+
Example prompt:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
Test all components in `path/to/directory/`.
|
|
239
|
+
First, analyze the directory and create a todo list ordered by complexity.
|
|
240
|
+
Then, process ONE file at a time, waiting for my confirmation that tests pass
|
|
241
|
+
before proceeding to the next.
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Summary Checklist
|
|
245
|
+
|
|
246
|
+
Before starting multi-file testing:
|
|
247
|
+
|
|
248
|
+
- [ ] Listed all files needing tests
|
|
249
|
+
- [ ] Ordered by complexity (simple → complex)
|
|
250
|
+
- [ ] Created todo list for tracking
|
|
251
|
+
- [ ] Understand dependencies between files
|
|
252
|
+
|
|
253
|
+
During testing:
|
|
254
|
+
|
|
255
|
+
- [ ] Processing ONE file at a time
|
|
256
|
+
- [ ] Running tests after EACH file
|
|
257
|
+
- [ ] Fixing failures BEFORE proceeding
|
|
258
|
+
- [ ] Updating todo list progress
|
|
259
|
+
|
|
260
|
+
After completion:
|
|
261
|
+
|
|
262
|
+
- [ ] All individual tests pass
|
|
263
|
+
- [ ] Full directory test run passes
|
|
264
|
+
- [ ] Coverage goals met
|
|
265
|
+
- [ ] Todo list shows all complete
|