Frontend Testing
This guide covers testing strategies and implementation for the Rhesis frontend application.
Testing Framework The frontend uses Jest as the test runner and React Testing Library for component testing, providing comprehensive unit testing capabilities.
Quick Start
Running Tests
Equivalent make targets are also available from apps/frontend: make test runs npm test with --passWithNoTests --ci --watchAll=false; make test-coverage runs npm run test:ci (Jest with coverage reporters aligned with CI). ./rh test frontend (repo root) runs plain npm test in apps/frontend.
Coverage is collected (text, json-summary, lcov reporters) but there is no enforced coverage threshold in jest.config.js — a drop in coverage doesn’t fail the build.
End-to-end Testing (Playwright)
Changes under apps/frontend/** trigger [Test] Frontend E2E in CI. CI uses Docker for the Quick Start backend (make test-e2e-ci, Chromium only).
Locally without Docker (frontend + mocked API only):
This starts a dedicated dev server on port 3100 (so it does not clash with npm run dev on 3000), seeds auth without a backend (E2E_NO_DOCKER=1), and runs Playwright route mocks for API data.
CI / full backend (requires Docker):
Debug: npm run test:e2e:ui or npm run test:e2e:headed. Config: playwright.config.ts; specs: tests/e2e/.
Testing Configuration
Jest Setup
The testing framework is configured in jest.config.js:
There is no coverageThreshold — coverage is reported, not gated.
Test Environment Setup
The test environment is configured in jest.setup.js (abridged below — the real file also adds jest-axe, a next/navigation mock, and localStorage/sessionStorage mocks):
Writing Tests
Component Testing
Use React Testing Library to test component behavior and user interactions:
Hook Testing
renderHook/act come from @testing-library/react directly — there’s no separate @testing-library/react-hooks package. Hooks built on @tanstack/react-query mutations (e.g. useTasks) test the mutation functions, not a fetched-on-mount list:
Utility Function Testing
Test utility functions with Jest — formatDate (src/utils/date.ts) takes an optional timezone, not a locale (it always formats with en-GB internally, and defaults to the runtime’s local timezone when none is given):
Test Utilities
Global Render Wrapper
jest.config.js remaps @testing-library/react itself to src/test-utils.tsx, so every render() call in the suite automatically gets QueryClientProvider and the MUI theme — no renderWithProviders helper is needed at each call site:
Separately, src/__mocks__/test-utils.tsx mocks next-auth/react and exports mock data factories (createMockProject, createMockTest, etc.) for building fixtures — it does not define a render wrapper itself.
Mock Implementations
Create mock implementations for API clients:
Testing Best Practices
Test Structure
Good Practices
// Test user behavior, not implementation
expect(screen.getByRole('button')).toBeInTheDocument();
// Use semantic queries
screen.getByRole('button', { name: /submit/i });
// Test error states
expect(screen.getByText(/error/i)).toBeInTheDocument();Avoid These
// Don't test implementation details
expect(wrapper.find('.my-button')).toHaveLength(1);
// Avoid fragile queries
screen.getByClassName('button-submit');
// Don't test internal state
expect(component.state.isLoading).toBe(true);Test Organization
Tests are co-located in a __tests__/ subfolder next to the code they cover (e.g. components/common/__tests__/BaseDrawer.test.tsx, utils/api-client/__tests__/...), plus a top-level src/__tests__/ for broader integration tests.
Integration with CI/CD
Local Validation Script
apps/frontend/scripts/validate.sh runs tests as part of a full local gate (format, type-check, lint, test, build). It is not wired to a git hook or invoked by CI — run it manually before opening a PR:
GitHub Actions
The unit-test workflow is .github/workflows/frontend-test.yml (“[Test] Frontend Unit”) — not frontend.yml, which is a build/deploy workflow with no test job:
A separate community-boundary job in the same workflow checks for EE-import leaks into the community codebase.
Troubleshooting
Common Issues
Environment Variables Missing
# Add to jest.setup.js
process.env.API_BASE_URL = 'http://localhost:8080/api/v1';
window.__ENV__ = {
apiBaseUrl: 'http://localhost:8080/api/v1',
};Browser APIs Not Available
# Mock in jest.setup.js
global.matchMedia = jest.fn().mockImplementation(query => ({ ... }));
global.ResizeObserver = jest.fn();Async Operations Not Awaited
// Use act() for async operations
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 0));
});