moex-vibe/docs/superpowers/specs/2026-06-13-frontend-test-coverage.md
Sergey Krylov 729e94b4db
Some checks failed
CI / lint (pull_request) Failing after 1m53s
CI / test (pull_request) Failing after 1m45s
CI / build (pull_request) Failing after 1m44s
CI / lint (push) Failing after 1m39s
CI / test (push) Failing after 1m40s
CI / build (push) Failing after 1m34s
test: add 95 frontend unit tests with vitest, RTL, and MSW
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
2026-06-14 08:27:15 +03:00

128 lines
5.4 KiB
Markdown

# 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:
```tsx
function renderWithProviders(ui, { authState, queryClient, route } = {})
```
- `QueryClientProvider` with fresh client (defaultOptions: `queries: { retry: false }`)
- `AuthProvider` with optional override of initial state
- `MemoryRouter` with 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` -> user
- `POST /api/v1/auth/login` -> tokens
- `POST /api/v1/auth/register` -> tokens
- `POST /api/v1/auth/refresh` -> tokens
- `POST /api/v1/auth/logout` -> 204
- `PATCH /api/v1/auth/me` -> updated user
- `GET /api/v1/shares/:secid` -> share
- `GET /api/v1/shares/:secid/candles` -> candles
- `GET /api/v1/shares/:secid/dividends` -> dividends
- `GET /api/v1/bonds/:secid` -> bond
- `GET /api/v1/bonds/:secid/candles` -> candles
- `GET /api/v1/securities/search` -> search results
- `GET /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.ts`
- `src/test/setup.ts`
- `src/test/server.ts`
- `src/test/handlers.ts`
- `src/test/test-utils.tsx`
- `src/test/factories.ts`
**Tests:**
- `api/client.test.ts` — basic get/post, 401 -> refresh -> retry, persistent 401 -> throws
- `api/auth.test.ts` — login, register, refresh, logout, getMe, updateProfile
### Phase 2: Auth + Hooks
- `context/AuthContext.test.tsx``login`/`register`/`logout`/`updateProfile` update context state
- `hooks/useAuth.test.tsx` — returns context values, throws outside provider
- `hooks/useSearch.test.ts` — empty query, results found, no results, error
- `hooks/useStock.test.ts` — data loaded, loading, error
- `hooks/useStockCandles.test.ts` — data loaded, empty array, error
- `hooks/useStockDividends.test.ts` — data loaded, no dividends, error
- `hooks/useBond.test.ts` — data loaded, loading, error
- `hooks/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 state
- `components/ProtectedRoute.test.tsx` — authenticated -> renders children; unauthenticated -> redirects to `/login?redirect=`
- `components/StockDetails.test.tsx` — renders all fields, missing optional fields, null/undefined data
- `components/BondDetails.test.tsx` — renders all fields, missing optional fields, null/undefined data
- `components/PriceChart.test.tsx` — renders chart with candle data, empty data array, error state
- `components/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 displayed
- `pages/RegisterPage.test.tsx` — renders form, validation errors, successful register redirects, password mismatch
- `pages/HomePage.test.tsx` — renders welcome content and navigation links
- `pages/StockPage.test.tsx` — loading skeleton, renders all sections (details, chart, dividends), error state, not found
- `pages/BondPage.test.tsx` — loading skeleton, renders all sections (details, chart), error state, not found
- `pages/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"` to `apps/frontend/package.json`
- Add `"test:frontend": "npm run test -w apps/frontend"` to root `package.json`
- Verify: `npm run test:frontend` passes
## Testing rules
1. **No `waitFor` for queries** — use `findBy*` / `findAllBy*` which already await
2. **`userEvent` over `fireEvent`** — simulates real interactions
3. **MSW handlers reset in `afterEach`**`server.resetHandlers()`
4. **`server.close()` in `afterAll`**
5. **No snapshot tests**
6. **Factory data only includes required fields** — tests explicitly add optional fields when needed
7. **Each `describe` tests one concern** — no 200-line tests