---
description: Jest testing conventions
globs: ["**/*.test.{ts,tsx,js}","**/__tests__/**"]
alwaysApply: false
---

# Jest

- Run `npx jest` (CI), `npx jest path/to/file` while working, `npx jest --coverage` for coverage. TS via `ts-jest` or the existing transform; don't change the transform.
- Tests next to code (`foo.test.ts`) or in `__tests__/`, matching the repo. `describe` per unit, `it('does X when Y')` per behavior.
- Async: `await` the promise or use `await expect(p).rejects.toThrow(...)`. Never mix `done` callbacks with async functions.
- Mock at boundaries: `jest.mock('../lib/http')` for modules you own, MSW or `nock` for HTTP. Prefer `jest.spyOn(obj, 'method')` over replacing whole modules.
- Restore between tests: `restoreMocks: true` / `clearMocks: true` in config, or `afterEach(() => jest.restoreAllMocks())`.
- Timers: `jest.useFakeTimers()` with `jest.advanceTimersByTime` / `await jest.runAllTimersAsync()`; restore with `jest.useRealTimers()`.
- ESM-only packages may need `transformIgnorePatterns` updates; don't paper over import errors with mocks.
- Use `toEqual` for structure, `toBe` for primitives/identity, `toHaveBeenCalledWith` sparingly (behavior first).
- React components: Testing Library + `user-event`, `jest-environment-jsdom` per file (`@jest-environment jsdom`) if not global.
- Snapshots only for small stable output; review before updating with `-u`.

## Don't
- Don't leave `.only`/`.skip`, and never weaken assertions to pass.

## Done when
`npx jest` passes with no open-handle warnings (`--detectOpenHandles` clean).
