Some checks failed
95 tests across 22 files covering all frontend modules: - API layer: client, auth - Components: BondDetails, Layout, PriceChart, ProtectedRoute, SearchBar, StockDetails - Context: AuthContext - Hooks: useAuth, useBond, useBondCandles, useSearch, useStock, useStockCandles, useStockDividends - Pages: BondPage, HomePage, LoginPage, ProfilePage, RegisterPage, StockPage Infrastructure: - vitest + @testing-library/react + MSW v2 with 13 API handlers - Co-located test files alongside source files - Test utilities: setup, server, factories, test-utils - BrowserRouter future flags for MemoryRouter test compatibility - Root test:frontend script for workspace-wide execution
5.4 KiB
5.4 KiB
Frontend Unit Test Coverage
Stack
- vitest — test runner
- @testing-library/react + @testing-library/jest-dom + @testing-library/user-event — component testing
- jsdom — DOM environment
- msw (v2) — network-level mocking via
setupServer
Project structure (new files)
apps/frontend/
├── vitest.config.ts
└── src/
└── test/
├── setup.ts # jest-dom matchers, matchMedia mock
├── server.ts # MSW setupServer instance
├── handlers.ts # Default MSW handlers for all endpoints
├── test-utils.tsx # renderWithProviders wrapper
└── factories.ts # Mock data factories (share, bond, candle, user)
Conventions
| Rule | Value |
|---|---|
| Location | ComponentName.test.tsx alongside ComponentName.tsx |
| Pattern | AAA (Arrange -> Act -> Assert) |
| Describe nesting | describe('Component') -> describe('when ...') |
| Snapshots | None |
| Mocks | MSW for API; vi.fn() for callbacks only |
| QueryClient | Fresh instance per test via renderWithProviders with retry: false |
renderWithProviders
Custom wrapper that composes:
function renderWithProviders(ui, { authState, queryClient, route } = {})
QueryClientProviderwith fresh client (defaultOptions:queries: { retry: false })AuthProviderwith optional override of initial stateMemoryRouterwith optional initial route- Returns
{ ...render(ui), queryClient }
MSW handlers (default)
All API endpoints return 200 with valid mock data by default. Handlers:
GET /api/v1/auth/me-> userPOST /api/v1/auth/login-> tokensPOST /api/v1/auth/register-> tokensPOST /api/v1/auth/refresh-> tokensPOST /api/v1/auth/logout-> 204PATCH /api/v1/auth/me-> updated userGET /api/v1/shares/:secid-> shareGET /api/v1/shares/:secid/candles-> candlesGET /api/v1/shares/:secid/dividends-> dividendsGET /api/v1/bonds/:secid-> bondGET /api/v1/bonds/:secid/candles-> candlesGET /api/v1/securities/search-> search resultsGET /api/v1/health-> ok
Errors are tested by overriding handlers per-test via server.use().
Implementation order (5 phases)
Phase 1: Infrastructure + API client
Files to create:
vitest.config.tssrc/test/setup.tssrc/test/server.tssrc/test/handlers.tssrc/test/test-utils.tsxsrc/test/factories.ts
Tests:
api/client.test.ts— basic get/post, 401 -> refresh -> retry, persistent 401 -> throwsapi/auth.test.ts— login, register, refresh, logout, getMe, updateProfile
Phase 2: Auth + Hooks
context/AuthContext.test.tsx—login/register/logout/updateProfileupdate context statehooks/useAuth.test.tsx— returns context values, throws outside providerhooks/useSearch.test.ts— empty query, results found, no results, errorhooks/useStock.test.ts— data loaded, loading, errorhooks/useStockCandles.test.ts— data loaded, empty array, errorhooks/useStockDividends.test.ts— data loaded, no dividends, errorhooks/useBond.test.ts— data loaded, loading, errorhooks/useBondCandles.test.ts— data loaded, empty array, error
Phase 3: UI Components
components/SearchBar.test.tsx— typing triggers search, dropdown shows results, clicking result navigates, empty input hides dropdown, loading statecomponents/ProtectedRoute.test.tsx— authenticated -> renders children; unauthenticated -> redirects to/login?redirect=components/StockDetails.test.tsx— renders all fields, missing optional fields, null/undefined datacomponents/BondDetails.test.tsx— renders all fields, missing optional fields, null/undefined datacomponents/PriceChart.test.tsx— renders chart with candle data, empty data array, error statecomponents/Layout.test.tsx— renders search bar, auth state toggles login/profile link, logout button works on profile
Phase 4: Pages
pages/LoginPage.test.tsx— renders form, validation errors, successful login redirects, server error displayedpages/RegisterPage.test.tsx— renders form, validation errors, successful register redirects, password mismatchpages/HomePage.test.tsx— renders welcome content and navigation linkspages/StockPage.test.tsx— loading skeleton, renders all sections (details, chart, dividends), error state, not foundpages/BondPage.test.tsx— loading skeleton, renders all sections (details, chart), error state, not foundpages/ProfilePage.test.tsx— loads user data, renders form, successful update, validation error, not authenticated -> redirect
Phase 5: Scripts & wiring
- Add
"test": "vitest run"and"test:watch": "vitest"toapps/frontend/package.json - Add
"test:frontend": "npm run test -w apps/frontend"to rootpackage.json - Verify:
npm run test:frontendpasses
Testing rules
- No
waitForfor queries — usefindBy*/findAllBy*which already await userEventoverfireEvent— simulates real interactions- MSW handlers reset in
afterEach—server.resetHandlers() server.close()inafterAll- No snapshot tests
- Factory data only includes required fields — tests explicitly add optional fields when needed
- Each
describetests one concern — no 200-line tests