@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,529 @@
|
|
|
1
|
+
# 1.9 Vitest-Specific Features
|
|
2
|
+
|
|
3
|
+
Vitest provides powerful features beyond basic testing. Use them to write better, more maintainable tests.
|
|
4
|
+
|
|
5
|
+
## Test Filtering
|
|
6
|
+
|
|
7
|
+
Use `.only`, `.skip`, and `.todo` to control test execution during development.
|
|
8
|
+
|
|
9
|
+
**✅ Correct: focusing on specific tests**
|
|
10
|
+
```ts
|
|
11
|
+
describe('UserService', () => {
|
|
12
|
+
it.only('should create user', () => {
|
|
13
|
+
// Only this test runs
|
|
14
|
+
const user = createUser({ name: 'John' });
|
|
15
|
+
expect(user.name).toBe('John');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should update user', () => {
|
|
19
|
+
// Skipped while .only is active
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it.skip('should delete user', () => {
|
|
23
|
+
// Temporarily disabled
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it.todo('should restore deleted user');
|
|
27
|
+
// Placeholder for future test
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**⚠️ Warning: Remove `.only` before committing**
|
|
32
|
+
```ts
|
|
33
|
+
// DON'T commit this!
|
|
34
|
+
it.only('should work', () => {
|
|
35
|
+
// Other tests won't run in CI
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Conditional Tests
|
|
40
|
+
|
|
41
|
+
Run tests conditionally based on environment.
|
|
42
|
+
|
|
43
|
+
**✅ Correct: platform-specific tests**
|
|
44
|
+
```ts
|
|
45
|
+
describe('FileSystem', () => {
|
|
46
|
+
it.runIf(process.platform === 'win32')('should handle Windows paths', () => {
|
|
47
|
+
expect(normalizePath('C:\\Users\\test')).toBe('C:/Users/test');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it.skipIf(process.platform === 'win32')('should handle Unix paths', () => {
|
|
51
|
+
expect(normalizePath('/home/test')).toBe('/home/test');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Concurrent Tests
|
|
57
|
+
|
|
58
|
+
Run independent tests in parallel for faster execution.
|
|
59
|
+
|
|
60
|
+
**✅ Correct: concurrent test execution**
|
|
61
|
+
```ts
|
|
62
|
+
describe.concurrent('API endpoints', () => {
|
|
63
|
+
it('should fetch users', async () => {
|
|
64
|
+
const users = await api.getUsers();
|
|
65
|
+
expect(users).toHaveLength(10);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should fetch products', async () => {
|
|
69
|
+
const products = await api.getProducts();
|
|
70
|
+
expect(products).toHaveLength(20);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should fetch orders', async () => {
|
|
74
|
+
const orders = await api.getOrders();
|
|
75
|
+
expect(orders).toHaveLength(5);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
// All three tests run in parallel
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**⚠️ Caution: Only use for isolated tests**
|
|
82
|
+
```ts
|
|
83
|
+
describe.concurrent('Database tests', () => {
|
|
84
|
+
// ❌ BAD: These tests share state and will interfere!
|
|
85
|
+
it('should create user', async () => {
|
|
86
|
+
await db.insert({ id: 1, name: 'Alice' });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should count users', async () => {
|
|
90
|
+
const count = await db.count();
|
|
91
|
+
expect(count).toBe(1); // Flaky! Depends on other test
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Test Context
|
|
97
|
+
|
|
98
|
+
Use `test.extend` to create custom test fixtures.
|
|
99
|
+
|
|
100
|
+
**✅ Correct: reusable test fixtures**
|
|
101
|
+
```ts
|
|
102
|
+
import { test as base, expect } from 'vitest';
|
|
103
|
+
|
|
104
|
+
interface TestContext {
|
|
105
|
+
userService: UserService;
|
|
106
|
+
db: Database;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const test = base.extend<TestContext>({
|
|
110
|
+
userService: async ({}, use) => {
|
|
111
|
+
const service = new UserService();
|
|
112
|
+
await use(service);
|
|
113
|
+
await service.cleanup();
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
db: async ({}, use) => {
|
|
117
|
+
const db = await createTestDatabase();
|
|
118
|
+
await use(db);
|
|
119
|
+
await db.close();
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('should create user', async ({ userService, db }) => {
|
|
124
|
+
const user = await userService.create({ name: 'John' });
|
|
125
|
+
const saved = await db.findById(user.id);
|
|
126
|
+
expect(saved).toEqual(user);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Mocking Modules
|
|
131
|
+
|
|
132
|
+
Use `vi.mock()` to mock entire modules.
|
|
133
|
+
|
|
134
|
+
**✅ Correct: mocking external dependency**
|
|
135
|
+
```ts
|
|
136
|
+
import { sendEmail } from './email-service';
|
|
137
|
+
|
|
138
|
+
vi.mock('./email-service', () => ({
|
|
139
|
+
sendEmail: vi.fn().mockResolvedValue({ success: true }),
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
it('should send welcome email', async () => {
|
|
143
|
+
await onUserSignup({ email: 'user@example.com' });
|
|
144
|
+
|
|
145
|
+
expect(sendEmail).toHaveBeenCalledWith({
|
|
146
|
+
to: 'user@example.com',
|
|
147
|
+
template: 'welcome',
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**✅ Correct: partial module mock**
|
|
153
|
+
```ts
|
|
154
|
+
vi.mock('./utils', async (importOriginal) => {
|
|
155
|
+
const actual = await importOriginal();
|
|
156
|
+
return {
|
|
157
|
+
...actual,
|
|
158
|
+
fetchData: vi.fn(), // Mock only fetchData
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Spy On Methods
|
|
164
|
+
|
|
165
|
+
Use `vi.spyOn()` to spy on object methods.
|
|
166
|
+
|
|
167
|
+
**✅ Correct: spying on console methods**
|
|
168
|
+
```ts
|
|
169
|
+
it('should log error message', () => {
|
|
170
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation();
|
|
171
|
+
|
|
172
|
+
logger.error('Something went wrong');
|
|
173
|
+
|
|
174
|
+
expect(consoleSpy).toHaveBeenCalledWith('[ERROR]', 'Something went wrong');
|
|
175
|
+
|
|
176
|
+
consoleSpy.mockRestore();
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Fake Timers
|
|
181
|
+
|
|
182
|
+
Control time in tests using fake timers.
|
|
183
|
+
|
|
184
|
+
**✅ Correct: testing debounce**
|
|
185
|
+
```ts
|
|
186
|
+
describe('debounce', () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
vi.useFakeTimers();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
afterEach(() => {
|
|
192
|
+
vi.useRealTimers();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should delay function execution', () => {
|
|
196
|
+
const callback = vi.fn();
|
|
197
|
+
const debounced = debounce(callback, 1000);
|
|
198
|
+
|
|
199
|
+
debounced();
|
|
200
|
+
expect(callback).not.toHaveBeenCalled();
|
|
201
|
+
|
|
202
|
+
vi.advanceTimersByTime(1000);
|
|
203
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should reset timer on subsequent calls', () => {
|
|
207
|
+
const callback = vi.fn();
|
|
208
|
+
const debounced = debounce(callback, 1000);
|
|
209
|
+
|
|
210
|
+
debounced();
|
|
211
|
+
vi.advanceTimersByTime(500);
|
|
212
|
+
debounced(); // Resets timer
|
|
213
|
+
vi.advanceTimersByTime(500);
|
|
214
|
+
expect(callback).not.toHaveBeenCalled();
|
|
215
|
+
|
|
216
|
+
vi.advanceTimersByTime(500);
|
|
217
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**✅ Correct: testing intervals**
|
|
223
|
+
```ts
|
|
224
|
+
it('should poll every second', () => {
|
|
225
|
+
vi.useFakeTimers();
|
|
226
|
+
|
|
227
|
+
const callback = vi.fn();
|
|
228
|
+
const poller = new Poller(callback, 1000);
|
|
229
|
+
|
|
230
|
+
poller.start();
|
|
231
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
232
|
+
|
|
233
|
+
vi.advanceTimersByTime(3000);
|
|
234
|
+
expect(callback).toHaveBeenCalledTimes(4); // Initial + 3 intervals
|
|
235
|
+
|
|
236
|
+
poller.stop();
|
|
237
|
+
vi.useRealTimers();
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Test Each
|
|
242
|
+
|
|
243
|
+
Use `test.each()` for parameterized tests.
|
|
244
|
+
|
|
245
|
+
**✅ Correct: testing multiple inputs**
|
|
246
|
+
```ts
|
|
247
|
+
test.each([
|
|
248
|
+
{ input: 2, expected: 4 },
|
|
249
|
+
{ input: 3, expected: 9 },
|
|
250
|
+
{ input: 4, expected: 16 },
|
|
251
|
+
])('square($input) should equal $expected', ({ input, expected }) => {
|
|
252
|
+
expect(square(input)).toBe(expected);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**✅ Correct: array syntax**
|
|
257
|
+
```ts
|
|
258
|
+
test.each([
|
|
259
|
+
[1, 1],
|
|
260
|
+
[2, 4],
|
|
261
|
+
[3, 9],
|
|
262
|
+
])('square(%i) = %i', (input, expected) => {
|
|
263
|
+
expect(square(input)).toBe(expected);
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Coverage
|
|
268
|
+
|
|
269
|
+
Generate code coverage reports to identify untested code.
|
|
270
|
+
|
|
271
|
+
**vitest.config.ts:**
|
|
272
|
+
```ts
|
|
273
|
+
export default {
|
|
274
|
+
test: {
|
|
275
|
+
coverage: {
|
|
276
|
+
provider: 'v8',
|
|
277
|
+
reporter: ['text', 'html', 'lcov'],
|
|
278
|
+
exclude: [
|
|
279
|
+
'node_modules/',
|
|
280
|
+
'test/',
|
|
281
|
+
'**/*.test.ts',
|
|
282
|
+
'**/*.config.ts',
|
|
283
|
+
],
|
|
284
|
+
thresholds: {
|
|
285
|
+
lines: 80,
|
|
286
|
+
functions: 80,
|
|
287
|
+
branches: 80,
|
|
288
|
+
statements: 80,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Run coverage:**
|
|
296
|
+
```bash
|
|
297
|
+
vitest --coverage
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**✅ Correct: focus on meaningful coverage**
|
|
301
|
+
```ts
|
|
302
|
+
// Don't chase 100% coverage blindly
|
|
303
|
+
// Focus on testing critical business logic
|
|
304
|
+
|
|
305
|
+
describe('PaymentProcessor', () => {
|
|
306
|
+
it('should process valid payment', () => {
|
|
307
|
+
// Critical path - must be tested
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should reject invalid payment', () => {
|
|
311
|
+
// Error path - must be tested
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle network timeout', () => {
|
|
315
|
+
// Edge case - important to test
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Watch Mode
|
|
321
|
+
|
|
322
|
+
Use watch mode for rapid test-driven development.
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
# Watch mode with UI
|
|
326
|
+
vitest --ui
|
|
327
|
+
|
|
328
|
+
# Watch mode in terminal
|
|
329
|
+
vitest --watch
|
|
330
|
+
|
|
331
|
+
# Watch specific files
|
|
332
|
+
vitest watch src/services
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Benchmarking
|
|
336
|
+
|
|
337
|
+
Use `bench()` to measure performance.
|
|
338
|
+
|
|
339
|
+
**✅ Correct: comparing implementations**
|
|
340
|
+
```ts
|
|
341
|
+
import { bench, describe } from 'vitest';
|
|
342
|
+
|
|
343
|
+
describe('Array operations', () => {
|
|
344
|
+
bench('for loop', () => {
|
|
345
|
+
const arr = Array.from({ length: 1000 }, (_, i) => i);
|
|
346
|
+
let sum = 0;
|
|
347
|
+
for (let i = 0; i < arr.length; i++) {
|
|
348
|
+
sum += arr[i];
|
|
349
|
+
}
|
|
350
|
+
return sum;
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
bench('forEach', () => {
|
|
354
|
+
const arr = Array.from({ length: 1000 }, (_, i) => i);
|
|
355
|
+
let sum = 0;
|
|
356
|
+
arr.forEach(n => sum += n);
|
|
357
|
+
return sum;
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
bench('reduce', () => {
|
|
361
|
+
const arr = Array.from({ length: 1000 }, (_, i) => i);
|
|
362
|
+
return arr.reduce((sum, n) => sum + n, 0);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## In-Source Testing
|
|
368
|
+
|
|
369
|
+
Define tests alongside implementation code.
|
|
370
|
+
|
|
371
|
+
**example.ts:**
|
|
372
|
+
```ts
|
|
373
|
+
export function add(a: number, b: number): number {
|
|
374
|
+
return a + b;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (import.meta.vitest) {
|
|
378
|
+
const { it, expect } = import.meta.vitest;
|
|
379
|
+
|
|
380
|
+
it('should add two numbers', () => {
|
|
381
|
+
expect(add(2, 3)).toBe(5);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should handle negative numbers', () => {
|
|
385
|
+
expect(add(-2, 3)).toBe(1);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**vitest.config.ts:**
|
|
391
|
+
```ts
|
|
392
|
+
export default {
|
|
393
|
+
test: {
|
|
394
|
+
includeSource: ['src/**/*.ts'],
|
|
395
|
+
},
|
|
396
|
+
define: {
|
|
397
|
+
'import.meta.vitest': 'undefined',
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
## Type Testing
|
|
403
|
+
|
|
404
|
+
Test TypeScript types using `expectTypeOf`.
|
|
405
|
+
|
|
406
|
+
**✅ Correct: type assertions**
|
|
407
|
+
```ts
|
|
408
|
+
import { expectTypeOf } from 'vitest';
|
|
409
|
+
|
|
410
|
+
it('should have correct return type', () => {
|
|
411
|
+
const result = fetchUser('123');
|
|
412
|
+
|
|
413
|
+
expectTypeOf(result).toEqualTypeOf<Promise<User>>();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('should accept correct parameter types', () => {
|
|
417
|
+
expectTypeOf(createUser).parameter(0).toMatchTypeOf<UserInput>();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should infer correct generic type', () => {
|
|
421
|
+
const users = [{ id: 1, name: 'Alice' }];
|
|
422
|
+
|
|
423
|
+
expectTypeOf(users).toEqualTypeOf<Array<{ id: number; name: string }>>();
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## Setup Files
|
|
428
|
+
|
|
429
|
+
Configure global test setup and teardown.
|
|
430
|
+
|
|
431
|
+
**vitest.config.ts:**
|
|
432
|
+
```ts
|
|
433
|
+
export default {
|
|
434
|
+
test: {
|
|
435
|
+
setupFiles: ['./test/setup.ts'],
|
|
436
|
+
globalSetup: ['./test/global-setup.ts'],
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**test/setup.ts (runs before each test file):**
|
|
442
|
+
```ts
|
|
443
|
+
import { beforeEach, afterEach } from 'vitest';
|
|
444
|
+
|
|
445
|
+
beforeEach(() => {
|
|
446
|
+
// Runs before each test
|
|
447
|
+
vi.clearAllMocks();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
afterEach(() => {
|
|
451
|
+
// Runs after each test
|
|
452
|
+
vi.restoreAllMocks();
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**test/global-setup.ts (runs once before all tests):**
|
|
457
|
+
```ts
|
|
458
|
+
export async function setup() {
|
|
459
|
+
// Start test database, etc.
|
|
460
|
+
console.log('Starting test environment...');
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export async function teardown() {
|
|
464
|
+
// Clean up global resources
|
|
465
|
+
console.log('Cleaning up test environment...');
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Environment
|
|
470
|
+
|
|
471
|
+
Specify different test environments for different tests.
|
|
472
|
+
|
|
473
|
+
**✅ Correct: browser environment for DOM tests**
|
|
474
|
+
```ts
|
|
475
|
+
/**
|
|
476
|
+
* @vitest-environment jsdom
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
import { render } from '@testing-library/react';
|
|
480
|
+
|
|
481
|
+
it('should render component', () => {
|
|
482
|
+
const { getByText } = render(<Button>Click me</Button>);
|
|
483
|
+
expect(getByText('Click me')).toBeInTheDocument();
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**✅ Correct: node environment for API tests**
|
|
488
|
+
```ts
|
|
489
|
+
/**
|
|
490
|
+
* @vitest-environment node
|
|
491
|
+
*/
|
|
492
|
+
|
|
493
|
+
it('should read file', async () => {
|
|
494
|
+
const content = await fs.readFile('./test.txt', 'utf-8');
|
|
495
|
+
expect(content).toContain('test data');
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Retry Flaky Tests
|
|
500
|
+
|
|
501
|
+
Retry flaky tests automatically.
|
|
502
|
+
|
|
503
|
+
**✅ Correct: retry configuration**
|
|
504
|
+
```ts
|
|
505
|
+
// vitest.config.ts
|
|
506
|
+
export default {
|
|
507
|
+
test: {
|
|
508
|
+
retry: 2, // Retry failed tests up to 2 times
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**✅ Correct: per-test retry**
|
|
514
|
+
```ts
|
|
515
|
+
it('flaky network test', { retry: 3 }, async () => {
|
|
516
|
+
const data = await fetchFromUnreliableAPI();
|
|
517
|
+
expect(data).toBeDefined();
|
|
518
|
+
});
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**⚠️ Better: Fix the flakiness instead of retrying**
|
|
522
|
+
```ts
|
|
523
|
+
// Instead of retrying, mock the unreliable dependency
|
|
524
|
+
it('network test', async () => {
|
|
525
|
+
const mockFetch = vi.fn().mockResolvedValue({ data: 'test' });
|
|
526
|
+
const data = await fetchData(mockFetch);
|
|
527
|
+
expect(data).toBeDefined();
|
|
528
|
+
});
|
|
529
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-design-guidelines
|
|
3
|
+
description: Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
|
|
4
|
+
argument-hint: <file-or-pattern>
|
|
5
|
+
metadata:
|
|
6
|
+
author: vercel
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Web Interface Guidelines
|
|
11
|
+
|
|
12
|
+
Review files for compliance with Web Interface Guidelines.
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
1. Fetch the latest guidelines from the source URL below
|
|
17
|
+
2. Read the specified files (or prompt user for files/pattern)
|
|
18
|
+
3. Check against all rules in the fetched guidelines
|
|
19
|
+
4. Output findings in the terse `file:line` format
|
|
20
|
+
|
|
21
|
+
## Guidelines Source
|
|
22
|
+
|
|
23
|
+
Fetch fresh guidelines before each review:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and output format instructions.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
When a user provides a file or pattern argument:
|
|
34
|
+
1. Fetch guidelines from the source URL above
|
|
35
|
+
2. Read the specified files
|
|
36
|
+
3. Apply all rules from the fetched guidelines
|
|
37
|
+
4. Output findings using the format specified in the guidelines
|
|
38
|
+
|
|
39
|
+
If no files specified, ask the user which files to review.
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|