start-vibing 3.0.8 → 3.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/package.json +1 -1
  2. package/template/.claude/CLAUDE.md +18 -3
  3. package/template/.claude/skills/api-docs/SKILL.md +206 -0
  4. package/template/.claude/skills/claude-seo/SKILL.md +84 -0
  5. package/template/.claude/skills/mongoose-patterns/SKILL.md +188 -0
  6. package/template/.claude/skills/playwright-testing/SKILL.md +251 -0
  7. package/template/.claude/skills/skill-creator/SKILL.md +106 -0
  8. package/template/.claude/skills/test-infrastructure/SKILL.md +242 -0
  9. package/template/.claude/agents/_archive/01-orchestration/agent-selector.md +0 -130
  10. package/template/.claude/agents/_archive/01-orchestration/checkpoint-manager.md +0 -142
  11. package/template/.claude/agents/_archive/01-orchestration/context-manager.md +0 -138
  12. package/template/.claude/agents/_archive/01-orchestration/error-recovery.md +0 -182
  13. package/template/.claude/agents/_archive/01-orchestration/orchestrator.md +0 -114
  14. package/template/.claude/agents/_archive/01-orchestration/parallel-coordinator.md +0 -141
  15. package/template/.claude/agents/_archive/01-orchestration/task-decomposer.md +0 -121
  16. package/template/.claude/agents/_archive/01-orchestration/workflow-router.md +0 -119
  17. package/template/.claude/agents/_archive/02-typescript/bun-runtime-expert.md +0 -197
  18. package/template/.claude/agents/_archive/02-typescript/esm-resolver.md +0 -193
  19. package/template/.claude/agents/_archive/02-typescript/import-alias-enforcer.md +0 -158
  20. package/template/.claude/agents/_archive/02-typescript/ts-generics-helper.md +0 -183
  21. package/template/.claude/agents/_archive/02-typescript/ts-migration-helper.md +0 -238
  22. package/template/.claude/agents/_archive/02-typescript/ts-strict-checker.md +0 -180
  23. package/template/.claude/agents/_archive/02-typescript/ts-types-analyzer.md +0 -199
  24. package/template/.claude/agents/_archive/02-typescript/type-definition-writer.md +0 -187
  25. package/template/.claude/agents/_archive/02-typescript/zod-schema-designer.md +0 -212
  26. package/template/.claude/agents/_archive/02-typescript/zod-validator.md +0 -158
  27. package/template/.claude/agents/_archive/03-testing/playwright-assertions.md +0 -265
  28. package/template/.claude/agents/_archive/03-testing/playwright-e2e.md +0 -247
  29. package/template/.claude/agents/_archive/03-testing/playwright-fixtures.md +0 -234
  30. package/template/.claude/agents/_archive/03-testing/playwright-multi-viewport.md +0 -256
  31. package/template/.claude/agents/_archive/03-testing/playwright-page-objects.md +0 -247
  32. package/template/.claude/agents/_archive/03-testing/test-cleanup-manager.md +0 -248
  33. package/template/.claude/agents/_archive/03-testing/test-data-generator.md +0 -254
  34. package/template/.claude/agents/_archive/03-testing/tester-integration.md +0 -278
  35. package/template/.claude/agents/_archive/03-testing/tester-unit.md +0 -207
  36. package/template/.claude/agents/_archive/03-testing/vitest-config.md +0 -287
  37. package/template/.claude/agents/_archive/04-docker/container-health.md +0 -255
  38. package/template/.claude/agents/_archive/04-docker/deployment-validator.md +0 -225
  39. package/template/.claude/agents/_archive/04-docker/docker-compose-designer.md +0 -281
  40. package/template/.claude/agents/_archive/04-docker/docker-env-manager.md +0 -235
  41. package/template/.claude/agents/_archive/04-docker/docker-multi-stage.md +0 -241
  42. package/template/.claude/agents/_archive/04-docker/dockerfile-optimizer.md +0 -208
  43. package/template/.claude/agents/_archive/05-database/database-seeder.md +0 -273
  44. package/template/.claude/agents/_archive/05-database/mongodb-query-optimizer.md +0 -230
  45. package/template/.claude/agents/_archive/05-database/mongoose-aggregation.md +0 -306
  46. package/template/.claude/agents/_archive/05-database/mongoose-index-optimizer.md +0 -182
  47. package/template/.claude/agents/_archive/05-database/mongoose-schema-designer.md +0 -267
  48. package/template/.claude/agents/_archive/06-security/auth-session-validator.md +0 -68
  49. package/template/.claude/agents/_archive/06-security/input-sanitizer.md +0 -80
  50. package/template/.claude/agents/_archive/06-security/owasp-checker.md +0 -97
  51. package/template/.claude/agents/_archive/06-security/permission-auditor.md +0 -100
  52. package/template/.claude/agents/_archive/06-security/security-auditor.md +0 -84
  53. package/template/.claude/agents/_archive/06-security/sensitive-data-scanner.md +0 -83
  54. package/template/.claude/agents/_archive/07-documentation/api-documenter.md +0 -136
  55. package/template/.claude/agents/_archive/07-documentation/changelog-manager.md +0 -105
  56. package/template/.claude/agents/_archive/07-documentation/claude-md-compactor.md +0 -214
  57. package/template/.claude/agents/_archive/07-documentation/documenter.md +0 -184
  58. package/template/.claude/agents/_archive/07-documentation/domain-updater.md +0 -138
  59. package/template/.claude/agents/_archive/07-documentation/jsdoc-generator.md +0 -114
  60. package/template/.claude/agents/_archive/07-documentation/readme-generator.md +0 -135
  61. package/template/.claude/agents/_archive/08-git/branch-manager.md +0 -58
  62. package/template/.claude/agents/_archive/08-git/commit-manager.md +0 -78
  63. package/template/.claude/agents/_archive/09-quality/code-reviewer.md +0 -71
  64. package/template/.claude/agents/_archive/09-quality/quality-checker.md +0 -67
  65. package/template/.claude/agents/_archive/10-research/best-practices-finder.md +0 -89
  66. package/template/.claude/agents/_archive/10-research/competitor-analyzer.md +0 -106
  67. package/template/.claude/agents/_archive/10-research/pattern-researcher.md +0 -93
  68. package/template/.claude/agents/_archive/10-research/research-cache-manager.md +0 -76
  69. package/template/.claude/agents/_archive/10-research/research-web.md +0 -98
  70. package/template/.claude/agents/_archive/10-research/tech-evaluator.md +0 -101
  71. package/template/.claude/agents/_archive/11-ui-ux/accessibility-auditor.md +0 -136
  72. package/template/.claude/agents/_archive/11-ui-ux/design-system-enforcer.md +0 -125
  73. package/template/.claude/agents/_archive/11-ui-ux/skeleton-generator.md +0 -118
  74. package/template/.claude/agents/_archive/11-ui-ux/ui-desktop.md +0 -132
  75. package/template/.claude/agents/_archive/11-ui-ux/ui-mobile.md +0 -125
  76. package/template/.claude/agents/_archive/11-ui-ux/ui-tablet.md +0 -110
  77. package/template/.claude/agents/_archive/12-performance/api-latency-analyzer.md +0 -156
  78. package/template/.claude/agents/_archive/12-performance/bundle-analyzer.md +0 -113
  79. package/template/.claude/agents/_archive/12-performance/memory-leak-detector.md +0 -137
  80. package/template/.claude/agents/_archive/12-performance/performance-profiler.md +0 -115
  81. package/template/.claude/agents/_archive/12-performance/query-optimizer.md +0 -124
  82. package/template/.claude/agents/_archive/12-performance/render-optimizer.md +0 -154
  83. package/template/.claude/agents/_archive/_backup/analyzer.md +0 -134
  84. package/template/.claude/agents/_archive/_backup/code-reviewer.md +0 -279
  85. package/template/.claude/agents/_archive/_backup/commit-manager.md +0 -219
  86. package/template/.claude/agents/_archive/_backup/debugger.md +0 -280
  87. package/template/.claude/agents/_archive/_backup/documenter.md +0 -237
  88. package/template/.claude/agents/_archive/_backup/domain-updater.md +0 -197
  89. package/template/.claude/agents/_archive/_backup/final-validator.md +0 -169
  90. package/template/.claude/agents/_archive/_backup/orchestrator.md +0 -149
  91. package/template/.claude/agents/_archive/_backup/performance.md +0 -232
  92. package/template/.claude/agents/_archive/_backup/quality-checker.md +0 -240
  93. package/template/.claude/agents/_archive/_backup/research.md +0 -315
  94. package/template/.claude/agents/_archive/_backup/security-auditor.md +0 -192
  95. package/template/.claude/agents/_archive/_backup/tester.md +0 -566
  96. package/template/.claude/agents/_archive/_backup/ui-ux-reviewer.md +0 -247
