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
+123Lines changed: 123 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -180,6 +180,42 @@ The project uses Chromatic for visual regression testing. PRs automatically trig
180
180
2. PRs will show visual diff status checks
181
181
3. Changes to main are auto-accepted as the new baseline
182
182
183
+
**Deterministic Date Rendering:**
184
+
185
+
Stories that display relative dates (e.g. "5 days ago", "Pending (14d)") must mock `globalThis.Date` in a `beforeEach` hook to pin `Date.now()` to a fixed timestamp. Without this, Chromatic detects a visual change every day as the rendered text shifts. Use the pattern from `PaymentDetailsModal.stories.tsx`:
186
+
187
+
```typescript
188
+
const FIXED_NOW =newDate('2026-02-15T12:00:00Z')
189
+
190
+
const meta = {
191
+
// ...
192
+
beforeEach: () => {
193
+
const OriginalDate =globalThis.Date
194
+
const fixedTime =FIXED_NOW.getTime()
195
+
196
+
const MockDate:any=function (...args:any[]) {
197
+
if (args.length===0) returnnewOriginalDate(fixedTime)
This applies to any component using `formatDistanceToNow`, `getDaysPending`, or other relative date calculations.
218
+
183
219
### Privacy and GDPR Compliance
184
220
185
221
-**User Data Protection:** Always abide by GDPR regulations when handling any user data, including but not limited to:
@@ -342,3 +378,90 @@ When refactoring code:
342
378
- Document any complex or non-obvious props
343
379
344
380
This structure ensures the codebase remains maintainable, testable, and scalable as the project grows.
381
+
382
+
## Testing Guidelines
383
+
384
+
The project uses **Vitest** with `@testing-library/react` for unit and integration tests, and **Storybook interaction tests** for component behavior.
385
+
386
+
### Test Philosophy
387
+
388
+
-**Test behavior, not implementation.** Assert on observable outcomes (return values, rendered output, side effects) rather than internal state or private methods.
389
+
-**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.
390
+
-**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).
391
+
-**Tests are documentation.** A reader should understand what the code does by reading the test names and assertions.
392
+
393
+
### What to Test
394
+
395
+
-**Domain logic and data transformations** (e.g., validation functions, formatters, business rules).
396
+
-**API route handlers** — test request/response contracts with realistic payloads.
397
+
-**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.
398
+
-**Error handling paths** — verify that errors produce correct user-facing messages or status codes.
399
+
-**Edge cases and boundary conditions** — empty arrays, null values, missing optional fields, maximum lengths.
400
+
401
+
### What NOT to Test
402
+
403
+
-**Type definitions and interfaces** — TypeScript already validates these at compile time.
404
+
-**Simple pass-through functions** — if a function just calls another with the same arguments, the value of testing it is near zero.
405
+
-**Third-party library internals** — trust that Sanity, NextAuth, tRPC work correctly. Mock them at the boundary and test your integration logic.
406
+
-**Styling and layout** — use Storybook visual testing and Chromatic for visual regression instead.
407
+
408
+
### Writing Tests
409
+
410
+
```typescript
411
+
// Test file location: __tests__/{path matching src/}
// Use descriptive test names that explain the scenario and expected outcome
415
+
describe('validateProposal', () => {
416
+
it('should reject proposals without a title', () => { ... })
417
+
it('should accept proposals with all required fields', () => { ... })
418
+
})
419
+
```
420
+
421
+
-**File naming:**`{module}.test.ts` or `{Component}.test.tsx`, mirroring the source path under `__tests__/`.
422
+
-**Structure:** Use `describe` blocks to group by function or feature, `it` blocks for individual scenarios.
423
+
-**Setup:** Use factory functions or test fixtures over complex `beforeEach` setup. Keep test data close to where it's used.
424
+
-**Assertions:** Be specific. Prefer `toEqual` over `toBeTruthy`. Assert on error messages, not just that an error was thrown.
425
+
-**Async:** Always `await` async operations. Never use `done` callbacks.
426
+
427
+
### Mocking
428
+
429
+
-**Mock at boundaries, not internally.** Mock external services (Sanity client, email provider, Slack API), not internal utility functions.
430
+
-**Use `vi.mock()` for module-level mocks.** Vitest auto-hoists these, so standard ESM `import` works — no need for `require()` workarounds.
431
+
-**Prefer dependency injection** where possible over module mocking.
432
+
-**Avoid `Object.defineProperty` on `process.env`** — Vitest makes env properties non-configurable. Use direct assignment: `process.env.MY_VAR = 'value'`.
433
+
-**Clean up mocks** with `vi.restoreAllMocks()` in `afterEach` or `beforeEach` to prevent test pollution.
434
+
435
+
### Environment Directives
436
+
437
+
Tests run in `node` environment by default. For component tests that need DOM APIs, add a docblock at the top of the file:
438
+
439
+
```typescript
440
+
/**
441
+
* @vitest-environment jsdom
442
+
*/
443
+
```
444
+
445
+
### Storybook Interaction Tests
446
+
447
+
For component behavior testing in context, prefer Storybook `play` functions using `storybook/test`:
448
+
449
+
```typescript
450
+
import { expect, fn, userEvent, within } from'storybook/test'
451
+
452
+
exportconst ClickTest:Story= {
453
+
args: { onClick: fn() },
454
+
play: async ({ canvasElement }) => {
455
+
const canvas =within(canvasElement)
456
+
awaituserEvent.click(canvas.getByRole('button'))
457
+
},
458
+
}
459
+
```
460
+
461
+
These run in CI via `pnpm run storybook:test-ci` and complement unit tests by verifying components in a realistic rendering context.
462
+
463
+
### Performance
464
+
465
+
-**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.
466
+
-**Keep tests fast.** The full suite should run in under 15 seconds. If a test needs more than 5 seconds, reconsider the approach.
467
+
-**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