skillshop 2.0.0 → 2.1.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/README.md +217 -104
- package/agents-content/000_communication-writing.block.md +11 -0
- package/agents-content/001_external-actions.block.md +9 -0
- package/agents-content/AGENTS.md +11 -0
- package/package.json +6 -3
- package/skills-content/architecture-scanner/SKILL.md +274 -0
- package/skills-content/architecture-scanner/files.json +10 -0
- package/skills-content/architecture-scanner/scan.sh +823 -0
- package/skills-content/change-review/SKILL.md +76 -0
- package/skills-content/change-review/agents/openai.yaml +4 -0
- package/skills-content/changelog/SKILL.md +99 -0
- package/skills-content/changeset/changeset.md +80 -0
- package/skills-content/code-buddy/SKILL.md +136 -0
- package/skills-content/design-system-catalog/SKILL.md +106 -0
- package/skills-content/design-system-catalog/extract-components.mjs +441 -0
- package/skills-content/design-system-catalog/generate-catalog.sh +255 -0
- package/skills-content/flare/SKILL.md +96 -0
- package/skills-content/grilling/SKILL.md +31 -0
- package/skills-content/install-sigmap/SKILL.md +97 -0
- package/skills-content/one-shot/SKILL.md +51 -0
- package/skills-content/release/SKILL.md +128 -0
- package/skills-content/shadow-clone/SKILL.md +228 -0
- package/skills-content/shadow-clone/extract-signatures.mjs +202 -0
- package/skills-content/shadow-clone/scan-repo.mjs +343 -0
- package/skills-content/ship/SKILL.md +95 -0
- package/skills-content/shots/SKILL.md +89 -0
- package/skills-content/shots/agents/openai.yaml +4 -0
- package/skills-content/skillshop/SKILL.md +104 -0
- package/skills-content/skillz/SKILL.md +15 -0
- package/skills-content/transcript-clean/SKILL.md +55 -0
- package/skills-content/transcript-edit/SKILL.md +80 -0
- package/skills-content/vitest/GENERATION.md +5 -0
- package/skills-content/vitest/SKILL.md +52 -0
- package/skills-content/vitest/references/advanced-environments.md +264 -0
- package/skills-content/vitest/references/advanced-projects.md +300 -0
- package/skills-content/vitest/references/advanced-type-testing.md +237 -0
- package/skills-content/vitest/references/advanced-vi.md +249 -0
- package/skills-content/vitest/references/core-cli.md +166 -0
- package/skills-content/vitest/references/core-config.md +174 -0
- package/skills-content/vitest/references/core-describe.md +193 -0
- package/skills-content/vitest/references/core-expect.md +219 -0
- package/skills-content/vitest/references/core-hooks.md +244 -0
- package/skills-content/vitest/references/core-test-api.md +233 -0
- package/skills-content/vitest/references/features-concurrency.md +250 -0
- package/skills-content/vitest/references/features-context.md +238 -0
- package/skills-content/vitest/references/features-coverage.md +207 -0
- package/skills-content/vitest/references/features-filtering.md +211 -0
- package/skills-content/vitest/references/features-mocking.md +265 -0
- package/skills-content/vitest/references/features-snapshots.md +207 -0
- package/skills-content/workleaf/SKILL.md +77 -0
- package/skills-content/worklog/SKILL.md +145 -0
- package/skills-content/worklog/references/summary-format.md +114 -0
- package/skills-content/worktree/SKILL.md +71 -0
- package/src/agents-instructions.js +219 -0
- package/src/cli.js +87 -23
- package/src/import.js +12 -7
- package/src/index.js +1 -0
- package/src/legacy-agents-content.js +36 -0
- package/src/legacy-cli.js +1234 -0
- package/src/library.js +9 -0
- package/src/specifier.js +11 -0
- package/src/user-install.js +48 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-coverage
|
|
3
|
+
description: Code coverage with V8 or Istanbul providers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Coverage
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Run tests with coverage
|
|
12
|
+
vitest run --coverage
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// vitest.config.ts
|
|
19
|
+
defineConfig({
|
|
20
|
+
test: {
|
|
21
|
+
coverage: {
|
|
22
|
+
// Provider: 'v8' (default, faster) or 'istanbul' (more compatible)
|
|
23
|
+
provider: 'v8',
|
|
24
|
+
|
|
25
|
+
// Enable coverage
|
|
26
|
+
enabled: true,
|
|
27
|
+
|
|
28
|
+
// Reporters
|
|
29
|
+
reporter: ['text', 'json', 'html'],
|
|
30
|
+
|
|
31
|
+
// Files to include
|
|
32
|
+
include: ['src/**/*.{ts,tsx}'],
|
|
33
|
+
|
|
34
|
+
// Files to exclude
|
|
35
|
+
exclude: [
|
|
36
|
+
'node_modules/',
|
|
37
|
+
'tests/',
|
|
38
|
+
'**/*.d.ts',
|
|
39
|
+
'**/*.test.ts',
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
// Report uncovered files
|
|
43
|
+
all: true,
|
|
44
|
+
|
|
45
|
+
// Thresholds
|
|
46
|
+
thresholds: {
|
|
47
|
+
lines: 80,
|
|
48
|
+
functions: 80,
|
|
49
|
+
branches: 80,
|
|
50
|
+
statements: 80,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Providers
|
|
58
|
+
|
|
59
|
+
### V8 (Default)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm i -D @vitest/coverage-v8
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- Faster, no pre-instrumentation
|
|
66
|
+
- Uses V8's native coverage
|
|
67
|
+
- Recommended for most projects
|
|
68
|
+
|
|
69
|
+
### Istanbul
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm i -D @vitest/coverage-istanbul
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- Pre-instruments code
|
|
76
|
+
- Works in any JS runtime
|
|
77
|
+
- More overhead but widely compatible
|
|
78
|
+
|
|
79
|
+
## Reporters
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
coverage: {
|
|
83
|
+
reporter: [
|
|
84
|
+
'text', // Terminal output
|
|
85
|
+
'text-summary', // Summary only
|
|
86
|
+
'json', // JSON file
|
|
87
|
+
'html', // HTML report
|
|
88
|
+
'lcov', // For CI tools
|
|
89
|
+
'cobertura', // XML format
|
|
90
|
+
],
|
|
91
|
+
reportsDirectory: './coverage',
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Thresholds
|
|
96
|
+
|
|
97
|
+
Fail tests if coverage is below threshold:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
coverage: {
|
|
101
|
+
thresholds: {
|
|
102
|
+
// Global thresholds
|
|
103
|
+
lines: 80,
|
|
104
|
+
functions: 75,
|
|
105
|
+
branches: 70,
|
|
106
|
+
statements: 80,
|
|
107
|
+
|
|
108
|
+
// Per-file thresholds
|
|
109
|
+
perFile: true,
|
|
110
|
+
|
|
111
|
+
// Auto-update thresholds (for gradual improvement)
|
|
112
|
+
autoUpdate: true,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Ignoring Code
|
|
118
|
+
|
|
119
|
+
### V8
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
/* v8 ignore next -- @preserve */
|
|
123
|
+
function ignored() {
|
|
124
|
+
return 'not covered'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* v8 ignore start -- @preserve */
|
|
128
|
+
// All code here ignored
|
|
129
|
+
/* v8 ignore stop -- @preserve */
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Istanbul
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
/* istanbul ignore next -- @preserve */
|
|
136
|
+
function ignored() {}
|
|
137
|
+
|
|
138
|
+
/* istanbul ignore if -- @preserve */
|
|
139
|
+
if (condition) {
|
|
140
|
+
// ignored
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Note: `@preserve` keeps comments through esbuild.
|
|
145
|
+
|
|
146
|
+
## Package.json Scripts
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"scripts": {
|
|
151
|
+
"test": "vitest",
|
|
152
|
+
"test:coverage": "vitest run --coverage",
|
|
153
|
+
"test:coverage:watch": "vitest --coverage"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Vitest UI Coverage
|
|
159
|
+
|
|
160
|
+
Enable HTML coverage in Vitest UI:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
coverage: {
|
|
164
|
+
enabled: true,
|
|
165
|
+
reporter: ['text', 'html'],
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Run with `vitest --ui` to view coverage visually.
|
|
170
|
+
|
|
171
|
+
## CI Integration
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
# GitHub Actions
|
|
175
|
+
- name: Run tests with coverage
|
|
176
|
+
run: npm run test:coverage
|
|
177
|
+
|
|
178
|
+
- name: Upload coverage to Codecov
|
|
179
|
+
uses: codecov/codecov-action@v3
|
|
180
|
+
with:
|
|
181
|
+
files: ./coverage/lcov.info
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Coverage with Sharding
|
|
185
|
+
|
|
186
|
+
Merge coverage from sharded runs:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
vitest run --shard=1/3 --coverage --reporter=blob
|
|
190
|
+
vitest run --shard=2/3 --coverage --reporter=blob
|
|
191
|
+
vitest run --shard=3/3 --coverage --reporter=blob
|
|
192
|
+
|
|
193
|
+
vitest --merge-reports --coverage --reporter=json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Key Points
|
|
197
|
+
|
|
198
|
+
- V8 is faster, Istanbul is more compatible
|
|
199
|
+
- Use `--coverage` flag or `coverage.enabled: true`
|
|
200
|
+
- Include `all: true` to see uncovered files
|
|
201
|
+
- Set thresholds to enforce minimum coverage
|
|
202
|
+
- Use `@preserve` comment to keep ignore hints
|
|
203
|
+
|
|
204
|
+
<!--
|
|
205
|
+
Source references:
|
|
206
|
+
- https://vitest.dev/guide/coverage.html
|
|
207
|
+
-->
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-filtering
|
|
3
|
+
description: Filter tests by name, file patterns, and tags
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test Filtering
|
|
7
|
+
|
|
8
|
+
## CLI Filtering
|
|
9
|
+
|
|
10
|
+
### By File Path
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Run files containing "user"
|
|
14
|
+
vitest user
|
|
15
|
+
|
|
16
|
+
# Multiple patterns
|
|
17
|
+
vitest user auth
|
|
18
|
+
|
|
19
|
+
# Specific file
|
|
20
|
+
vitest src/user.test.ts
|
|
21
|
+
|
|
22
|
+
# By line number
|
|
23
|
+
vitest src/user.test.ts:25
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### By Test Name
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Tests matching pattern
|
|
30
|
+
vitest -t "login"
|
|
31
|
+
vitest --testNamePattern "should.*work"
|
|
32
|
+
|
|
33
|
+
# Regex patterns
|
|
34
|
+
vitest -t "/user|auth/"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Changed Files
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Uncommitted changes
|
|
41
|
+
vitest --changed
|
|
42
|
+
|
|
43
|
+
# Since specific commit
|
|
44
|
+
vitest --changed HEAD~1
|
|
45
|
+
vitest --changed abc123
|
|
46
|
+
|
|
47
|
+
# Since branch
|
|
48
|
+
vitest --changed origin/main
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Related Files
|
|
52
|
+
|
|
53
|
+
Run tests that import specific files:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
vitest related src/utils.ts src/api.ts --run
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Useful with lint-staged:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// .lintstagedrc.js
|
|
63
|
+
export default {
|
|
64
|
+
'*.{ts,tsx}': 'vitest related --run',
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Focus Tests (.only)
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
test.only('only this runs', () => {})
|
|
72
|
+
|
|
73
|
+
describe.only('only this suite', () => {
|
|
74
|
+
test('runs', () => {})
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In CI, `.only` throws error unless configured:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
defineConfig({
|
|
82
|
+
test: {
|
|
83
|
+
allowOnly: true, // Allow .only in CI
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Skip Tests
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
test.skip('skipped', () => {})
|
|
92
|
+
|
|
93
|
+
// Conditional
|
|
94
|
+
test.skipIf(process.env.CI)('not in CI', () => {})
|
|
95
|
+
test.runIf(!process.env.CI)('local only', () => {})
|
|
96
|
+
|
|
97
|
+
// Dynamic skip
|
|
98
|
+
test('dynamic', ({ skip }) => {
|
|
99
|
+
skip(someCondition, 'reason')
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Tags
|
|
104
|
+
|
|
105
|
+
Filter by custom tags:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
test('database test', { tags: ['db'] }, () => {})
|
|
109
|
+
test('slow test', { tags: ['slow', 'integration'] }, () => {})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Run tagged tests:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
vitest --tags db
|
|
116
|
+
vitest --tags "db,slow" # OR
|
|
117
|
+
vitest --tags db --tags slow # OR
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Configure allowed tags:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
defineConfig({
|
|
124
|
+
test: {
|
|
125
|
+
tags: ['db', 'slow', 'integration'],
|
|
126
|
+
strictTags: true, // Fail on unknown tags
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Include/Exclude Patterns
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
defineConfig({
|
|
135
|
+
test: {
|
|
136
|
+
// Test file patterns
|
|
137
|
+
include: ['**/*.{test,spec}.{ts,tsx}'],
|
|
138
|
+
|
|
139
|
+
// Exclude patterns
|
|
140
|
+
exclude: [
|
|
141
|
+
'**/node_modules/**',
|
|
142
|
+
'**/e2e/**',
|
|
143
|
+
'**/*.skip.test.ts',
|
|
144
|
+
],
|
|
145
|
+
|
|
146
|
+
// Include source for in-source testing
|
|
147
|
+
includeSource: ['src/**/*.ts'],
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Watch Mode Filtering
|
|
153
|
+
|
|
154
|
+
In watch mode, press:
|
|
155
|
+
- `p` - Filter by filename pattern
|
|
156
|
+
- `t` - Filter by test name pattern
|
|
157
|
+
- `a` - Run all tests
|
|
158
|
+
- `f` - Run only failed tests
|
|
159
|
+
|
|
160
|
+
## Projects Filtering
|
|
161
|
+
|
|
162
|
+
Run specific project:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
vitest --project unit
|
|
166
|
+
vitest --project integration --project e2e
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Environment-based Filtering
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const isDev = process.env.NODE_ENV === 'development'
|
|
173
|
+
const isCI = process.env.CI
|
|
174
|
+
|
|
175
|
+
describe.skipIf(isCI)('local only tests', () => {})
|
|
176
|
+
describe.runIf(isDev)('dev tests', () => {})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Combining Filters
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# File pattern + test name + changed
|
|
183
|
+
vitest user -t "login" --changed
|
|
184
|
+
|
|
185
|
+
# Related files + run mode
|
|
186
|
+
vitest related src/auth.ts --run
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## List Tests Without Running
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
vitest list # Show all test names
|
|
193
|
+
vitest list -t "user" # Filter by name
|
|
194
|
+
vitest list --filesOnly # Show only file paths
|
|
195
|
+
vitest list --json # JSON output
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Key Points
|
|
199
|
+
|
|
200
|
+
- Use `-t` for test name pattern filtering
|
|
201
|
+
- `--changed` runs only tests affected by changes
|
|
202
|
+
- `--related` runs tests importing specific files
|
|
203
|
+
- Tags provide semantic test grouping
|
|
204
|
+
- Use `.only` for debugging, but configure CI to reject it
|
|
205
|
+
- Watch mode has interactive filtering
|
|
206
|
+
|
|
207
|
+
<!--
|
|
208
|
+
Source references:
|
|
209
|
+
- https://vitest.dev/guide/filtering.html
|
|
210
|
+
- https://vitest.dev/guide/cli.html
|
|
211
|
+
-->
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mocking
|
|
3
|
+
description: Mock functions, modules, timers, and dates with vi utilities
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Mocking
|
|
7
|
+
|
|
8
|
+
## Mock Functions
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { expect, vi } from 'vitest'
|
|
12
|
+
|
|
13
|
+
// Create mock function
|
|
14
|
+
const fn = vi.fn()
|
|
15
|
+
fn('hello')
|
|
16
|
+
|
|
17
|
+
expect(fn).toHaveBeenCalled()
|
|
18
|
+
expect(fn).toHaveBeenCalledWith('hello')
|
|
19
|
+
|
|
20
|
+
// With implementation
|
|
21
|
+
const add = vi.fn((a, b) => a + b)
|
|
22
|
+
expect(add(1, 2)).toBe(3)
|
|
23
|
+
|
|
24
|
+
// Mock return values
|
|
25
|
+
fn.mockReturnValue(42)
|
|
26
|
+
fn.mockReturnValueOnce(1).mockReturnValueOnce(2)
|
|
27
|
+
fn.mockResolvedValue({ data: true })
|
|
28
|
+
fn.mockRejectedValue(new Error('fail'))
|
|
29
|
+
|
|
30
|
+
// Mock implementation
|
|
31
|
+
fn.mockImplementation((x) => x * 2)
|
|
32
|
+
fn.mockImplementationOnce(() => 'first call')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Spying on Objects
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const cart = {
|
|
39
|
+
getTotal: () => 100,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const spy = vi.spyOn(cart, 'getTotal')
|
|
43
|
+
cart.getTotal()
|
|
44
|
+
|
|
45
|
+
expect(spy).toHaveBeenCalled()
|
|
46
|
+
|
|
47
|
+
// Mock implementation
|
|
48
|
+
spy.mockReturnValue(200)
|
|
49
|
+
expect(cart.getTotal()).toBe(200)
|
|
50
|
+
|
|
51
|
+
// Restore original
|
|
52
|
+
spy.mockRestore()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Module Mocking
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// vi.mock is hoisted to top of file
|
|
59
|
+
vi.mock('./api', () => ({
|
|
60
|
+
fetchUser: vi.fn(() => ({ id: 1, name: 'Mock' })),
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
import { fetchUser } from './api'
|
|
64
|
+
|
|
65
|
+
test('mocked module', () => {
|
|
66
|
+
expect(fetchUser()).toEqual({ id: 1, name: 'Mock' })
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Partial Mock
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
vi.mock('./utils', async (importOriginal) => {
|
|
74
|
+
const actual = await importOriginal()
|
|
75
|
+
return {
|
|
76
|
+
...actual,
|
|
77
|
+
specificFunction: vi.fn(),
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Auto-mock with Spy
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Keep implementation but spy on calls
|
|
86
|
+
vi.mock('./calculator', { spy: true })
|
|
87
|
+
|
|
88
|
+
import { add } from './calculator'
|
|
89
|
+
|
|
90
|
+
test('spy on module', () => {
|
|
91
|
+
const result = add(1, 2) // Real implementation
|
|
92
|
+
expect(result).toBe(3)
|
|
93
|
+
expect(add).toHaveBeenCalledWith(1, 2)
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Manual Mocks (__mocks__)
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
src/
|
|
101
|
+
__mocks__/
|
|
102
|
+
axios.ts # Mocks 'axios'
|
|
103
|
+
api/
|
|
104
|
+
__mocks__/
|
|
105
|
+
client.ts # Mocks './client'
|
|
106
|
+
client.ts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// Just call vi.mock with no factory
|
|
111
|
+
vi.mock('axios')
|
|
112
|
+
vi.mock('./api/client')
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Dynamic Mocking (vi.doMock)
|
|
116
|
+
|
|
117
|
+
Not hoisted - use for dynamic imports:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
test('dynamic mock', async () => {
|
|
121
|
+
vi.doMock('./config', () => ({
|
|
122
|
+
apiUrl: 'http://test.local',
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
const { apiUrl } = await import('./config')
|
|
126
|
+
expect(apiUrl).toBe('http://test.local')
|
|
127
|
+
|
|
128
|
+
vi.doUnmock('./config')
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Mock Timers
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { afterEach, beforeEach, vi } from 'vitest'
|
|
136
|
+
|
|
137
|
+
beforeEach(() => {
|
|
138
|
+
vi.useFakeTimers()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
afterEach(() => {
|
|
142
|
+
vi.useRealTimers()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('timers', () => {
|
|
146
|
+
const fn = vi.fn()
|
|
147
|
+
setTimeout(fn, 1000)
|
|
148
|
+
|
|
149
|
+
expect(fn).not.toHaveBeenCalled()
|
|
150
|
+
|
|
151
|
+
vi.advanceTimersByTime(1000)
|
|
152
|
+
expect(fn).toHaveBeenCalled()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Other timer methods
|
|
156
|
+
vi.runAllTimers() // Run all pending timers
|
|
157
|
+
vi.runOnlyPendingTimers() // Run only currently pending
|
|
158
|
+
vi.advanceTimersToNextTimer() // Advance to next timer
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Async Timer Methods
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
test('async timers', async () => {
|
|
165
|
+
vi.useFakeTimers()
|
|
166
|
+
|
|
167
|
+
let resolved = false
|
|
168
|
+
setTimeout(() => Promise.resolve().then(() => { resolved = true }), 100)
|
|
169
|
+
|
|
170
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
171
|
+
expect(resolved).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Mock Dates
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
vi.setSystemTime(new Date('2024-01-01'))
|
|
179
|
+
expect(new Date().getFullYear()).toBe(2024)
|
|
180
|
+
|
|
181
|
+
vi.useRealTimers() // Restore
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Mock Globals
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
vi.stubGlobal('fetch', vi.fn(() =>
|
|
188
|
+
Promise.resolve({ json: () => ({ data: 'mock' }) })
|
|
189
|
+
))
|
|
190
|
+
|
|
191
|
+
// Restore
|
|
192
|
+
vi.unstubAllGlobals()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Mock Environment Variables
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
vi.stubEnv('API_KEY', 'test-key')
|
|
199
|
+
expect(import.meta.env.API_KEY).toBe('test-key')
|
|
200
|
+
|
|
201
|
+
// Restore
|
|
202
|
+
vi.unstubAllEnvs()
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Clearing Mocks
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const fn = vi.fn()
|
|
209
|
+
fn()
|
|
210
|
+
|
|
211
|
+
fn.mockClear() // Clear call history
|
|
212
|
+
fn.mockReset() // Clear history + implementation
|
|
213
|
+
fn.mockRestore() // Restore original (for spies)
|
|
214
|
+
|
|
215
|
+
// Global
|
|
216
|
+
vi.clearAllMocks()
|
|
217
|
+
vi.resetAllMocks()
|
|
218
|
+
vi.restoreAllMocks()
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Config Auto-Reset
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// vitest.config.ts
|
|
225
|
+
defineConfig({
|
|
226
|
+
test: {
|
|
227
|
+
clearMocks: true, // Clear before each test
|
|
228
|
+
mockReset: true, // Reset before each test
|
|
229
|
+
restoreMocks: true, // Restore after each test
|
|
230
|
+
unstubEnvs: true, // Restore env vars
|
|
231
|
+
unstubGlobals: true, // Restore globals
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Hoisted Variables for Mocks
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const mockFn = vi.hoisted(() => vi.fn())
|
|
240
|
+
|
|
241
|
+
vi.mock('./module', () => ({
|
|
242
|
+
getData: mockFn,
|
|
243
|
+
}))
|
|
244
|
+
|
|
245
|
+
import { getData } from './module'
|
|
246
|
+
|
|
247
|
+
test('hoisted mock', () => {
|
|
248
|
+
mockFn.mockReturnValue('test')
|
|
249
|
+
expect(getData()).toBe('test')
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Key Points
|
|
254
|
+
|
|
255
|
+
- `vi.mock` is hoisted - called before imports
|
|
256
|
+
- Use `vi.doMock` for dynamic, non-hoisted mocking
|
|
257
|
+
- Always restore mocks to avoid test pollution
|
|
258
|
+
- Use `{ spy: true }` to keep implementation but track calls
|
|
259
|
+
- `vi.hoisted` lets you reference variables in mock factories
|
|
260
|
+
|
|
261
|
+
<!--
|
|
262
|
+
Source references:
|
|
263
|
+
- https://vitest.dev/guide/mocking.html
|
|
264
|
+
- https://vitest.dev/api/vi.html
|
|
265
|
+
-->
|