Testing
Jumbo React Vite uses Vitest 4 as the test runner with React Testing Library 16 for
component tests and @testing-library/user-event for simulating real user interactions.
Test stack
| Tool | Version | Role |
|---|---|---|
| Vitest | 4.1.x | Test runner, coverage (v8) |
| React Testing Library | 16.3.x | Component query utilities |
@testing-library/user-event | 14.6.x | User interaction simulation |
@testing-library/jest-dom | 6.9.x | Custom DOM matchers |
| jsdom | 29.x | Browser-like DOM environment |
Running tests
# Interactive watch mode
pnpm test
# Single run (CI)
pnpm test:run
# With coverage report
pnpm test:coverageCoverage reports are written to ./coverage/ in text, json, and html formats.
Vitest configuration
// vitest.config.ts (key sections)
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
css: true,
coverage: {
provider: 'v8',
include: [
'src/utilities/helpers/index.tsx',
'src/translations/**/*.ts',
'@jumbo/utilities/formatHelpers.ts',
'@jumbo/utilities/helpers.ts',
'@jumbo/utilities/styleHelpers.ts',
'@jumbo/utilities/systemHelpers.ts',
'@jumbo/utilities/cookies.ts',
],
thresholds: {
statements: 80,
branches: 80,
functions: 80,
lines: 80,
},
},
},
});The include list scopes coverage to utilities and translation files. Component coverage is not
currently enforced but is encouraged on new feature code.
renderWithProviders
Always use renderWithProviders instead of bare render from @testing-library/react. It wraps
the component with the three providers required for most components in this project:
// src/test/renderWithProviders.tsx
const AllProviders = ({ children }: { children: ReactNode }) => (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ThemeProvider theme={defaultTheme}>
<BrowserRouter>{children}</BrowserRouter>
</ThemeProvider>
</LocalizationProvider>
);
export const renderWithProviders = (ui, options?) =>
render(ui, { wrapper: AllProviders, ...options });Import it from @/test/renderWithProviders:
import { screen } from '@testing-library/react';
import { renderWithProviders } from '@/test/renderWithProviders';Only use bare render for components that explicitly require zero providers (pure utility
components, static display components).
Writing component tests
Create a co-located test file
Place the test file next to the component:
src/components/MyWidget/ MyWidget.tsx MyWidget.test.tsxWrite the test
tsx// src/components/MyWidget/MyWidget.test.tsx import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { renderWithProviders } from '@/test/renderWithProviders'; import { MyWidget } from './MyWidget'; describe('MyWidget', () => { it('renders the widget title', () => { renderWithProviders(<MyWidget title='Revenue' value={42_500} />); expect(screen.getByText('Revenue')).toBeInTheDocument(); }); it('calls onAction when the button is clicked', async () => { const user = userEvent.setup(); const handleAction = vi.fn(); renderWithProviders(<MyWidget title='Revenue' onAction={handleAction} />); await user.click(screen.getByRole('button', { name: /refresh/i })); expect(handleAction).toHaveBeenCalledOnce(); }); });
Writing utility tests
Pure utility functions are tested without renderWithProviders:
// @jumbo/utilities/cookies.test.ts
import { getCookie, setCookie, eraseCookie } from '@jumbo/utilities/cookies';
describe('setCookie / getCookie', () => {
afterEach(() => {
eraseCookie('test-cookie');
});
it('stores and retrieves a string value', () => {
setCookie('test-cookie', 'hello', 1);
expect(getCookie('test-cookie')).toBe('hello');
});
it('returns null for a missing cookie', () => {
expect(getCookie('nonexistent')).toBeNull();
});
});Coverage targets
Every file in the include list must maintain:
| Metric | Threshold |
|---|---|
| Statements | 80% |
| Branches | 80% |
| Functions | 80% |
| Lines | 80% |
If pnpm test:coverage fails a threshold, the exit code is non-zero and CI will block the merge.
Test files live next to the source file they test (Foo.tsx → Foo.test.tsx). This makes it
easy to see at a glance whether a component is tested, and keeps related code together when
moving or renaming files.