claude-toolkit 0.1.12 → 0.1.18
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/CHANGELOG.md +24 -0
- package/README.md +3 -0
- package/core/skills/ct-testing-patterns/SKILL.md +37 -1
- package/docs/README.md +3 -0
- package/docs/best-practices/testing/README.md +84 -0
- package/docs/best-practices/testing/playwright-e2e.md +649 -0
- package/docs/best-practices/testing/storybook-interaction.md +445 -0
- package/docs/best-practices/testing/vitest-unit.md +451 -0
- package/docs/stacks/playwright-patterns.md +179 -0
- package/docs/stacks/storybook-patterns.md +160 -0
- package/docs/stacks/vite-vitest-patterns.md +485 -0
- package/package.json +1 -1
- package/stacks/playwright/skills/ct-playwright-patterns/SKILL.md +168 -0
- package/stacks/playwright/stack.json +39 -0
- package/stacks/solidjs/stack.json +7 -1
- package/stacks/storybook/skills/ct-storybook-patterns/SKILL.md +149 -0
- package/stacks/storybook/stack.json +46 -0
- package/stacks/vite/skills/ct-vite-vitest-patterns/SKILL.md +492 -0
- package/stacks/vite/stack.json +66 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-vite-vitest-patterns
|
|
3
|
+
description: Vite build tooling and Vitest unit/integration testing patterns -- config, plugins, mocking, coverage, browser mode, and workspace
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vite + Vitest Patterns
|
|
7
|
+
|
|
8
|
+
Vite is the build tool; Vitest is its native test runner. They share a unified config -- aliases, plugins, and `define` values work identically in both. This is the primary advantage over separate build/test toolchains.
|
|
9
|
+
|
|
10
|
+
## Vite Configuration
|
|
11
|
+
|
|
12
|
+
### Config Structure
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { defineConfig } from 'vite'
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
base: '/',
|
|
19
|
+
plugins: [],
|
|
20
|
+
envPrefix: 'VITE_',
|
|
21
|
+
|
|
22
|
+
resolve: {
|
|
23
|
+
alias: { '@': '/src' },
|
|
24
|
+
conditions: [],
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
css: {
|
|
28
|
+
transformer: 'postcss', // or 'lightningcss'
|
|
29
|
+
modules: {},
|
|
30
|
+
preprocessorOptions: {},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
oxc: {}, // Oxc transformer (replaces esbuild in Vite 8)
|
|
34
|
+
|
|
35
|
+
server: {
|
|
36
|
+
port: 5173,
|
|
37
|
+
proxy: {},
|
|
38
|
+
warmup: { clientFiles: [] }, // pre-transform hot files for faster cold starts
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
build: {
|
|
42
|
+
target: 'baseline-widely-available',
|
|
43
|
+
outDir: 'dist',
|
|
44
|
+
sourcemap: false,
|
|
45
|
+
minify: 'oxc', // 'oxc' (default) | 'terser' | false
|
|
46
|
+
rolldownOptions: {}, // Rolldown bundler config (replaces rollupOptions in Vite 8)
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
ssr: {
|
|
50
|
+
target: 'node',
|
|
51
|
+
noExternal: [],
|
|
52
|
+
external: [],
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Conditional Config
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
export default defineConfig(({ command, mode, isSsrBuild }) => {
|
|
61
|
+
if (command === 'serve') return { /* dev-specific */ }
|
|
62
|
+
return { /* build-specific */ }
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Environment Variables
|
|
67
|
+
|
|
68
|
+
Loading order (later overrides earlier): `.env` > `.env.local` > `.env.[mode]` > `.env.[mode].local`.
|
|
69
|
+
|
|
70
|
+
Built-in `import.meta.env`: `MODE`, `BASE_URL`, `PROD`, `DEV`, `SSR`.
|
|
71
|
+
|
|
72
|
+
**Security:** Never put secrets in `VITE_*` variables -- they are embedded in client bundles.
|
|
73
|
+
|
|
74
|
+
TypeScript declarations (`src/vite-env.d.ts`):
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
/// <reference types="vite/client" />
|
|
78
|
+
|
|
79
|
+
interface ImportMetaEnv {
|
|
80
|
+
readonly VITE_API_URL: string
|
|
81
|
+
}
|
|
82
|
+
interface ImportMeta {
|
|
83
|
+
readonly env: ImportMetaEnv
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Plugin System
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
export default defineConfig({
|
|
91
|
+
plugins: [
|
|
92
|
+
solidPlugin(),
|
|
93
|
+
process.env.ANALYZE && visualizer(), // conditional (falsy ignored)
|
|
94
|
+
{ ...myPlugin(), enforce: 'pre' }, // before Vite core transforms
|
|
95
|
+
{ ...myPlugin(), apply: 'build' }, // build only
|
|
96
|
+
],
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Key hooks (execution order): `config` > `configResolved` > `configureServer` > `transformIndexHtml` > `resolveId` > `load` > `transform` > `handleHotUpdate`.
|
|
101
|
+
|
|
102
|
+
### Library Mode
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { resolve } from 'node:path'
|
|
106
|
+
|
|
107
|
+
export default defineConfig({
|
|
108
|
+
build: {
|
|
109
|
+
lib: {
|
|
110
|
+
entry: resolve(import.meta.dirname, 'lib/main.ts'),
|
|
111
|
+
name: 'MyLib',
|
|
112
|
+
fileName: 'my-lib',
|
|
113
|
+
formats: ['es', 'cjs'],
|
|
114
|
+
},
|
|
115
|
+
rolldownOptions: {
|
|
116
|
+
external: ['solid-js', 'solid-js/web'], // always externalize peer deps
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### SSR
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"build:client": "vite build --outDir dist/client",
|
|
127
|
+
"build:server": "vite build --outDir dist/server --ssr src/entry-server.ts"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Use `middlewareMode: true` + `appType: 'custom'` for framework SSR integration.
|
|
132
|
+
|
|
133
|
+
### Build Performance
|
|
134
|
+
|
|
135
|
+
1. **Avoid barrel files** -- import directly from source modules, not re-export `index.ts`
|
|
136
|
+
2. **Use explicit extensions** -- `import './Component.tsx'` not `import './Component'`
|
|
137
|
+
3. **Warm up hot files** -- `server.warmup.clientFiles: ['./src/main.tsx']`
|
|
138
|
+
4. **Prefer native CSS** -- CSS nesting is supported natively; avoid Sass when possible
|
|
139
|
+
5. **Narrow resolve.extensions** -- remove extensions you don't use
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Vitest Configuration
|
|
144
|
+
|
|
145
|
+
### Standalone Config
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { defineConfig } from 'vitest/config'
|
|
149
|
+
|
|
150
|
+
export default defineConfig({
|
|
151
|
+
test: {
|
|
152
|
+
include: ['**/*.{test,spec}.{ts,tsx}'],
|
|
153
|
+
environment: 'node', // 'node' | 'jsdom' | 'happy-dom'
|
|
154
|
+
globals: false,
|
|
155
|
+
setupFiles: [],
|
|
156
|
+
testTimeout: 5000,
|
|
157
|
+
pool: 'forks', // 'forks' | 'threads' | 'vmThreads'
|
|
158
|
+
|
|
159
|
+
restoreMocks: true, // auto vi.restoreAllMocks() after each test
|
|
160
|
+
clearMocks: true,
|
|
161
|
+
|
|
162
|
+
coverage: {
|
|
163
|
+
provider: 'v8',
|
|
164
|
+
include: ['src/**/*.{ts,tsx}'],
|
|
165
|
+
exclude: ['**/*.test.*', '**/*.d.ts', 'src/types/**'],
|
|
166
|
+
reporter: ['text', 'html', 'lcov'],
|
|
167
|
+
thresholds: { lines: 80, branches: 80, functions: 80, statements: 80 },
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
snapshotSerializers: [],
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Integrated with Vite Config
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
/// <reference types="vitest/config" />
|
|
179
|
+
import { defineConfig } from 'vite'
|
|
180
|
+
|
|
181
|
+
export default defineConfig({
|
|
182
|
+
plugins: [solidPlugin()],
|
|
183
|
+
resolve: { alias: { '@': '/src' } }, // shared: works in both app and tests
|
|
184
|
+
test: {
|
|
185
|
+
environment: 'jsdom',
|
|
186
|
+
setupFiles: ['./tests/setup.ts'],
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Vite's `resolve.alias`, `plugins`, and `define` are automatically inherited by Vitest. Never duplicate them.
|
|
192
|
+
|
|
193
|
+
### Merging Separate Configs
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { defineConfig, mergeConfig } from 'vitest/config'
|
|
197
|
+
import viteConfig from './vite.config'
|
|
198
|
+
|
|
199
|
+
export default mergeConfig(viteConfig, defineConfig({
|
|
200
|
+
test: { environment: 'jsdom' },
|
|
201
|
+
}))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Workspace / Projects (Vitest 3+)
|
|
205
|
+
|
|
206
|
+
Use `test.projects` for monorepos or mixed environments:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
export default defineConfig({
|
|
210
|
+
test: {
|
|
211
|
+
projects: [
|
|
212
|
+
'packages/*',
|
|
213
|
+
{
|
|
214
|
+
test: {
|
|
215
|
+
name: 'unit',
|
|
216
|
+
include: ['src/**/*.test.ts'],
|
|
217
|
+
environment: 'node',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
test: {
|
|
222
|
+
name: 'components',
|
|
223
|
+
include: ['src/**/*.test.tsx'],
|
|
224
|
+
environment: 'jsdom',
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Mocking
|
|
235
|
+
|
|
236
|
+
### Mock Functions
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const fn = vi.fn()
|
|
240
|
+
fn.mockReturnValue(42)
|
|
241
|
+
fn.mockResolvedValue({ data: [] })
|
|
242
|
+
fn.mockImplementation((x) => x * 2)
|
|
243
|
+
|
|
244
|
+
expect(fn).toHaveBeenCalledWith('arg')
|
|
245
|
+
expect(fn).toHaveBeenCalledTimes(1)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Module Mocking
|
|
249
|
+
|
|
250
|
+
`vi.mock` is hoisted above imports automatically:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
vi.mock('./api', () => ({
|
|
254
|
+
fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Test' }),
|
|
255
|
+
}))
|
|
256
|
+
|
|
257
|
+
// Access original implementation inside mock
|
|
258
|
+
vi.mock('./utils', async (importOriginal) => {
|
|
259
|
+
const actual = await importOriginal<typeof import('./utils')>()
|
|
260
|
+
return { ...actual, format: vi.fn() }
|
|
261
|
+
})
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Hoisting caveat:** Variables defined before `vi.mock()` are not accessible inside the factory. Use `vi.hoisted()` for shared variables:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const { mockFetch } = vi.hoisted(() => ({
|
|
268
|
+
mockFetch: vi.fn(),
|
|
269
|
+
}))
|
|
270
|
+
|
|
271
|
+
vi.mock('./api', () => ({ fetch: mockFetch }))
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Spying
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
278
|
+
expect(spy).toHaveBeenCalledWith('expected warning')
|
|
279
|
+
spy.mockRestore()
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Timer Mocking
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
vi.useFakeTimers()
|
|
286
|
+
vi.setSystemTime(new Date('2026-01-01'))
|
|
287
|
+
|
|
288
|
+
setTimeout(callback, 1000)
|
|
289
|
+
vi.advanceTimersByTime(1000)
|
|
290
|
+
expect(callback).toHaveBeenCalled()
|
|
291
|
+
|
|
292
|
+
vi.useRealTimers()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Environment Stubs
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
vi.stubGlobal('__VERSION__', '1.0.0')
|
|
299
|
+
vi.stubEnv('VITE_API_URL', 'http://test.example.com')
|
|
300
|
+
|
|
301
|
+
afterEach(() => {
|
|
302
|
+
vi.unstubAllGlobals()
|
|
303
|
+
vi.unstubAllEnvs()
|
|
304
|
+
})
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Snapshot Testing
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// File snapshots (stored in __snapshots__/)
|
|
313
|
+
expect(result).toMatchSnapshot()
|
|
314
|
+
|
|
315
|
+
// Inline snapshots (written into test file by vitest --update)
|
|
316
|
+
expect(result).toMatchInlineSnapshot(`{ "id": 1 }`)
|
|
317
|
+
|
|
318
|
+
// Custom file path
|
|
319
|
+
expect(htmlOutput).toMatchFileSnapshot('./fixtures/expected.html')
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Keep inline snapshots under 10-15 lines. Use file snapshots for complex output. Snapshots catch regressions but don't verify correctness -- combine with explicit assertions on critical values.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## In-Source Testing
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// src/utils/math.ts
|
|
330
|
+
export function add(...args: number[]) {
|
|
331
|
+
return args.reduce((a, b) => a + b, 0)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (import.meta.vitest) {
|
|
335
|
+
const { it, expect } = import.meta.vitest
|
|
336
|
+
it('adds numbers', () => {
|
|
337
|
+
expect(add(1, 2, 3)).toBe(6)
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Config:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
export default defineConfig({
|
|
346
|
+
test: { includeSource: ['src/**/*.ts'] },
|
|
347
|
+
define: { 'import.meta.vitest': 'undefined' }, // tree-shake from production
|
|
348
|
+
})
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Best for small utility functions. Use separate test files for components and integration tests.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Browser Mode (Vitest 4+, Stable)
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { playwright } from '@vitest/browser-playwright'
|
|
359
|
+
|
|
360
|
+
export default defineConfig({
|
|
361
|
+
test: {
|
|
362
|
+
browser: {
|
|
363
|
+
enabled: true,
|
|
364
|
+
provider: playwright(),
|
|
365
|
+
instances: [{ browser: 'chromium' }],
|
|
366
|
+
headless: true,
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
})
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Providers: `@vitest/browser-playwright` (recommended), `@vitest/browser-webdriverio`.
|
|
373
|
+
|
|
374
|
+
Quick setup: `npx vitest init browser`
|
|
375
|
+
|
|
376
|
+
### Visual Regression
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { page } from 'vitest/browser'
|
|
380
|
+
|
|
381
|
+
it('matches visual baseline', async () => {
|
|
382
|
+
await page.goto('/component-demo')
|
|
383
|
+
await expect(page.getByRole('button')).toMatchScreenshot()
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Playwright Traces
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
browser: {
|
|
391
|
+
provider: playwright({ trace: 'on-first-retry' }),
|
|
392
|
+
// 'off' | 'on' | 'on-first-retry' | 'on-all-retries' | 'retain-on-failure'
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Browser Mode Limitations
|
|
397
|
+
|
|
398
|
+
- Cannot `vi.spyOn` module exports -- use `vi.mock('./module', { spy: true })` instead
|
|
399
|
+
- Thread-blocking APIs (`alert`, `confirm`, `prompt`) are auto-mocked
|
|
400
|
+
- Uses testing-library selectors: `getByRole`, `getByText`, `getByLabelText`
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Coverage
|
|
405
|
+
|
|
406
|
+
Install: `@vitest/coverage-v8` (default, fastest) or `@vitest/coverage-istanbul` (universal).
|
|
407
|
+
|
|
408
|
+
Run: `vitest --coverage`
|
|
409
|
+
|
|
410
|
+
Ignore comments:
|
|
411
|
+
|
|
412
|
+
- V8: `/* v8 ignore next */`
|
|
413
|
+
- Istanbul: `/* istanbul ignore next -- @preserve */`
|
|
414
|
+
|
|
415
|
+
Include `@preserve` to prevent Oxc minifier from stripping comments.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Custom Matchers
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import { expect } from 'vitest'
|
|
423
|
+
|
|
424
|
+
expect.extend({
|
|
425
|
+
toBeWithinRange(received: number, floor: number, ceiling: number) {
|
|
426
|
+
const pass = received >= floor && received <= ceiling
|
|
427
|
+
return {
|
|
428
|
+
pass,
|
|
429
|
+
message: () => `expected ${received} to be within [${floor}, ${ceiling}]`,
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
})
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
TypeScript (`vitest.d.ts`):
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
import 'vitest'
|
|
439
|
+
|
|
440
|
+
declare module 'vitest' {
|
|
441
|
+
interface Matchers<T = any> {
|
|
442
|
+
toBeWithinRange(floor: number, ceiling: number): T
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Schema Validation (Vitest 4+)
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
import { z } from 'zod'
|
|
451
|
+
|
|
452
|
+
const UserSchema = z.object({ id: z.number(), name: z.string() })
|
|
453
|
+
expect(response.data).toSatisfy(expect.schemaMatching(UserSchema))
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Works with any Standard Schema v1 library (Zod, Valibot, ArkType).
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Vite 8 Migration
|
|
461
|
+
|
|
462
|
+
| Old (Vite 7-) | New (Vite 8) | Status |
|
|
463
|
+
|---|---|---|
|
|
464
|
+
| `build.rollupOptions` | `build.rolldownOptions` | Deprecated, compat layer exists |
|
|
465
|
+
| `optimizeDeps.esbuildOptions` | `optimizeDeps.rolldownOptions` | Deprecated |
|
|
466
|
+
| `esbuild` config | `oxc` config | Deprecated |
|
|
467
|
+
| `build.minify: 'esbuild'` | `build.minify: 'oxc'` | New default |
|
|
468
|
+
|
|
469
|
+
The compat layer auto-converts old keys. New code should use the new keys.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Anti-Patterns
|
|
474
|
+
|
|
475
|
+
### Vite
|
|
476
|
+
|
|
477
|
+
1. **Barrel file sprawl** -- Re-exporting through `index.ts` forces transforming all modules. Import directly from source.
|
|
478
|
+
2. **Secrets in VITE_ variables** -- Embedded in client bundles. Use server-only env vars, proxy through API routes.
|
|
479
|
+
3. **Duplicating resolve config for tests** -- Vitest inherits Vite's aliases and plugins. Don't redeclare.
|
|
480
|
+
4. **Using `rollupOptions` in Vite 8** -- Works via compat but generates warnings. Use `rolldownOptions`.
|
|
481
|
+
5. **Not externalizing peer deps in library mode** -- Bundling framework deps causes duplicate instances.
|
|
482
|
+
|
|
483
|
+
### Vitest
|
|
484
|
+
|
|
485
|
+
1. **Over-mocking internal modules** -- Mock at boundaries (HTTP, filesystem, external APIs), not internal functions.
|
|
486
|
+
2. **Forgetting vi.mock is hoisted** -- Variables before `vi.mock()` aren't accessible in the factory. Use `vi.hoisted()`.
|
|
487
|
+
3. **Not restoring mocks** -- Set `restoreMocks: true` in config. Leaked mocks cause cascading failures.
|
|
488
|
+
4. **Using jsdom when node suffices** -- jsdom adds overhead. Use workspace projects to split: `node` for logic, `jsdom` for components.
|
|
489
|
+
5. **Large inline snapshots** -- Over 10-15 lines reduces readability. Use `toMatchFileSnapshot`.
|
|
490
|
+
6. **Snapshot-only testing** -- Snapshots catch regressions but don't verify correctness. Add explicit assertions.
|
|
491
|
+
7. **Global jsdom environment** -- Use `test.projects` to run DOM tests in jsdom and logic tests in node.
|
|
492
|
+
8. **Using `vi.spyOn` in browser mode** -- Use `vi.mock('./module', { spy: true })` instead.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-vite-vitest-patterns",
|
|
3
|
+
"description": "Vite build tooling and Vitest unit/integration testing patterns",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"vite.config.ts": "ct-vite-vitest-patterns",
|
|
6
|
+
"vitest.config.ts": "ct-vite-vitest-patterns",
|
|
7
|
+
"src/**/*.test.ts": "ct-vite-vitest-patterns",
|
|
8
|
+
"src/**/*.test.tsx": "ct-vite-vitest-patterns",
|
|
9
|
+
"tests": "ct-vite-vitest-patterns"
|
|
10
|
+
},
|
|
11
|
+
"fileExtensions": ["test.ts", "test.tsx", "spec.ts", "spec.tsx"],
|
|
12
|
+
"skillRules": {
|
|
13
|
+
"ct-vite-vitest-patterns": {
|
|
14
|
+
"description": "Vite build config, plugins, env vars, and Vitest testing with mocking, coverage, browser mode, and workspace",
|
|
15
|
+
"priority": 7,
|
|
16
|
+
"triggers": {
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vite",
|
|
19
|
+
"vitest",
|
|
20
|
+
"vi.fn",
|
|
21
|
+
"vi.mock",
|
|
22
|
+
"vi.spyOn",
|
|
23
|
+
"defineConfig",
|
|
24
|
+
"coverage",
|
|
25
|
+
"snapshot",
|
|
26
|
+
"in-source test",
|
|
27
|
+
"browser mode"
|
|
28
|
+
],
|
|
29
|
+
"keywordPatterns": [
|
|
30
|
+
"\\bvite\\b",
|
|
31
|
+
"\\bvitest\\b",
|
|
32
|
+
"\\bvi\\.fn\\b",
|
|
33
|
+
"\\bvi\\.mock\\b",
|
|
34
|
+
"\\bvi\\.spyOn\\b"
|
|
35
|
+
],
|
|
36
|
+
"pathPatterns": [
|
|
37
|
+
"**/vite.config.*",
|
|
38
|
+
"**/vitest.config.*",
|
|
39
|
+
"**/*.test.ts",
|
|
40
|
+
"**/*.test.tsx",
|
|
41
|
+
"**/*.spec.ts",
|
|
42
|
+
"**/*.spec.tsx",
|
|
43
|
+
"**/tests/**"
|
|
44
|
+
],
|
|
45
|
+
"intentPatterns": [
|
|
46
|
+
"(?:create|write|add|run).*(?:test|spec|vitest)",
|
|
47
|
+
"(?:configure|setup).*(?:vite|vitest|coverage)",
|
|
48
|
+
"(?:mock|spy|stub).*(?:function|module|timer)"
|
|
49
|
+
],
|
|
50
|
+
"contentPatterns": [
|
|
51
|
+
"vitest/config",
|
|
52
|
+
"vi.fn(",
|
|
53
|
+
"vi.mock(",
|
|
54
|
+
"vi.spyOn(",
|
|
55
|
+
"vi.useFakeTimers",
|
|
56
|
+
"toMatchSnapshot",
|
|
57
|
+
"toMatchInlineSnapshot",
|
|
58
|
+
"import.meta.vitest",
|
|
59
|
+
"@vitest/coverage",
|
|
60
|
+
"@vitest/browser"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"relatedSkills": ["ct-testing-patterns", "ct-storybook-patterns", "ct-playwright-patterns"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|