|
| 1 | +--- |
| 2 | +description: Bun test and React Testing Library patterns for React Router apps |
| 3 | +globs: apps/**/*.test.{ts, tsx}, apps/**/*.spec.{ts, tsx}, apps/**/test/**/* |
| 4 | +alwaysApply: false |
| 5 | +--- |
| 6 | + |
| 7 | +# Testing Best Practices |
| 8 | + |
| 9 | +## Stack |
| 10 | + |
| 11 | +- **Runner:** Bun test (`bun:test`) with JSDOM bootstrap in `test/setup.ts` (loaded via `bunfig.toml`) |
| 12 | +- **Component/DOM:** React Testing Library |
| 13 | +- **Router:** `createMemoryRouter` + `RouterProvider` via shared `renderWithRouter` helper |
| 14 | + |
| 15 | +## Test Layout (todo-app) |
| 16 | + |
| 17 | +| Pattern | Location | Purpose | |
| 18 | +|---------------|-----------------------------------|--------| |
| 19 | +| Unit | `app/lib/__tests__/*.test.ts` | Pure utilities, helpers | |
| 20 | +| Component | `app/components/__tests__/*.test.tsx` | UI with router/context mocking | |
| 21 | +| Integration | `app/routes/__tests__/*.integration.test.tsx` | Full route + provider flows | |
| 22 | + |
| 23 | +Shared setup: `test/setup.ts` (jest-dom, RTL cleanup). Shared helpers: `test/test-utils.tsx`. |
| 24 | + |
| 25 | +## Router-Dependent Components |
| 26 | + |
| 27 | +Use `renderWithRouter` for any component that uses `Link`, `useNavigate`, `useFetcher`, or `useHref`: |
| 28 | + |
| 29 | +```tsx |
| 30 | +import { renderWithRouter } from '../../../test/test-utils'; |
| 31 | + |
| 32 | +it('renders and submits', () => { |
| 33 | + renderWithRouter(<MyComponent />); |
| 34 | +}); |
| 35 | + |
| 36 | +it('renders at /contact', () => { |
| 37 | + renderWithRouter(<Page />, { initialEntries: ['/contact'] }); |
| 38 | +}); |
| 39 | +``` |
| 40 | + |
| 41 | +For full route trees (loaders, nested layouts), use `createTestRouter` or pass `routes` into `renderWithRouter`. |
| 42 | + |
| 43 | +## Integration Tests |
| 44 | + |
| 45 | +Wrap route components with the same providers as the app (e.g. TodoProvider), then assert user flows: |
| 46 | + |
| 47 | +```tsx |
| 48 | +import { renderWithRouter } from '../../test/test-utils'; |
| 49 | +import { TodoProvider } from '../../lib/todo-context'; |
| 50 | +import Home from '../home'; |
| 51 | + |
| 52 | +renderWithRouter( |
| 53 | + <TodoProvider> |
| 54 | + <Home /> |
| 55 | + </TodoProvider> |
| 56 | +); |
| 57 | +// fire events, assert DOM/state |
| 58 | +``` |
| 59 | + |
| 60 | +## Bun Config (reference) |
| 61 | + |
| 62 | +- **preload:** `test/setup.ts` via `bunfig.toml` |
| 63 | +- **jsdom:** initialized in `test/setup.ts` (globals + `@testing-library/jest-dom`) |
| 64 | +- **testTimeout:** Bun default is `5000`; adjust with `bun test --timeout <ms>` when needed |
| 65 | + |
| 66 | +## Commands |
| 67 | + |
| 68 | +- `bun run test` – run tests |
| 69 | +- `bun run test:watch` – watch mode |
| 70 | +- `bun run test:run` / `bun run test:ci` – single run (CI) |
| 71 | + |
| 72 | +## Best Practices |
| 73 | + |
| 74 | +- Prefer `getByRole`, `getByLabelText`, `getByPlaceholderText` over brittle selectors |
| 75 | +- Use `jest.fn()` from `bun:test` for callbacks; assert calls and args |
| 76 | +- Hoist repeated regex/selectors to describe scope to satisfy lint rules |
| 77 | +- Keep tests focused; use multiple `it` blocks instead of one large test |
| 78 | +- For forms: test validation, submit behavior, and error display (see lambda-curry-forms rules for FormError) |
0 commit comments