How This App Is Tested
Unit tests here run on Jest with React Testing Library, and the philosophy is RTL's own: test what the operator sees and does, not the component's internals. This lesson gives you the lay of the land — where tests live, what's already mocked for you, and the configuration quirks that will otherwise cost you an afternoon.
Running them
npm run test # watch mode, for development
npm run test:ci # single pass with coverage — what the pipeline runs
Every PR targeting development runs the suite; results and coverage land in the Azure DevOps
PR tabs (details).
Where tests live
Co-located, always: MeasurementToggle.tsx sits next to MeasurementToggle.test.tsx in the
same component folder. There is no parallel tests/ tree to keep in sync. Shared mocks live in
__mocks__/ at the repo root — ArcGIS modules, Zustand stores, context providers — and the
Mocking Playbook catalogs all of them.
What's already set up for you
jest.setup.js runs before every test file and pre-mocks the things almost every component
touches: the Next.js router, a signed-in next-auth session, cameraOptionsStore, and
Swiper's CSS imports. Your test starts from "a logged-in user on the home route" for free.
For components that need real providers — MUI theme, React Query, session, date pickers — use the shared helper instead of hand-rolling wrappers:
import { renderWithProviders } from 'util/renderWithProviders';
test('renders', () => {
renderWithProviders(<MyComponent />);
});
It builds a fresh QueryClient per test (retries off, cache disabled) so no state leaks
between tests.
The config, and its two traps
jest.config.js wraps Next.js's next/jest preset with a few custom entries:
jest.polyfills.js runs first (so server-side imports find Request/Response),
transformIgnorePatterns lets @arcgis/core and swiper be transformed, and
moduleNameMapper routes @/ paths and CSS to stubs.
testPathIgnorePatterns lists five Notification test files, commented "skip to reduce memory
usage." All five paths are stale — the components moved under
Notifications/NotificationSettingsForm/, so the patterns match nothing and those tests run
in every suite. If a "skipped" Notification test fails your run, that's why. Fix the paths or
delete the entries; don't trust the comment.
^swiper/css.*$ must be mapped before the generic ^.+\.(css)$ rule — both point at
scripts/fileTransform.js today, but the specific rule exists because Swiper's CSS imports
aren't real files on disk. If you touch moduleNameMapper, keep the specific entry first.
Wrapping up
Watch mode locally, coverage in CI, tests beside their components, a setup file that fakes the world's boring parts, and one shared provider-wrapper. Mind the stale ignore patterns. Now write one: Writing Tests.