Cypress: Move the cursor (Drag and Drop)

Created on 29 Oct 2017  路  35Comments  路  Source: cypress-io/cypress

I read the issue #386 but triggering the JavaScript events seem a bit complicated in some situation (when you don't know the underlying code).

Use case

My goal is to assert a drag'n drop sorting library work as expected : http://grafikart.github.io/ReorderJS/index.html

Current behavior:

I can make my test pass with the electron browser but it will fail on chrome (nothing moves)

      cy.visit('http://grafikart.github.io/ReorderJS/index.html')
      cy.get('.column:eq(3)').as('toMove')
      cy.get('@toMove')
            .should('attr', 'data-position', '2')
            .trigger('mousedown')
      cy.get('.column:eq(2)').trigger('mousemove')
      cy.get('@toMove')
           .trigger('mouseup')
           .should('attr', 'data-position', '1')

How to reproduce:

You can produce a passing test on electron that fails on chrome with this simplified test (default resolution not changed : 1000 x 660)

    it('Should sort', function () {
      cy.visit('http://grafikart.github.io/ReorderJS/index.html')
      cy.get('.column:eq(3)')
        .should('attr', 'data-position', '2')
        .trigger('mousedown')
        .trigger('mousemove', {clientX: 500, clientY: 50})
        .trigger('mouseup')
        .should('attr', 'data-position', '1')
    })
chrome needs investigating drag-and-drop bug

Most helpful comment

Dear great community, i investigated the drag and drop functionality and noticed that it doesnt drop to the targets position.
So when the cursor wants to drop it, it drops at the origin position where the element actual was.
Workaround is to tell cypress to set all 3 position properties to the new position ->>>
Like this for example :

   cy.get( selector )
    .trigger('mousedown', { button: 0 })
     .wait(1500)
     .trigger('mousemove', {
    clientX: 80,
      clientY: 90,
     screenX: 80,
    screenY: 90,
   pageX: 80,
     pageY: 90
     })
   .trigger('mouseup', { force: true });

I tried to set only on of them and it failed, tried only one property at a time.

IF you set ALL 3 properties, you will actual get this to work .

If this works for everybody, i recommend cypress team to add this to their documentation.

All 35 comments

Thank you for providing a reproducible example, this helps a lot!

I was able to reproduce the behavior you describe with the test failing in Chrome and passing in Electron on Cypress version 1.0.3.

I wonder if you could create a new issue and move the proposed API changes for drag and drop there? We'd like to keep this issue to track the potential bug with the current .trigger() command - but I do like the proposals for drag and drop and would like a separate thread to discuss.

I'll create a new issue asap, do you want me to edit this issue to remove the proposal ? (I'll only keep the trigger bug here).

Sure, thanks!

Is there anyone tried Cypress with vuedraggable?
Base on cypress-example-recipes, I've tried both way

cy
        .get('#mydraggable')
        .trigger('mousedown', { which: 1 })
        .trigger('mousemove', { clientX: x, clientY: y })
        .trigger('mouseup', {force: true})
cy.get('#mydraggable')
        .trigger('dragstart')
      cy.get('#mytarget')
        .trigger('drop')

It seems not working here.
Please reply if there is any solution for vuedraggable. thanks a lot

Hi,
I tried to create an example for Vuedraggable, but drag and drop is not really working though.
It would be great if anyone can take a look and bring up some way to cope with the issue I'm encountered.
Vuedraggable_Testing

We have a recipe for drag and drop here. https://docs.cypress.io/examples/examples/recipes.html#

Also this is some code I've used in the past. My guess is you are not sending in the dataTransfer object correctly.

      const dt = {
        types: []
      }

      function dragAndDropFromTo (num, columnIndex) {
        cy.get(`#${num}.item`).trigger('dragstart', { dataTransfer: dt})
        cy.get(`.desk:eq(${columnIndex}) .desk-items`).trigger('drop')
      }

Thanks @brian-mann
I'm still not sure how to do with my example.
What exactly should be placed in dataTransfer?
Would you mind share some more information about it?
Thanks a lot.

There isn't a generic answer for this - it depends on what your app is bound to, and what events and properties it's listening for. I would inspect the Vue code for the plugin to see what it expects in the event. Whatever that is - that's what you send.

@danceric0919 I'd suggest taking a look at my process for how I worked through getting Cypress to drag and drop in this link, it explains a bit more what Brian means above about how it 'depends on what your app is bound to' -> https://github.com/cypress-io/cypress/issues/669#issuecomment-342944640

@brian-mann @jennifer-shehane
I spent some time inspecting the implementation of Vuedraggable and found it a little bit hard to provide everything the plugin need.
I'll continue trying to make it work, please let me know if any working example.
thanks a lot.

note that drag/drop triggers more events then just mousedown / mouseup
(stuff like pointerdown etc.)