@@ -0,0 +1,251 @@
1
+ ---
2
+ name: playwright-testing
3
+ description: "ALWAYS invoke when creating or editing Playwright E2E tests. Do NOT write E2E tests without checking Page Object Model, fixture design, and multi-viewport patterns first."
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Playwright Testing
8
+
9
+ Architecture patterns for Playwright E2E tests: Page Objects, fixtures, assertions, and multi-viewport testing.
10
+
11
+ ## Playwright Config
12
+
13
+ ```typescript
14
+ // playwright.config.ts
15
+ import { defineConfig, devices } from '@playwright/test';
16
+
17
+ export default defineConfig({
18
+ testDir: './tests/e2e',
19
+ fullyParallel: true,
20
+ forbidOnly: !!process.env['CI'],
21
+ retries: process.env['CI'] ? 2 : 0,
22
+ workers: process.env['CI'] ? 1 : undefined,
23
+ reporter: [['html'], ['list']],
24
+ use: {
25
+ baseURL: 'http://localhost:3000',
26
+ trace: 'on-first-retry',
27
+ screenshot: 'only-on-failure',
28
+ },
29
+ projects: [
30
+ { name: 'Desktop', use: { ...devices['Desktop Chrome'] } },
31
+ { name: 'Tablet', use: { ...devices['iPad Mini'] } },
32
+ { name: 'Mobile', use: { ...devices['iPhone 14'] } },
33
+ ],
34
+ webServer: {
35
+ command: 'bun run dev',
36
+ url: 'http://localhost:3000',
37
+ reuseExistingServer: !process.env['CI'],
38
+ },
39
+ });
40
+ ```
41
+
42
+ ## Page Object Model
43
+
44
+ ```typescript
45
+ // tests/e2e/pages/base.page.ts
46
+ import { type Page, type Locator, expect } from '@playwright/test';
47
+
48
+ export abstract class BasePage {
49
+ constructor(protected page: Page) {}
50
+
51
+ abstract readonly url: string;
52
+
53
+ async goto() {
54
+ await this.page.goto(this.url);
55
+ }
56
+
57
+ async waitForReady() {
58
+ await this.page.waitForLoadState('networkidle');
59
+ }
60
+
61
+ // Shared helpers
62
+ async getToast() {
63
+ return this.page.locator('[data-sonner-toast]');
64
+ }
65
+ }
66
+ ```
67
+
68
+ ```typescript
69
+ // tests/e2e/pages/login.page.ts
70
+ export class LoginPage extends BasePage {
71
+ readonly url = '/login';
72
+
73
+ // Locators (prefer data-testid, role, text)
74
+ readonly emailInput = this.page.getByLabel('Email');
75
+ readonly passwordInput = this.page.getByLabel('Password');
76
+ readonly submitButton = this.page.getByRole('button', { name: 'Sign in' });
77
+ readonly errorMessage = this.page.getByRole('alert');
78
+
79
+ async login(email: string, password: string) {
80
+ await this.emailInput.fill(email);
81
+ await this.passwordInput.fill(password);
82
+ await this.submitButton.click();
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Component Page Objects
88
+
89
+ ```typescript
90
+ // tests/e2e/pages/components/data-table.component.ts
91
+ export class DataTableComponent {
92
+ constructor(private container: Locator) {}
93
+
94
+ get rows() { return this.container.getByRole('row'); }
95
+ get headers() { return this.container.getByRole('columnheader'); }
96
+
97
+ async sortBy(column: string) {
98
+ await this.container.getByRole('columnheader', { name: column }).click();
99
+ }
100
+
101
+ async getRowCount() {
102
+ return this.rows.count() - 1; // Minus header
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Fixture Architecture
108
+
109
+ ```typescript
110
+ // tests/e2e/fixtures/index.ts
111
+ import { test as base } from '@playwright/test';
112
+ import { LoginPage } from '../pages/login.page';
113
+ import { DashboardPage } from '../pages/dashboard.page';
114
+
115
+ // Worker fixture — shared across tests in same worker
116
+ // Test fixture — fresh per test
117
+ type TestFixtures = {
118
+ loginPage: LoginPage;
119
+ dashboardPage: DashboardPage;
120
+ authenticatedPage: Page;
121
+ };
122
+
123
+ export const test = base.extend<TestFixtures>({
124
+ loginPage: async ({ page }, use) => {
125
+ await use(new LoginPage(page));
126
+ },
127
+ dashboardPage: async ({ page }, use) => {
128
+ await use(new DashboardPage(page));
129
+ },
130
+ authenticatedPage: async ({ page }, use) => {
131
+ // Setup: login
132
+ await page.goto('/login');
133
+ await page.getByLabel('Email').fill('test@example.com');
134
+ await page.getByLabel('Password').fill('Password123!');
135
+ await page.getByRole('button', { name: 'Sign in' }).click();
136
+ await page.waitForURL('/dashboard');
137
+ // Use the authenticated page
138
+ await use(page);
139
+ // Teardown: cleanup (automatic)
140
+ },
141
+ });
142
+
143
+ export { expect } from '@playwright/test';
144
+ ```
145
+
146
+ ### Auto-fixtures (run before every test)
147
+
148
+ ```typescript
149
+ export const test = base.extend<{ autoCleanup: void }>({
150
+ autoCleanup: [async ({ page }, use) => {
151
+ await use(); // Test runs here
152
+ // Cleanup after test
153
+ await page.evaluate(() => localStorage.clear());
154
+ }, { auto: true }],
155
+ });
156
+ ```
157
+
158
+ ## Assertion Reference
159
+
160
+ ```typescript
161
+ // Element assertions
162
+ await expect(locator).toBeVisible();
163
+ await expect(locator).toBeHidden();
164
+ await expect(locator).toBeEnabled();
165
+ await expect(locator).toBeDisabled();
166
+ await expect(locator).toBeChecked();
167
+ await expect(locator).toBeFocused();
168
+ await expect(locator).toHaveText('Expected text');
169
+ await expect(locator).toContainText('partial');
170
+ await expect(locator).toHaveValue('expected');
171
+ await expect(locator).toHaveAttribute('href', '/path');
172
+ await expect(locator).toHaveClass(/active/);
173
+ await expect(locator).toHaveCount(5);
174
+ await expect(locator).toHaveCSS('color', 'rgb(0, 0, 0)');
175
+
176
+ // Page assertions
177
+ await expect(page).toHaveURL('/expected-path');
178
+ await expect(page).toHaveURL(/\/users\/\d+/);
179
+ await expect(page).toHaveTitle('Page Title');
180
+
181
+ // Negation
182
+ await expect(locator).not.toBeVisible();
183
+
184
+ // Soft assertions (don't stop test)
185
+ await expect.soft(locator).toHaveText('text');
186
+
187
+ // Custom timeout
188
+ await expect(locator).toBeVisible({ timeout: 10000 });
189
+
190
+ // API response assertions
191
+ const response = await page.request.get('/api/users');
192
+ expect(response.ok()).toBeTruthy();
193
+ expect(response.status()).toBe(200);
194
+ ```
195
+
196
+ ## Multi-Viewport Testing
197
+
198
+ ```typescript
199
+ // tests/e2e/responsive/dashboard.spec.ts
200
+ import { test, expect } from '../fixtures';
201
+
202
+ const viewports = [
203
+ { name: 'Mobile', width: 375, height: 812 },
204
+ { name: 'Tablet', width: 768, height: 1024 },
205
+ { name: 'Desktop', width: 1280, height: 720 },
206
+ ];
207
+
208
+ for (const viewport of viewports) {
209
+ test.describe(`Dashboard - ${viewport.name}`, () => {
210
+ test.use({ viewport: { width: viewport.width, height: viewport.height } });
211
+
212
+ test('navigation is appropriate', async ({ page }) => {
213
+ await page.goto('/dashboard');
214
+
215
+ if (viewport.width < 768) {
216
+ // Mobile: bottom nav or hamburger
217
+ await expect(page.getByTestId('mobile-nav')).toBeVisible();
218
+ await expect(page.getByTestId('sidebar')).toBeHidden();
219
+ } else if (viewport.width < 1280) {
220
+ // Tablet: collapsible sidebar
221
+ await expect(page.getByTestId('sidebar')).toBeVisible();
222
+ } else {
223
+ // Desktop: full sidebar
224
+ await expect(page.getByTestId('sidebar')).toBeVisible();
225
+ await expect(page.getByTestId('search-bar')).toBeVisible();
226
+ }
227
+ });
228
+ });
229
+ }
230
+ ```
231
+
232
+ ## Selector Best Practices
233
+
234
+ | Priority | Selector | Example |
235
+ |----------|----------|---------|
236
+ | 1 | `getByRole` | `getByRole('button', { name: 'Submit' })` |
237
+ | 2 | `getByLabel` | `getByLabel('Email')` |
238
+ | 3 | `getByText` | `getByText('Welcome')` |
239
+ | 4 | `getByTestId` | `getByTestId('user-avatar')` |
240
+ | 5 | CSS (last resort) | `locator('.card:first-child')` |
241
+
242
+ ## Critical Rules
243
+
244
+ 1. **Page Object Model** — Every page/component gets a POM class extending BasePage
245
+ 2. **Fixtures over beforeEach** — Use `test.extend` for reusable setup/teardown
246
+ 3. **3 viewports** — Test Mobile (375), Tablet (768), Desktop (1280+) separately
247
+ 4. **Role-based selectors** — Prefer `getByRole`/`getByLabel` over CSS selectors
248
+ 5. **No hardcoded waits** — Use `waitForURL`, `waitForLoadState`, `expect` auto-retry
249
+ 6. **Trace on failure** — `trace: 'on-first-retry'` for debugging
250
+ 7. **Parallel by default** — `fullyParallel: true` unless tests share state
251
+ 8. **Worker fixtures** — Share expensive setup (auth, DB) across tests in same worker
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: skill-creator
3
+ description: "ALWAYS invoke when creating, editing, or optimizing Claude Code skills. Do NOT create skills without following the interview, draft, test, iterate loop first."
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Skill Creator
8
+
9
+ Meta-skill for creating new skills and iteratively improving them.
10
+
11
+ > Source: `anthropics/skills` (adapted for project conventions)
12
+
13
+ ## Creation Loop
14
+
15
+ 1. **Capture Intent** — What should this skill do? When should it trigger? What's the output format?
16
+ 2. **Interview & Research** — Ask about edge cases, input/output formats, dependencies
17
+ 3. **Write the SKILL.md** — See anatomy below
18
+ 4. **Test** — Create 2-3 realistic test prompts, run with/without skill
19
+ 5. **Evaluate** — Review outputs qualitatively and quantitatively
20
+ 6. **Iterate** — Improve based on feedback, repeat until satisfied
21
+ 7. **Optimize Description** — Generate trigger eval queries for better activation
22
+
23
+ ## Skill Anatomy
24
+
25
+ ```
26
+ skill-name/
27
+ ├── SKILL.md (required)
28
+ │ ├── YAML frontmatter (name, description, allowed-tools)
29
+ │ └── Markdown instructions
30
+ └── Optional resources
31
+ ├── scripts/ — Executable code
32
+ ├── references/ — Docs loaded on demand
33
+ └── assets/ — Templates, icons
34
+ ```
35
+
36
+ ### Frontmatter (Project Convention)
37
+
38
+ ```yaml
39
+ ---
40
+ name: my-skill
41
+ description: "ALWAYS invoke when [trigger]. Do NOT [negation]."
42
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
43
+ ---
44
+ ```
45
+
46
+ **Description rules:**
47
+ - Start with `ALWAYS invoke when...`
48
+ - Include `Do NOT...` negation
49
+ - Keep under 200 chars
50
+ - Be slightly "pushy" — Claude tends to undertrigger skills
51
+
52
+ ### Writing Guide
53
+
54
+ - Keep SKILL.md under 500 lines
55
+ - Use imperative form in instructions
56
+ - Explain **why** things are important (not heavy-handed MUSTs)
57
+ - Include examples for complex patterns
58
+ - Reference files from SKILL.md with guidance on when to read
59
+ - For large reference files (>300 lines), include a table of contents
60
+
61
+ ## Testing Skills
62
+
63
+ ```json
64
+ // evals/evals.json
65
+ {
66
+ "skill_name": "example-skill",
67
+ "evals": [
68
+ {
69
+ "id": 1,
70
+ "prompt": "User's task prompt",
71
+ "expected_output": "Description of expected result",
72
+ "files": []
73
+ }
74
+ ]
75
+ }
76
+ ```
77
+
78
+ For each test case, run Claude with and without the skill, then compare outputs.
79
+
80
+ ## Description Optimization
81
+
82
+ 1. **Generate 20 eval queries** — Mix of should-trigger (8-10) and should-not-trigger (8-10)
83
+ 2. **Make queries realistic** — Include file paths, personal context, casual speech, typos
84
+ 3. **Focus negatives on near-misses** — Adjacent domains, ambiguous phrasing
85
+ 4. **Test triggering** — Run queries against the description
86
+ 5. **Iterate** — Improve description based on trigger accuracy
87
+
88
+ ## Validation
89
+
90
+ After creating/modifying a skill:
91
+
92
+ ```bash
93
+ bash .claude/scripts/validate-skills.sh
94
+ ```
95
+
96
+ Checks: frontmatter, imperative pattern, negation, char budget < 16,000.
97
+
98
+ ## Critical Rules
99
+
100
+ 1. **Interview first** — Understand the user's intent before writing
101
+ 2. **Imperative description** — `ALWAYS invoke when... Do NOT...`
102
+ 3. **Under 200 chars** — Description must be concise
103
+ 4. **Test with real prompts** — Not abstract requests
104
+ 5. **Generalize from feedback** — Don't overfit to test cases
105
+ 6. **Explain the why** — Theory of mind over rigid MUSTs
106
+ 7. **Bundle repeated work** — If every test run writes the same helper, put it in scripts/
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: test-infrastructure
3
+ description: "ALWAYS invoke when setting up test configs, creating data factories, or writing integration tests. Do NOT configure Vitest or create test data without checking patterns first."
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Test Infrastructure
8
+
9
+ Patterns for Vitest configuration, data factories, integration tests, and test cleanup.
10
+
11
+ ## Data Factory Pattern
12
+
13
+ ```typescript
14
+ // tests/factories/user.factory.ts
15
+ import { ObjectId } from 'mongodb';
16
+
17
+ let counter = 0;
18
+
19
+ function nextId(): string {
20
+ return new ObjectId().toHexString();
21
+ }
22
+
23
+ function nextEmail(): string {
24
+ counter++;
25
+ return `user-${counter}-${Date.now()}@test.com`;
26
+ }
27
+
28
+ export function createUser(overrides: Partial<IUser> = {}): IUser {
29
+ return {
30
+ _id: nextId(),
31
+ email: nextEmail(),
32
+ name: `Test User ${counter}`,
33
+ password: 'HashedPassword123!',
34
+ role: 'user',
35
+ isActive: true,
36
+ createdAt: new Date(),
37
+ updatedAt: new Date(),
38
+ ...overrides,
39
+ };
40
+ }
41
+
42
+ export function createUsers(count: number, overrides: Partial<IUser> = {}): IUser[] {
43
+ return Array.from({ length: count }, () => createUser(overrides));
44
+ }
45
+ ```
46
+
47
+ ### Random Data Helpers (no faker dependency)
48
+
49
+ ```typescript
50
+ // tests/helpers/random.ts
51
+ export const random = {
52
+ string: (length = 10) =>
53
+ Math.random().toString(36).substring(2, 2 + length),
54
+ number: (min: number, max: number) =>
55
+ Math.floor(Math.random() * (max - min + 1)) + min,
56
+ boolean: () => Math.random() > 0.5,
57
+ date: (daysBack = 30) => {
58
+ const d = new Date();
59
+ d.setDate(d.getDate() - Math.floor(Math.random() * daysBack));
60
+ return d;
61
+ },
62
+ pick: <T>(arr: T[]): T => arr[Math.floor(Math.random() * arr.length)]!,
63
+ email: () => `test-${Date.now()}-${Math.random().toString(36).slice(2, 6)}@test.com`,
64
+ };
65
+ ```
66
+
67
+ ## Vitest Configuration
68
+
69
+ ```typescript
70
+ // vitest.config.ts
71
+ import { defineConfig } from 'vitest/config';
72
+ import path from 'path';
73
+
74
+ export default defineConfig({
75
+ test: {
76
+ globals: true,
77
+ environment: 'node',
78
+ include: ['tests/**/*.test.ts', 'tests/**/*.spec.ts'],
79
+ exclude: ['tests/e2e/**'],
80
+ coverage: {
81
+ provider: 'v8',
82
+ reporter: ['text', 'lcov'],
83
+ include: ['src/**/*.ts', 'server/**/*.ts'],
84
+ exclude: ['**/*.d.ts', '**/*.test.ts'],
85
+ thresholds: { statements: 80, branches: 75, functions: 80, lines: 80 },
86
+ },
87
+ setupFiles: ['tests/setup.ts'],
88
+ testTimeout: 10000,
89
+ },
90
+ resolve: {
91
+ alias: {
92
+ '$types': path.resolve(__dirname, './types'),
93
+ '@common': path.resolve(__dirname, './common/index.ts'),
94
+ '@db': path.resolve(__dirname, './common/db'),
95
+ '@': path.resolve(__dirname, './src'),
96
+ },
97
+ },
98
+ });
99
+ ```
100
+
101
+ ### Setup File
102
+
103
+ ```typescript
104
+ // tests/setup.ts
105
+ import { beforeAll, afterAll, afterEach } from 'vitest';
106
+ import { MongoMemoryServer } from 'mongodb-memory-server';
107
+ import mongoose from 'mongoose';
108
+
109
+ let mongod: MongoMemoryServer;
110
+
111
+ beforeAll(async () => {
112
+ mongod = await MongoMemoryServer.create();
113
+ await mongoose.connect(mongod.getUri());
114
+ });
115
+
116
+ afterEach(async () => {
117
+ const collections = mongoose.connection.collections;
118
+ for (const key in collections) {
119
+ await collections[key]!.deleteMany({});
120
+ }
121
+ });
122
+
123
+ afterAll(async () => {
124
+ await mongoose.disconnect();
125
+ await mongod.stop();
126
+ });
127
+ ```
128
+
129
+ ## Integration Test Patterns
130
+
131
+ ```typescript
132
+ // tests/integration/user.service.test.ts
133
+ import { describe, it, expect, beforeEach } from 'vitest';
134
+ import { UserService } from '@/services/user.service';
135
+ import { UserModel } from '@/models/user.model';
136
+ import { createUser } from '../factories/user.factory';
137
+
138
+ describe('UserService', () => {
139
+ let service: UserService;
140
+
141
+ beforeEach(() => {
142
+ service = new UserService();
143
+ });
144
+
145
+ describe('createUser', () => {
146
+ it('should create a user with hashed password', async () => {
147
+ const input = { email: 'new@test.com', password: 'Pass123!', name: 'Test' };
148
+ const user = await service.create(input);
149
+
150
+ expect(user.email).toBe('new@test.com');
151
+ expect(user.password).not.toBe('Pass123!'); // Hashed
152
+ });
153
+
154
+ it('should reject duplicate emails', async () => {
155
+ const existing = createUser({ email: 'dup@test.com' });
156
+ await UserModel.create(existing);
157
+
158
+ await expect(
159
+ service.create({ email: 'dup@test.com', password: 'Pass123!', name: 'Test' })
160
+ ).rejects.toThrow(/duplicate/i);
161
+ });
162
+ });
163
+
164
+ describe('findById', () => {
165
+ it('should return null for non-existent user', async () => {
166
+ const result = await service.findById('507f1f77bcf86cd799439011');
167
+ expect(result).toBeNull();
168
+ });
169
+ });
170
+ });
171
+ ```
172
+
173
+ ### API Route Testing
174
+
175
+ ```typescript
176
+ // tests/integration/api/users.test.ts
177
+ import { describe, it, expect } from 'vitest';
178
+ import { createUser } from '../../factories/user.factory';
179
+
180
+ describe('POST /api/users', () => {
181
+ it('should validate required fields', async () => {
182
+ const response = await app.inject({
183
+ method: 'POST',
184
+ url: '/api/users',
185
+ payload: {},
186
+ });
187
+
188
+ expect(response.statusCode).toBe(400);
189
+ expect(response.json().errors).toBeDefined();
190
+ });
191
+
192
+ it('should create user with valid data', async () => {
193
+ const input = { email: 'new@test.com', password: 'Pass123!', name: 'Test' };
194
+ const response = await app.inject({
195
+ method: 'POST',
196
+ url: '/api/users',
197
+ payload: input,
198
+ });
199
+
200
+ expect(response.statusCode).toBe(201);
201
+ expect(response.json().email).toBe('new@test.com');
202
+ expect(response.json().password).toBeUndefined(); // Not exposed
203
+ });
204
+ });
205
+ ```
206
+
207
+ ## Cleanup Strategy
208
+
209
+ ```typescript
210
+ // Delete children before parents to avoid foreign key issues
211
+ async function cleanupDatabase() {
212
+ const collections = ['orders', 'products', 'users', 'sessions'];
213
+ for (const name of collections) {
214
+ const collection = mongoose.connection.collection(name);
215
+ if (collection) await collection.deleteMany({});
216
+ }
217
+ }
218
+
219
+ // Per-test isolation with transaction rollback
220
+ async function withTransaction<T>(fn: () => Promise<T>): Promise<T> {
221
+ const session = await mongoose.startSession();
222
+ session.startTransaction();
223
+ try {
224
+ const result = await fn();
225
+ await session.abortTransaction(); // Always rollback in tests
226
+ return result;
227
+ } finally {
228
+ session.endSession();
229
+ }
230
+ }
231
+ ```
232
+
233
+ ## Critical Rules
234
+
235
+ 1. **ObjectId + counter** — Use real ObjectIds, counter for unique emails
236
+ 2. **No faker** — Use simple random helpers (no heavy dependency)
237
+ 3. **MongoMemoryServer** — In-memory DB for integration tests
238
+ 4. **Path aliases** — Mirror project aliases in vitest.config.ts
239
+ 5. **Children before parents** — Delete dependent collections first
240
+ 6. **afterEach cleanup** — Clear all collections between tests
241
+ 7. **Factory pattern** — `createUser(overrides)` for flexible test data
242
+ 8. **Separate E2E** — Exclude `tests/e2e/` from Vitest (Playwright handles those)