# Frontend testing Перед нетривиальными тестами сформулируй [общий Test Intent](engineering.md#test-intent); отдельный документ не требуется. Trivial low-risk изменения обходятся без него. ## Выбор проверки Читай при изменении тестов, доступа, денежных расчётов, восстановления интерфейса или исправлении подтверждённой регрессии. Выбирай boundary по наблюдаемому исходу, а не внутреннему helper/component state. | Изменение | Дешёвая надёжная проверка | | --- | --- | | Чистое domain преобразование/денежный расчёт | Unit case с граничными значениями | | Форма, route access, recovery, UI-state selection | RTL component/route test через видимое поведение | | Headers, refresh/retry, query invalidation | MSW на transport boundary | | Критический browser flow, который JSDOM не доказывает | Browser/e2e proof существующими доступными средствами; отдельный frontend e2e runner пока не принят | | Только declarative props/Storybook fixture | Type/build + semantic/visual review; unit test без собственной логики не нужен | Tests colocated с source (`*.test.tsx` для UI, существующий `*.test.ts` для pure code). Arrange/Act/Assert, semantic queries (`getByRole`, label); test id — когда пользовательский selector не выражает нужный элемент. Не тестируй Mantine/React internals, private calls или passthrough wrapper ради coverage. ## Инструменты и isolation Используй `fireEvent` из объявленного `@testing-library/react` и ожидай async outcome через `findBy*`/`waitFor`. `user-event` доступен транзитивно через Storybook, но не принят как direct dependency; не добавляй его ради старого Markdown. MSW запускается только Vitest setup в `src/test/`, без browser worker/mock dev mode. Проверяй сеть MSW handlers, а не mock внутренностей query client или реальный backend. Независимый case получает свой `createTestQueryClient`; rerender сохраняет экземпляр и seeded cache. Providers выбирай явно. Existing teardown выполняет RTL cleanup, auth reset и MSW reset: [test client](../../src/test/query.tsx). Тестовый клиент отключает retries запросов и мутаций для предсказуемого исхода; это не меняет [политику обновления приложения](data.md#auth-и-invalidation). ## Важные случаи Форма: field errors, отсутствие request при invalid input, submit blocking, сохранение ввода при network error. Routes: доступ/redirect и neutral auth bootstrap. Finance: loading/error/empty/loaded, cached snapshot при refetch failure, retry, отсутствие старых данных после session/connection switch. ErrorBoundary: test-only throwing route, безопасная ссылка и reset после navigation. Примеры: [AuthForm test](../../src/features/auth/ui/AuthForm/AuthForm.test.tsx), [client test](../../src/shared/api/client.test.ts), [resource lifecycle test](../../src/entities/portfolio/api/resource-lifecycle.test.tsx). Не копируй fixtures/сценарии целиком, если задача затрагивает один исход. ## Команды Итерация: `npm run test:run -- ` без coverage. Полный `npm run verify` запускает Vitest один раз через `test:coverage`. Coverage floors — anti-regression constraint, не повод тестировать декларации. После UI изменения отдельно проверь доступность/визуальные состояния, которых JSDOM не доказывает.