3

I am trying to make DnD work in Cypress and React app.

At first I was trying all examples around those with simulating by mouse events but it is not working in general in my environment. :(

I was able to write a test where I know the drag element and drop element using this code.

const dataTransfer = new DataTransfer();


   cy.get(dragSelector).trigger("dragstart", {
       dataTransfer,
       force: true,
   });


   cy.get(dropSelector).trigger("drop", {
       dataTransfer,
       force: true,
   });

And it's working well. Similar sample code I have found in Cypress cookbook.

But now I need to do DnD where I don't know the drop zone / elem. Its resizer and I need dnd by offset for example 20px on Y axis. (Resize element height by 20px)

So is it possible to do it somehow like this ?

const dataTransfer = new DataTransfer();

   // drag selector is element with draggable attribute and I need move it by 20px from top to down  
   cy.get(dragSelector).trigger("dragstart", {
       dataTransfer,
       force: true,
   }).trigger("drop",{...how to set drop offset if is possible...})

trigger drop event as some parameters like offsetX and offsetY or x or y and pageX or pageY ... but I was not successful.

Like I sad I am not able use it by simulating mouse events in my app from unknow reason its not working.

SSpall
  • 139
  • 1
  • 8
Ivan Mjartan
  • 1,125
  • 1
  • 12
  • 23

1 Answers1

4

This is how I'm testing a divider (resizer) using mouse events.

I did add cypress-real-events for mousedown and mouseup, but it may be ok to use .trigger('mousedown') instead.

My app is React hooks, and I found it needed a small wait before checking. I think that can be optimized with a .should(), but for my tests now this is working.

Cypress.Commands.add('checkPosition', 
  {prevSubject: true}, 
  (subject, start, delta = {dx:0}) => {

    cy.wait(75, {log:false}).then(function () {
      
      const deltaValue = delta.dx || delta.dy
      const measurePoint = measurePoint || (delta.dx ? 'pageX' : 'pageY')
      const current = getClientRect(subject[0])[measurePoint]
      const expected = start[measurePoint] + deltaValue 
      expect(current).to.be.closeTo(expected, variance)
    })
  }
)

let start;
cy.get(divider)
  .then(subject => start = getClientRect(subject[0]))
  .realMouseDown({position:'center'})
  .then($el => {
    cy.wrap($el, {log:false})
      .trigger('mousemove', { pageX: start.pageX +20 })
      .checkPosition(start, {dx:20})
      .trigger('mousemove', { pageX: start.pageX +40 })
      .checkPosition(start, {dx:40})
      // more steps as required
      .realMouseUp()
      .checkPosition(start, {dx:100})
  })

You can do x or y movements in the test, my divider is either horizontal or vertical (can it be anything else?)

The calculation on position isn't exact, so I've added close.to assertion in /cypress/support/e2e.js.

const closeTo = (_chai, utils) => {
  function assertIscloseTo(expected, delta, msg) {
    msg = msg || utils.flag(this, 'message')
    msg = msg ? `${msg}: ` : ''
    _chai.assert(
      Math.abs(this._obj - expected) <= delta
        , msg + 'expected ' + this._obj + ' to be close to ' + expected + ' +/- ' + delta
        , msg + 'expected ' + this._obj + ' not to be close to ' + expected + ' +/- ' + delta
    )
  }
  _chai.Assertion.addMethod('closeTo', assertIscloseTo)
}
chai.use(closeTo)

getClientRect()

This is just to provide some rounding on the native getBoundingClientRect() function.

I also added centerX and centerY calc as it is sometimes useful to vary the mousedown location, if the React app records it's start position from the mousedown event.

function getClientRect(el) {
  const bb = el.getBoundingClientRect()
  return {
    top: Math.round(bb.top),
    bottom: Math.round(bb.bottom),
    left: Math.round(bb.left),
    right: Math.round(bb.right),
    height: Math.round(bb.height),
    width: Math.round(bb.width),
    x: Math.round(bb.x),
    y: Math.round(bb.y),
    centerX: Math.round(bb.left + (bb.width/2)),
    centerY: Math.round(bb.top + (bb.height/2)),
  }
}
Fody
  • 23,754
  • 3
  • 20
  • 37
  • Hi, thanks very much for your reply. Pls could you give me body of method getClientRect() ? I am not able collect correct rect. Thanks very much – Ivan Mjartan Feb 02 '23 at 09:33
  • Sorry, it's based on HTML native `el.getBoundingClientRect()` just rounding the floating point numbers, and I added `centerX` and `centerY` calculation so I could check the effect of changing the mousedown position. – Fody Feb 02 '23 at 18:12