Drag and Drop (перетаскивание элементов) является одной из более
сложных интерактивных функциональностей для тестирования в
React-приложениях. Стандартные методы взаимодействия с DOM, такие как
fireEvent.click или fireEvent.change, часто
недостаточны для корректной симуляции полного процесса перетаскивания.
React Testing Library предоставляет инструменты для работы с событиями,
но реализация drag and drop требует понимания нативных событий браузера
и их последовательности.
Для реализации drag and drop браузер использует несколько событий:
dragstart — возникает, когда начинается перетаскивание
элемента.drag — непрерывно срабатывает во время перемещения
элемента.dragenter — срабатывает, когда перетаскиваемый элемент
входит в область другого элемента.dragover — срабатывает при нахождении элемента над
зоной, в которую его можно сбросить. Для разрешения drop необходимо
вызвать event.preventDefault().dragleave — возникает, когда элемент покидает область
цели.drop — происходит, когда элемент сбрасывается в целевой
контейнер.dragend — срабатывает по завершении операции,
независимо от успешного сброса.Понимание этой последовательности важно, так как тесты должны имитировать не отдельные события, а их логическую цепочку.
React Testing Library не предоставляет встроенного метода
dragAndDrop, поэтому приходится использовать низкоуровневые
события через fireEvent или userEvent.
Последовательность действий выглядит следующим образом:
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyComponent from './MyComponent';
test('Перетаскивание элемента', async () => {
render(<MyComponent />);
const draggable = screen.getByTestId('draggable');
const droppable = screen.getByTestId('droppable');
fireEvent.dragStart(draggable);
fireEvent.dragEnter(droppable);
fireEvent.dragOver(droppable);
fireEvent.drop(droppable);
fireEvent.dragEnd(draggable);
expect(droppable).toHaveTextContent('Элемент перемещён');
});
Важный момент: dragOver должен содержать
preventDefault, иначе drop не сработает. В тестах это можно
эмулировать вручную:
fireEvent.dragOver(droppable, { preventDefault: () => {} });
DataTransferHTML5 Drag and Drop работает с объектом DataTransfer,
который хранит данные перетаскиваемого элемента. Без передачи этого
объекта многие библиотеки и кастомные компоненты не смогут корректно
обработать drop. Пример с передачей DataTransfer:
const data = new DataTransfer();
fireEvent.dragStart(draggable, { dataTransfer: data });
fireEvent.dragEnter(droppable, { dataTransfer: data });
fireEvent.dragOver(droppable, { dataTransfer: data });
fireEvent.drop(droppable, { dataTransfer: data });
fireEvent.dragEnd(draggable, { dataTransfer: data });
Использование DataTransfer обеспечивает правильное
взаимодействие с компонентами, которые ожидают доступ к данным
перетаскиваемого элемента через event.dataTransfer.
userEventБиблиотека @testing-library/user-event обеспечивает
более высокоуровневую имитацию пользовательских действий. Для drag and
drop можно использовать комбинацию pointer событий:
await userEvent.pointer([
{ keys: '[MouseLeft]', target: draggable, type: 'pointerDown' },
{ target: droppable, type: 'pointerMove' },
{ keys: '[/MouseLeft]', target: droppable, type: 'pointerUp' },
]);
Такой подход ближе к реальному поведению пользователя и учитывает
нюансы браузерного API. Особенно полезен при тестировании интеграции с
библиотеками drag and drop, такими как react-beautiful-dnd
или dnd-kit.
Droppable и Draggable. Тесты должны рендерить
всю структуру, иначе события не обработаются.userEvent, передавая координаты перемещения.Drag and drop чаще всего меняет порядок элементов или перемещает их в другую область. Проверка включает:
toHaveTextContent,
toContainElement).container.querySelectorAll и
сравнение массивов).jest.fn() для проверки
onDragEnd или onDrop).dragOver с preventDefault — drop
не срабатывает.DataTransfer — многие кастомные компоненты
игнорируют событие.click вместо pointer — не
симулирует реальные действия пользователя.DataTransfer для совместимости с HTML5
API.userEvent.pointer для интеграционных
тестов.Drag and drop требует внимательного подхода, так как простая имитация
клика или изменения DOM не отражает реальное поведение. Сочетание
fireEvent и userEvent обеспечивает гибкость и
точность тестов, а использование DataTransfer и правильной
последовательности событий гарантирует корректную симуляцию.