I have tried everything that Vuedraggable still not working. :(

Just found this thread, I'm also a bit confused by what dataTransfer is?

@iancrowther Sure, this is probably the best doc in terms of an overview of the Drag and Drop API in general: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

The HTML drag and drop interfaces are DragEvent, DataTransfer, DataTransferItem and DataTransferItemList.

Specific DataTransfer docs: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer

ah great, many thanks

For me, this worked for simulating drag and drop within a list in vue draggable/rubaxa sortable.

I am new to cypress..I am trying to implement Dran-n-drop feature using Nestable2 plugin(https://github.com/RamonSmit/Nestable2)

.trigger('mousedown')
.trigger('mousemove', {clientX: 500, clientY: 50})
 .trigger('mouseup')

I the above code when I use mousemove event and provide x and y positions there is no change in location of the element.Can anyone help me out

Facing the same issue not working if browser auto scrolls the page it basically needs to changes the clientX and ClientY cords if your page scroll.

any update?

What is the purpose of this property { which: 1 } in drag and drop ?
.trigger('mousedown', { which: 1 })

Been digging into this as well... I still can't get DnD to work with Cypress, but I believe @scrumvisualize that the {which} property refers to which mouse button is use 1=left, 2=middle, 3=right. Non-IE only.

Drag and drop needs to be simplified with cypress. I've been trying to get it to work with no luck for some time. What we really need is a simple cy.drag() and a cy.dragToElement()

Testcafe has these functions and they're super easy to use and understand:
https://devexpress.github.io/testcafe/documentation/test-api/actions/drag-element.html#drag-an-element-onto-another-one

Been digging into this as well... I still can't get DnD to work with Cypress, but I believe @scrumvisualize that the {which} property refers to which mouse button is use 1=left, 2=middle, 3=right. Non-IE only.

Somehow, I managed the Drag n Drop to work in our application. But if we use the same code on other 'web application' it will not work. So the team has to simplify and come up with easy solution in drag and drop

@tnrich I'd keep track of the cy.drag proposal issue here: https://github.com/cypress-io/cypress/issues/857

The following worked for me. I'm using the native drag and drop API with draggable attribute on my draggable elements.

Cypress.Commands.add("dragTo", { prevSubject: "element" }, (subject, targetEl) => {
    cy.wrap(subject).trigger("dragstart");
    cy.get(targetEl).trigger("drop");
  }
);

Usage:

cy.get(".source").dragTo(".target");

I'm using the HTML5 backend and a variation of @asumaran's snippet worked for me:

Cypress.Commands.add("dragTo", {prevSubject: "element"}, (subject, targetEl) => {
  cy.wrap(subject).trigger("dragstart");
  cy.get(targetEl).trigger("drop");
  cy.get(targetEl).trigger("dragend");
});

Any updates on this?

Just a note here.
There are also use cases when you don't have a target element to drop to. For example if you have a canvas with a shape, and u want to move the shape. So a proper drag command should also have the option to drop on specific coordinates imo.
thanks

I have exactly the same issue as @miromarchi .I need to 'drag' a vertex-marker on a map and 'drop' to a given set of co-ords on the map. How do I make a Cypress object from just a pair of pixel co-ordinates?
In the *.ts file that sets the listeners listens to pointerdown, dragstart, drag and dragend, pointermove & keypress events.

Yea same here i have a timeline and i have element position and using drag i want to move it 10px right for example then check the dom if it is rendered there.So far i haven`t found a solution....
image

With puppeteer i was able to do it easily but i don`t want to use 2 libraries to do this.

Dear great community, i investigated the drag and drop functionality and noticed that it doesnt drop to the targets position.
So when the cursor wants to drop it, it drops at the origin position where the element actual was.
Workaround is to tell cypress to set all 3 position properties to the new position ->>>
Like this for example :

   cy.get( selector )
    .trigger('mousedown', { button: 0 })
     .wait(1500)
     .trigger('mousemove', {
    clientX: 80,
      clientY: 90,
     screenX: 80,
    screenY: 90,
   pageX: 80,
     pageY: 90
     })
   .trigger('mouseup', { force: true });

I tried to set only on of them and it failed, tried only one property at a time.

IF you set ALL 3 properties, you will actual get this to work .

If this works for everybody, i recommend cypress team to add this to their documentation.

@Codex0607 Many thanks! Can confirm this is the only solution that worked for our team!

Dear community ,
can anyone help me in automating the empty e-signature field .

signature

In case you are looking for a solution - this worked for me:

    cy.get('@draggable').trigger('dragstart');
    cy.get('@drop-zone').trigger('dragenter', { force: true });
    cy.get('@drop-zone').trigger('drop', { force: true });

dragenter makes app think that the drop zone is hovered, { force: true } has been added because of additional layers that cover the drop zone . I'm using React DnD behind the scenes :wink:

After too long of trying for me, here's my solution.
Use button 0, not 1,
Force every click,
don't forget to click on the target element before mouseup.
This will work for some not for others but I hope it helps!

cy.visit("/your-page");
    cy.get("yourSelector")
      .trigger("mousedown", { button: 0 }, { force: true })
      .trigger("mousemove", 200, -200, { force: true })
    cy.get("yourTargetForDrop").click()
      .trigger("mouseup", { force: true });

The problem for me was that I was dropping on a wrong element.
Following code worked for me.

const dataTransfer = new DataTransfer; 
cy.get('.draggable').trigger('dragstart', { dataTransfer });
cy.get('.droppable').trigger('drop', { dataTransfer, force: true });
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jennifer-shehane picture jennifer-shehane  路  3Comments

rbung picture rbung  路  3Comments

jennifer-shehane picture jennifer-shehane  路  3Comments

zbigniewkalinowski picture zbigniewkalinowski  路  3Comments

brian-mann picture brian-mann  路  3Comments