Cypress не имеет встроенной команды для прямого перетаскивания
элементов, как это реализовано в некоторых других фреймворках, поэтому
для тестирования функционала drag and drop требуется
использование низкоуровневых событий браузера: mousedown,
mousemove и mouseup. Эти события эмулируют
поведение пользователя при перетаскивании элементов на странице.
Для начала необходимо определить целевой элемент и место назначения. Например:
cy.get('#draggable') // элемент, который нужно перетащить
.trigger('mousedown', { which: 1 }) // имитация нажатия левой кнопкой мыши
cy.get('#droppable') // место, куда нужно перенести элемент
.trigger('mousemove') // перемещение мыши
.trigger('mouseup', { force: true }) // отпускание мыши
Важный момент — использование { force: true } для
mouseup, когда элемент может быть перекрыт другими
элементами, что часто встречается в современных UI с анимациями.
Для более точного контроля над перемещением можно использовать
координаты мыши. Cypress позволяет передавать clientX и
clientY в событии:
cy.get('#draggable')
.trigger('mousedown', { which: 1, pageX: 100, pageY: 100 })
.trigger('mousemove', { pageX: 200, pageY: 200 })
.trigger('mouseup')
Использование координат полезно при тестировании сложных интерфейсов с сетками, таблицами или drag and drop внутри канбан-досок, где позиция цели критична.
Для упрощения работы с drag and drop существуют готовые плагины, например cypress-drag-drop. После установки через npm:
npm install --save-dev cypress-drag-drop
Необходимо подключить плагин в
cypress/support/commands.js:
import 'cypress-drag-drop'
После этого можно использовать команду .drag():
cy.get('#draggable').drag('#droppable')
Этот метод автоматически обрабатывает все события мыши и корректно работает даже с элементами, которые имеют анимацию или динамическую подгрузку.
Некоторые элементы могут иметь ограничения по оси или родительскому контейнеру. В таких случаях координаты движения должны учитывать границы:
cy.get('#draggable')
.trigger('mousedown', { which: 1 })
.trigger('mousemove', { clientX: 150, clientY: 0 }) // только горизонтальное движение
.trigger('mouseup')
Для работы с iframe необходимо сначала получить доступ к его содержимому:
cy.get('iframe')
.its('0.contentDocument.body')
.then(cy.wrap)
.find('#draggable')
.trigger('mousedown', { which: 1 })
Такой подход обеспечивает корректное взаимодействие с элементами внутри встроенных фреймов.
После выполнения drag and drop необходимо проверить состояние страницы. Наиболее часто проверяют:
Пример проверки позиции через CSS:
cy.get('#draggable')
.should('have.css', 'transform')
.and('match', /translate\(200px, 200px\)/)
Проверка успешного сброса в контейнер:
cy.get('#droppable')
.should('contain', 'Dropped!')
Многие drag and drop интерфейсы имеют плавные анимации, что может привести к флейки-тестам. Для стабильности используют:
{ force: true } при triggerwait() для ожидания анимацииanimationDistanceThreshold в Cypress
конфигеПример:
cy.get('#draggable')
.trigger('mousedown', { which: 1, force: true })
cy.wait(500) // ожидание анимации
cy.get('#droppable')
.trigger('mousemove', { force: true })
.trigger('mouseup', { force: true })
cypress/support/commands.js.Можно создать собственную команду для drag and drop, чтобы сократить дублирование кода:
Cypress.Commands.add('dragTo', { prevSubject: 'element' }, (subject, target) => {
cy.wrap(subject)
.trigger('mousedown', { which: 1 })
cy.get(target)
.trigger('mousemove')
.trigger('mouseup', { force: true })
})
Использование:
cy.get('#draggable').dragTo('#droppable')
Такой подход делает тесты более читаемыми и поддерживаемыми.