webdriverio: drag and drop bug

Environment (please complete the following information):

Config of WebdriverIO standard, no major configurations.

Describe the bug When using $(selector).dragAndDrop(target) The function was able to grab the selector. However the dragging and dropping is not behaving correctly. Instead of dragging and dropping on the target, it is dragging and dropping on the location of the mouse. Which defeats the purpose of dragging and dropping on the target. Looking at the dragAndDrop() function in the source code I noticed something was a bit off as well.

     /**
     * W3C way of handle the drag and drop action
     */
    return this.performActions([{
        type: 'pointer',
        id: 'finger1',
        parameters: { pointerType: 'mouse' },
        actions: [
            { type: 'pointerMove', duration: 0, x: sourceX, y: sourceY },
            { type: 'pointerDown', button: ACTION_BUTTON },
            { type: 'pause', duration: 10 }, // emulate human pause
            { type: 'pointerMove', duration, origin: 'pointer', x: targetX, y: targetY },
            { type: 'pointerUp', button: ACTION_BUTTON }
        ]
    }]).then(() => this.releaseActions())

The second pointerMove is using origin pointer. So I wonder if that is what causing the dropping to happen on the mouse coordinate instead.

To Reproduce

  1. use dragAndDrop function in your test.
  2. place your mouse somewhere from the target selector.
  3. You will notice that it will try to drag and drop the selector where the mouse is located.

Expected behavior It should be able to drag a selector to a target selector.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 5
  • Comments: 113 (78 by maintainers)

Most upvoted comments

if it only works in Headless mode or as mentioned above minimised windows then its a major bug as most ppl run their test on normal sized windows. So this is a bug as it does not work as intended (the specs does not say its for headless or minimised windows only)

@erwinheitzman thanks for checking. Just verified that this snippet below fixes (in my case) what I understand to be the problem: that the drop happens in the wrong place. You should be able to copy and paste:

  const getCoordsForElement = async (elementId) => {
    const rect = await browser.getElementRect(elementId);
    const X = parseInt(rect.x + (rect.width / 2), 10);
    const Y = parseInt(rect.y + (rect.height / 2), 10);
    return [X, Y];
  };

  browser.overwriteCommand('dragAndDrop', async function (_, target, duration = 100) {
    if (!target || target.constructor.name !== 'Element') {
      throw new Error('command dragAndDrop requires an WebdriverIO Element as first parameter')
    }

    const ACTION_BUTTON = 0;

    /**
     * get coordinates to drag and drop
     */
    const [sourceX, sourceY] = await getCoordsForElement(this.elementId);
    const [targetX, targetY] = await getCoordsForElement(target.elementId);
    const [diffX, diffY] = [targetX - sourceX, targetY - sourceY];

    return this.performActions([{
        type: 'pointer',
        id: 'finger1',
        parameters: { pointerType: 'mouse' },
        actions: [
            { type: 'pointerMove', duration: 0, x: sourceX, y: sourceY },
            { type: 'pointerDown', button: ACTION_BUTTON },
            { type: 'pause', duration: 10 }, // emulate human pause
            { type: 'pointerMove', duration, origin: 'pointer', x: diffX, y: diffY }
        ]
    }]).then(() => target.click());
  }, true);

I’ ve tried to do like @ZawadzkiB and @ducle91 and none of this methods works on chrome 😦. Why this issue is closed since it is still aplicable to chrome? @christian-bromann

I am using webdriver io v6 with newest chrome.

I can confirm that I have run into this issue as well and that the origin property is not the issue. I have tried every single combination I could think of to get this to work but both on Firefox and Chrome I could not get it working.

The pointerMove and pointerDown are executed as expected however after this, the pointerMove doesn’t work as expected and seems to stick to your mouse cursor. I have however seen elements light up when trying to dragAndDropping something on top of it (link a link, it does light up).

I have tried the following:

  • changing coordinates
  • using origin: Element
  • split the actions so they are separate
  • removed the pause
  • increased the pause
  • added a duration
  • removing the origin

EDIT: a very easy way to test this behaviour is using the following demo: https://the-internet.herokuapp.com/drag_and_drop

@christian-bromann Yes it is working with a single element. But the dragged element is attached to the mouse pointer position and not at the droppable position. If the mouse pointer is at the droppable element then, dragAndDrop works else the test will fail.

Next, I tried moveTo for moving the mouse pointer to that position. It didn’t work. basically, dragAndDrop test case fails.

Maybe late, but better late than sorry.

I’ve also been trying this with the following script on different browsers

