В TanStack Router маршруты можно защищать с помощью guard-функций, которые проверяют авторизацию пользователя или выполнение других условий перед переходом на страницу. Тестирование таких маршрутов требует имитации состояния приложения и проверки корректного поведения роутера в различных сценариях.
Защищённый маршрут создаётся с использованием свойства
beforeLoad или loader, где выполняется
проверка условий:
import { createRouter, createRouteConfig } from '@tanstack/router'
const isAuthenticated = () => Boolean(localStorage.getItem('token'))
const protectedRoute = createRouteConfig()
.path('/dashboard')
.beforeLoad(() => {
if (!isAuthenticated()) {
throw new Error('Unauthorized')
}
})
Ключевые моменты:
beforeLoad вызывается до загрузки
компонента, что позволяет перенаправлять пользователя или
блокировать доступ.test('should block access if user is not authenticated', () => {
localStorage.removeItem('token')
expect(() => protectedRoute.beforeLoad()).toThrow('Unauthorized')
})
test('should allow access if user is authenticated', () => {
localStorage.setItem('token', 'valid-token')
expect(() => protectedRoute.beforeLoad()).not.toThrow()
})
import { render, screen } from '@testing-library/react'
import { RouterProvider } from '@tanstack/router'
import { router } from './router'
test('redirects unauthorized user from dashboard', async () => {
localStorage.removeItem('token')
render(<RouterProvider router={router} />)
router.navigate('/dashboard')
expect(await screen.findByText('Login Page')).toBeInTheDocument()
})
Особенности:
router.navigate.Для упрощения тестирования защищённых маршрутов удобно создавать mock-сервисы авторизации:
export const authService = {
isAuthenticated: () => Boolean(localStorage.getItem('token')),
login: (token) => localStorage.setItem('token', token),
logout: () => localStorage.removeItem('token'),
}
Использование мока позволяет легко проверять все сценарии:
Если beforeLoad выполняет асинхронную проверку
(например, запрос к API), тесты должны работать с промисами:
const asyncProtectedRoute = createRouteConfig()
.path('/profile')
.beforeLoad(async () => {
const auth = await fetch('/api/auth/status').then(res => res.json())
if (!auth.loggedIn) throw new Error('Unauthorized')
})
test('blocks access if API returns unauthorized', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({ json: () => Promise.resolve({ loggedIn: false }) })
)
await expect(asyncProtectedRoute.beforeLoad()).rejects.toThrow('Unauthorized')
})
Важно: использовать await и
rejects.toThrow для корректной проверки асинхронных
guard-функций.
TanStack Router позволяет редиректить пользователя через
router.navigate в beforeLoad:
.beforeLoad(() => {
if (!isAuthenticated()) {
router.navigate('/login')
}
})
При тестировании редиректов нужно:
test('redirects to login if not authenticated', async () => {
localStorage.removeItem('token')
render(<RouterProvider router={router} />)
router.navigate('/dashboard')
expect(router.state.location.pathname).toBe('/login')
})
Для сокращения повторяющегося кода удобно создавать utility-функции для тестирования защищённых маршрутов:
export function testProtectedRoute(routePath, isAuthExpected) {
localStorage.removeItem('token')
if (isAuthExpected) localStorage.setItem('token', 'token')
render(<RouterProvider router={router} />)
router.navigate(routePath)
return router.state.location.pathname
}
// Пример использования
test('dashboard route', () => {
expect(testProtectedRoute('/dashboard', false)).toBe('/login')
expect(testProtectedRoute('/dashboard', true)).toBe('/dashboard')
})
Такой подход упрощает поддержание большого числа защищённых маршрутов и уменьшает дублирование тестов.
await и rejects.toThrow.Эта методика обеспечивает надёжное тестирование защищённых маршрутов в TanStack Router, позволяя гарантировать корректное управление доступом и стабильное поведение приложения.