I am struggling to test drag and drop with Cypress and Angular Material Drag and Drop. So the goal is to move \"Get to work\" from Todo to Done. I have created the following
It seems cy.trigger isn't sending the proper events at the correct target elements. I expect this to be fixed in version 4.0
...but, I've written up a small plugin for drag and drop.
It works by adding a dragTo command like so:
///
it('works', () => {
cy.visit('https://angular-oxkc7l-zirwfs.stackblitz.io/')
cy.contains('To do', { timeout: 15000 }) // ensure page is loaded -__-
const item = '.example-box:not(.cdk-drag-placeholder)'
cy.get('#cdk-drop-list-1').children(item).should('have.length', 5)
cy.get('.example-box:contains("Get to work")').dragTo('.example-box:contains("Get up")')
cy.get('#cdk-drop-list-1').children(item).should('have.length', 6)
// interpolates 10 extra mousemove events on the way
cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1', { steps: 10 })
cy.get('#cdk-drop-list-1').children(item).should('have.length', 7)
// sets steps >= 10
cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1', { smooth: true })
cy.get('#cdk-drop-list-1').children(item).should('have.length', 8)
cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1')
cy.get('#cdk-drop-list-1').children(item).should('have.length', 9)
})
To add it, try putting this in your support/index.js or pasting it at the bottom of a spec file (warning: poor code quality):
const getCoords = ($el) => {
const domRect = $el[0].getBoundingClientRect()
const coords = { x: domRect.left + (domRect.width / 2 || 0), y: domRect.top + (domRect.height / 2 || 0) }
return coords
}
const dragTo = (subject, to, opts) => {
opts = Cypress._.defaults(opts, {
// delay inbetween steps
delay: 0,
// interpolation between coords
steps: 0,
// >=10 steps
smooth: false,
})
if (opts.smooth) {
opts.steps = Math.max(opts.steps, 10)
}
const win = subject[0].ownerDocument.defaultView
const elFromCoords = (coords) => win.document.elementFromPoint(coords.x, coords.y)
const winMouseEvent = win.MouseEvent
const send = (type, coords, el) => {
el = el || elFromCoords(coords)
el.dispatchEvent(
new winMouseEvent(type, Object.assign({}, { clientX: coords.x, clientY: coords.y }, { bubbles: true, cancelable: true }))
)
}
const toSel = to
function drag (from, to, steps = 1) {
const fromEl = elFromCoords(from)
const _log = Cypress.log({
$el: fromEl,
name: 'drag to',
message: toSel,
})
_log.snapshot('before', { next: 'after', at: 0 })
_log.set({ coords: to })
send('mouseover', from, fromEl)
send('mousedown', from, fromEl)
cy.then(() => {
return Cypress.Promise.try(() => {
if (steps > 0) {
const dx = (to.x - from.x) / steps
const dy = (to.y - from.y) / steps
return Cypress.Promise.map(Array(steps).fill(), (v, i) => {
i = steps - 1 - i
let _to = {
x: from.x + dx * (i),
y: from.y + dy * (i),
}
send('mousemove', _to, fromEl)
return Cypress.Promise.delay(opts.delay)
}, { concurrency: 1 })
}
})
.then(() => {
send('mousemove', to, fromEl)
send('mouseover', to)
send('mousemove', to)
send('mouseup', to)
_log.snapshot('after', { at: 1 }).end()
})
})
}
const $el = subject
const fromCoords = getCoords($el)
const toCoords = getCoords(cy.$$(to))
drag(fromCoords, toCoords, opts.steps)
}
Cypress.Commands.addAll(
{ prevSubject: 'element' },
{
dragTo,
}
)