describe('Drag and drop', () => {
  it('should be able to put documents in the trash', () => {
    // load the url
    browser.url('https://marcojakob.github.io/dart-dnd/basic/')

    // Wait for the bin to be there
    const trashCan = $('.trash')
    expect(trashCan).toBeVisible({ wait: 15000 })

    // First check how many documents whe have
    expect($$('.document')).toBeElementsArrayOfSize(4)

    // Drag the first document to the trash can
    $$('.document')[0].dragAndDrop(trashCan)

    // for demo purpose
    browser.pause(1000)

    // Check the amount of documents is 3
    expect($$('.document')).toBeElementsArrayOfSize(3)

    // Check the trash can is full
    expect($('.trash.full')).toBeVisible()

    // Drag the first document to the trash can
    $$('.document')[0].dragAndDrop(trashCan)

    // for demo purpose
    browser.pause(1000)

    // Check the amount of documents is 2
    expect($$('.document')).toBeElementsArrayOfSize(2)

    // Drag the first document to the trash can
    $$('.document')[0].dragAndDrop(trashCan)

    // for demo purpose
    browser.pause(1000)

    // Check the amount of documents is 1
    expect($$('.document')).toBeElementsArrayOfSize(1)

    // Drag the last document to the trash can
    $$('.document')[0].dragAndDrop(trashCan)

    // for demo purpose
    browser.pause(1000)

    // Check the amount of documents is 0
    expect($$('.document')).toBeElementsArrayOfSize(0)
  })
})

And it works on the following combinations

image

IE11 doesn’t work in JSONWP or W3C, but I think that was expected

This is a movie of it

dragAndDrop

So for me it looks that it works

Has anyone been able to get dragAndDrop() or some workaround to work? I’m using Webdriverio v4 and chromedriver.

See also w3c/webdriver#1488

Thanks @wswebcreation, based on that I will close this issue. We appreciate any contributions that would fix this issue in IE (if this is even possible).

Sure.

const ACTION_BUTTON = 0;

browser.performActions([{
      type: 'pointer',
      id: 'finger1',
      parameters: { pointerType: 'mouse' },
      actions: [
          { type: 'pointerMove', duration: 0, x: sourceX, y: sourceY },
          { type: 'pointerDown', button: constants.ACTION_BUTTON },
          { type: 'pause', duration: 10 }, // emulate human pause
          { type: 'pointerMove', duration, origin: 'pointer', x: diffX, y: diffY }
      ]
}]);

Where sourceX and sourceY are the coordinates of the element you are dragging, and diffX and diffY are the differences between the sourceX and sourceY and the coordinates of the target element. After making this call to the performActions function, I call targetElement.click() as stated above. That seems to do the trick of keeping the pointer in the right place during the release (pointerUp).

In case it comes in useful, I created a helper function for finding the coordinates of an element:

const getCoordsForElement = async (elementId) => {
  const rect = await browser.getElementRect(elementId);
  const X = parseInt(rect.x + (rect.width / 2), 10);
  const Y = parseInt(rect.y + (rect.height / 2), 10);
  return [X, Y];
};

const [sourceX, sourceY] = getCoordsForElement(element.elementId);

Let me know if that helps.

@iamatharva this issue is closed because it is a duplicate of https://github.com/webdriverio/webdriverio/issues/8022 … please get involved help us resolve this problem. Thank you!

@SMdoras91 I am currently busy working on a v8 release so any contributions to this are more than welcome.

@SMdoras91 interesting investigation … we have updated the dragAndDrop code in v8 (npm install webdriverio@latest) and I wonder if we got around resolving the issue.

Here is a working example with Leaflet.js

describe('leaflet', () => {
  it('should move marker', () => {
    browser.url('https://cdpn.io/PyroStrex/fullpage/JKpGKv')

    const parentIframe = $('iframe#result')
    expect(parentIframe).toBeVisible()
    browser.switchToFrame(parentIframe)

    const marker = $('.leaflet-marker-icon')
    expect(marker).toBeVisible()

    browser.pause(1000) // for demo

    browser.performActions([
      {
        type: 'pointer',
        id: 'finger1',
        parameters: { pointerType: 'mouse' },
        actions: [
          { type: 'pointerMove', origin: marker, x: 0, y: 0 }, // origin is required
          { type: 'pointerDown', button: 0 },
          { type: 'pointerMove', origin: 'pointer', x: 1, y: 1 }, // hack for leaflet.js
          { type: 'pointerMove', origin: 'pointer', x: 30, y: 30 },
          { type: 'pointerUp', button: 0 },
        ],
      },
    ])

    browser.pause(1000) // for demo
  })
})

built-in webdriverio dragAndDrop function won’t work.

@christian-bromann well it is the same situation as other right now. I can’t share my working example due to rights restriction. To get it straight for this particular moment wdio is not capable to do any dragAndDrop tests on chrome and you do not have any plans to fix it in the near future?

@dimaQA31 dragAndDrop is an element-level command, so according to documentation we have to pass the third argument ‘true’ to overwriteCommand

@MaryanaVelyka for html-dnd you should use CSS selectors so #column-a and #column-b

Try this code should be OK

let expect = require('chai').expect;
let dragAndDrop = require('html-dnd').codeForSelectors;

describe('Drag & Drop: https://the-internet.herokuapp.com/drag_and_drop', function() {

	it('Should drag the element from A to B', function() {
		browser.url('https://the-internet.herokuapp.com/drag_and_drop')
		browser.execute(dragAndDrop, '#column-b', '#column-a');
		let column1Text = $('#column-a').getText()
		expect(column1Text).to.equal('B');
	});

});

No…(it’s a webdriver issue not webdriverio)

I have the problem to be able to drag and drop on an angular app. Can you please help me with this issue? Is this a bug of wdio and do you have an workaround for example with javascript? e.g. not working on https://www.w3schools.com/html/html5_draganddrop.asp