В React часто возникает необходимость отображать элементы, которые должны быть визуально “выведены” за пределы текущего контекста компонента, сохраняя при этом его логику. Примером таких элементов являются модальные окна и тосты. В этой главе рассматривается подход, который используется для создания и тестирования таких компонентов с помощью React Testing Library и возможностей React Portals.
React Portal позволяет рендерить дочерние элементы в другом DOM-узле, которое находится за пределами родительского компонента. Это полезно, когда необходимо, чтобы компонент, например, модальное окно или тост, был размещён в другом месте DOM-дерева, но при этом продолжал использовать контекст родительского компонента.
Использование портала позволяет избежать таких проблем, как неправильное позиционирование в случае использования модальных окон, всплывающих подсказок, контекстных меню и т.п. Они могут быть рендерены в body или в любой другой контейнер, что обеспечит правильное отображение поверх других элементов страницы.
Пример использования Portals:
import ReactDOM from 'react-dom';
const Modal = ({ children }) => {
return ReactDOM.createPortal(
<div className="modal">
{children}
</div>,
document.body
);
};
Здесь компонент Modal рендерится в body вне зависимости
от того, где он был вызван в компонентном дереве.
Для тестирования компонентов, использующих Portals, важно понимать,
что стандартные методы тестирования, такие как getByText,
могут не сработать сразу, так как элементы могут быть расположены вне
стандартной области видимости. React Testing Library предоставляет
полезные методы, которые позволяют взаимодействовать с такими
компонентами.
Пример теста для проверки, что модальное окно появляется в DOM:
import { render, screen, fireEvent } from '@testing-library/react';
import Modal from './Modal';
test('проверка отображения модального окна', () => {
render(
<Modal>
<h1>Модальное окно</h1>
</Modal>
);
// Проверка, что модальное окно отобразилось в DOM
expect(screen.getByText('Модальное окно')).toBeInTheDocument();
});
В данном примере, getByText находит элемент с текстом
внутри модального окна, даже если оно рендерится с использованием
Portals.
Для проверки работы модальных окон важно учесть действия, которые может выполнять пользователь. Например, закрытие окна при нажатии на кнопку.
import { render, screen, fireEvent } from '@testing-library/react';
import Modal from './Modal';
test('закрытие модального окна при нажатии на кнопку закрытия', () => {
const { rerender } = render(
<Modal>
<button>Закрыть</button>
</Modal>
);
// Имитируем клик по кнопке
fireEvent.click(screen.getByText('Закрыть'));
// Проверка, что модальное окно больше не отображается
expect(screen.queryByText('Модальное окно')).toBeNull();
});
Для имитации клика по элементу используется fireEvent, а
для проверки, что элемент больше не отображается в DOM, применяем
queryByText. Это важно, потому что queryByText
возвращает null, если элемент не найден, в отличие от
getByText, который выбрасывает ошибку.
Тосты (или уведомления) — это короткие всплывающие сообщения, которые обычно исчезают через несколько секунд. Часто их используют для отображения статусов или предупреждений в интерфейсе.
Тестирование тостов имеет схожие принципы, однако важно учитывать временной аспект — они появляются и исчезают в течение времени. Это требует использования асинхронных методов тестирования.
Для реализации и тестирования простого тоста, который появляется на
экране и исчезает через некоторое время, можно воспользоваться
асинхронным ожиданием с использованием waitFor.
import { render, screen, waitFor } from '@testing-library/react';
import Toast from './Toast';
test('отображение тоста и его скрытие через время', async () => {
render(<Toast message="Тост появился!" />);
// Проверяем, что тост отобразился на экране
expect(screen.getByText('Тост появился!')).toBeInTheDocument();
// Ждём исчезновения тоста
await waitFor(() => {
expect(screen.queryByText('Тост появился!')).toBeNull();
}, { timeout: 3000 });
});
В этом примере waitFor используется для ожидания, пока
тост исчезнет. Важно установить правильное время таймаута, которое
соответствует длительности отображения тоста.
Тосты могут быть связаны с асинхронными операциями, например, с запросами на сервер. В таких случаях важно проверить как компонент реагирует на изменение состояния.
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import Toast from './Toast';
test('тост отображается после успешного запроса', async () => {
const fetchSuccess = jest.fn().mockResolvedValue('Успех');
render(<Toast fetchData={fetchSuccess} />);
// Имитируем действие, которое вызовет асинхронный запрос
fireEvent.click(screen.getByText('Получить тост'));
// Ожидаем появления тоста
await waitFor(() => {
expect(screen.getByText('Успешно!')).toBeInTheDocument();
});
});
Здесь мы тестируем компонент тоста, который отображается после
выполнения асинхронного запроса. Для мокирования асинхронных функций
используется jest.fn().mockResolvedValue.
При тестировании компонентов, использующих асинхронные события, таких
как получение данных с сервера или задержка перед исчезновением тоста,
важно использовать такие методы, как waitFor,
findBy, и асинхронные утверждения. Эти инструменты
позволяют ожидать появления и исчезновения элементов в DOM, что критично
для корректного тестирования динамически изменяющихся компонентов.
Стилизованные компоненты также поддаются тестированию. Например, модальное окно может иметь уникальные стили для отображения, такие как затемнение фона или центровка окна. В таких случаях можно проверить наличие нужных классов:
import { render, screen } from '@testing-library/react';
import Modal from './Modal';
test('проверка стиля модального окна', () => {
render(<Modal />);
const modalElement = screen.getByRole('dialog');
// Проверка, что модальное окно имеет нужный стиль
expect(modalElement).toHaveStyle('background-color: rgba(0, 0, 0, 0.5)');
});
В этом тесте проверяется наличие конкретного стиля у модального окна.
React Testing Library позволяет работать с DOM-элементами, как с
обычными элементами, добавляя возможности для проверки стилей с помощью
метода toHaveStyle.
Portal компоненты, такие как модальные окна и тосты, играют ключевую роль в взаимодействии с пользователем в современных интерфейсах. Использование React Testing Library помогает обеспечить корректность их работы, даже если они рендерятся в другом месте DOM. Важно понимать, как взаимодействовать с такими элементами и правильно тестировать их отображение и функциональность с учётом асинхронных операций, времени жизни компонентов и их стилей.