You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+87Lines changed: 87 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -342,3 +342,90 @@ When refactoring code:
342
342
- Document any complex or non-obvious props
343
343
344
344
This structure ensures the codebase remains maintainable, testable, and scalable as the project grows.
345
+
346
+
## Testing Guidelines
347
+
348
+
The project uses **Vitest** with `@testing-library/react` for unit and integration tests, and **Storybook interaction tests** for component behavior.
349
+
350
+
### Test Philosophy
351
+
352
+
-**Test behavior, not implementation.** Assert on observable outcomes (return values, rendered output, side effects) rather than internal state or private methods.
353
+
-**Every test must exercise real source code.** A test that only asserts on values defined within the test file itself is worthless — delete it. If a test doesn't import from `src/`, it's not testing the application.
354
+
-**Prefer integration tests over isolated unit tests.** Test modules working together through their public APIs. Reserve heavy mocking for true external boundaries (network, database, third-party services).
355
+
-**Tests are documentation.** A reader should understand what the code does by reading the test names and assertions.
356
+
357
+
### What to Test
358
+
359
+
-**Domain logic and data transformations** (e.g., validation functions, formatters, business rules).
360
+
-**API route handlers** — test request/response contracts with realistic payloads.
361
+
-**React components** — test user-visible behavior: rendering, interactions, conditional display. Use `@testing-library/react` queries (`getByRole`, `getByText`) over implementation details like CSS classes or component internals.
362
+
-**Error handling paths** — verify that errors produce correct user-facing messages or status codes.
363
+
-**Edge cases and boundary conditions** — empty arrays, null values, missing optional fields, maximum lengths.
364
+
365
+
### What NOT to Test
366
+
367
+
-**Type definitions and interfaces** — TypeScript already validates these at compile time.
368
+
-**Simple pass-through functions** — if a function just calls another with the same arguments, the value of testing it is near zero.
369
+
-**Third-party library internals** — trust that Sanity, NextAuth, tRPC work correctly. Mock them at the boundary and test your integration logic.
370
+
-**Styling and layout** — use Storybook visual testing and Chromatic for visual regression instead.
371
+
372
+
### Writing Tests
373
+
374
+
```typescript
375
+
// Test file location: __tests__/{path matching src/}
// Use descriptive test names that explain the scenario and expected outcome
379
+
describe('validateProposal', () => {
380
+
it('should reject proposals without a title', () => { ... })
381
+
it('should accept proposals with all required fields', () => { ... })
382
+
})
383
+
```
384
+
385
+
-**File naming:**`{module}.test.ts` or `{Component}.test.tsx`, mirroring the source path under `__tests__/`.
386
+
-**Structure:** Use `describe` blocks to group by function or feature, `it` blocks for individual scenarios.
387
+
-**Setup:** Use factory functions or test fixtures over complex `beforeEach` setup. Keep test data close to where it's used.
388
+
-**Assertions:** Be specific. Prefer `toEqual` over `toBeTruthy`. Assert on error messages, not just that an error was thrown.
389
+
-**Async:** Always `await` async operations. Never use `done` callbacks.
390
+
391
+
### Mocking
392
+
393
+
-**Mock at boundaries, not internally.** Mock external services (Sanity client, email provider, Slack API), not internal utility functions.
394
+
-**Use `vi.mock()` for module-level mocks.** Vitest auto-hoists these, so standard ESM `import` works — no need for `require()` workarounds.
395
+
-**Prefer dependency injection** where possible over module mocking.
396
+
-**Avoid `Object.defineProperty` on `process.env`** — Vitest makes env properties non-configurable. Use direct assignment: `process.env.MY_VAR = 'value'`.
397
+
-**Clean up mocks** with `vi.restoreAllMocks()` in `afterEach` or `beforeEach` to prevent test pollution.
398
+
399
+
### Environment Directives
400
+
401
+
Tests run in `node` environment by default. For component tests that need DOM APIs, add a docblock at the top of the file:
402
+
403
+
```typescript
404
+
/**
405
+
* @vitest-environment jsdom
406
+
*/
407
+
```
408
+
409
+
### Storybook Interaction Tests
410
+
411
+
For component behavior testing in context, prefer Storybook `play` functions using `storybook/test`:
412
+
413
+
```typescript
414
+
import { expect, fn, userEvent, within } from'storybook/test'
415
+
416
+
exportconst ClickTest:Story= {
417
+
args: { onClick: fn() },
418
+
play: async ({ canvasElement }) => {
419
+
const canvas =within(canvasElement)
420
+
awaituserEvent.click(canvas.getByRole('button'))
421
+
},
422
+
}
423
+
```
424
+
425
+
These run in CI via `pnpm run storybook:test-ci` and complement unit tests by verifying components in a realistic rendering context.
426
+
427
+
### Performance
428
+
429
+
-**Avoid creating large data structures in tests.** Use `new Uint8Array(size)` instead of `new Array(size).fill('a').join('')` for large binary blobs — the latter is orders of magnitude slower.
430
+
-**Keep tests fast.** The full suite should run in under 15 seconds. If a test needs more than 5 seconds, reconsider the approach.
431
+
-**Use `vi.resetModules()` + dynamic `import()` for tests that need fresh module state** (e.g., environment variable–dependent config). Avoid `vi.isolateModules` which doesn't exist in Vitest.
0 commit comments