Перенаправления и redirects

В процессе тестирования React-приложений часто возникает необходимость проверить, как компоненты ведут себя при перенаправлениях. Это может быть связано с различными сценариями, такими как изменение маршрута после успешной отправки формы, перенаправление с ошибкой 404 или редирект по бизнес-логике. В React Testing Library существует несколько подходов для тестирования таких случаев.

Настройка теста с использованием MemoryRouter

Для тестирования перенаправлений в React-приложениях, использующих библиотеку react-router-dom, важно правильно настроить маршрутизацию. В случае с React Testing Library это достигается с помощью компонента MemoryRouter.

MemoryRouter позволяет эмулировать поведение маршрутов в тестах без необходимости взаимодействовать с реальной навигацией в браузере. Он сохраняет состояние маршрутов в памяти, что делает его идеальным для тестирования логики редиректов.

Пример настройки маршрутизатора в тестах:

import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';

test('редирект после логина', () => {
  render(
    <MemoryRouter initialEntries={['/login']}>
      <MyComponent />
    </MemoryRouter>
  );

  // здесь можно тестировать поведение компонента
});

Моделирование редиректов с использованием useHistory или useNavigate

React Router предоставляет хук useHistory (до версии 6) и useNavigate (начиная с версии 6) для программного перенаправления пользователей на другой маршрут. В тестах нужно проверить, что редирект происходит корректно.

Для тестирования редиректов важно отслеживать изменения маршрута. MemoryRouter помогает задать начальный путь, но сам редирект нужно имитировать с помощью соответствующего хука.

Пример с использованием useNavigate в React Router v6:

import { render, screen, fireEvent } from '@testing-library/react';
import { MemoryRouter, Routes, Route, useNavigate } from 'react-router-dom';

function LoginComponent() {
  const navigate = useNavigate();
  
  const handleLogin = () => {
    // логика логина, затем редирект
    navigate('/dashboard');
  };

  return <button onCl ick={handleLogin}>Войти</button>;
}

function Dashboard() {
  return <h1>Добро пожаловать на панель управления!</h1>;
}

test('редирект после клика на кнопку', () => {
  render(
    <MemoryRouter initialEntries={['/login']}>
      <Routes>
        <Route path="/login" element={<LoginComponent />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </MemoryRouter>
  );

  fireEvent.click(screen.getByText('Войти'));
  expect(screen.getByText('Добро пожаловать на панель управления!')).toBeInTheDocument();
});

Этот пример демонстрирует, как происходит редирект с маршрута /login на маршрут /dashboard после нажатия кнопки. В тестах важно убедиться, что новый маршрут отобразился корректно.

Проверка перенаправлений с помощью waitFor и findBy

Иногда в процессе тестирования нужно подождать, пока произойдут асинхронные действия, такие как загрузка данных или ожидание ответа от сервера. В таких случаях можно использовать утилиты waitFor и findBy из React Testing Library.

Пример:

import { render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter, Routes, Route, useNavigate } from 'react-router-dom';

function LoginComponent() {
  const navigate = useNavigate();
  
  const handleLogin = async () => {
    // имитируем асинхронный процесс логина
    await new Promise(resolve => setTimeout(resolve, 1000));
    navigate('/dashboard');
  };

  return <button onCl ick={handleLogin}>Войти</button>;
}

function Dashboard() {
  return <h1>Добро пожаловать на панель управления!</h1>;
}

test('редирект после асинхронной операции', async () => {
  render(
    <MemoryRouter initialEntries={['/login']}>
      <Routes>
        <Route path="/login" element={<LoginComponent />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </MemoryRouter>
  );

  fireEvent.click(screen.getByText('Войти'));

  await waitFor(() => screen.getByText('Добро пожаловать на панель управления!'));
  expect(screen.getByText('Добро пожаловать на панель управления!')).toBeInTheDocument();
});

Здесь мы используем waitFor, чтобы дождаться окончания асинхронной операции перед проверкой наличия элемента на странице. Это особенно полезно при тестировании перенаправлений, которые зависят от внешних факторов.

Тестирование редиректов с ошибками

В некоторых случаях перенаправление может быть связано с ошибкой, например, при попытке перейти на несуществующий маршрут. В таких случаях важно проверить, что приложение корректно обрабатывает ошибку 404 или другие редиректы.

Пример с тестированием страницы с ошибкой 404:

import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';

function NotFound() {
  return <h1>Страница не найдена</h1>;
}

test('редирект на страницу 404 при неправильном маршруте', () => {
  render(
    <MemoryRouter initialEntries={['/unknown']}>
      <Routes>
        <Route path="*" element={<NotFound />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText('Страница не найдена')).toBeInTheDocument();
});

В данном примере тестируется поведение приложения при переходе по несуществующему маршруту. Мы ожидаем, что пользователь будет перенаправлен на страницу с ошибкой 404.

Проверка редиректов на основе состояния

В некоторых приложениях редирект может зависеть от состояния компонента или глобального состояния, например, состояния авторизации. Для таких случаев важно проверить, что редирект происходит только при определенных условиях.

Пример с редиректом при отсутствии авторизации:

import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route, useNavigate } from 'react-router-dom';

function ProtectedPage() {
  const navigate = useNavigate();
  const isAuthenticated = false;  // состояние авторизации
  
  if (!isAuthenticated) {
    navigate('/login');
  }

  return <h1>Защищенная страница</h1>;
}

function LoginPage() {
  return <h1>Страница входа</h1>;
}

test('редирект на страницу входа при отсутствии авторизации', () => {
  render(
    <MemoryRouter initialEntries={['/protected']}>
      <Routes>
        <Route path="/protected" element={<ProtectedPage />} />
        <Route path="/login" element={<LoginPage />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText('Страница входа')).toBeInTheDocument();
});

Здесь проверяется, что пользователь будет перенаправлен на страницу входа, если он не авторизован.

Заключение

Тестирование перенаправлений в React-приложениях — это важная часть обеспечения корректности маршрутизации и пользовательского опыта. Использование MemoryRouter и хуков маршрутизации, таких как useNavigate, позволяет эффективно моделировать и тестировать перенаправления в различных сценариях. Важно учитывать как синхронные, так и асинхронные редиректы, а также правильно обрабатывать ошибки и состояния приложения, чтобы гарантировать правильную работу навигации.