playwright drag and drop
Asked Answered
H

3

9

Trying to test some drag and drop functionality, looks like playwright doesn't have drag and drop functionality so I'm using mouse.move(), mouse.down() & mouse.up().

However my attempts seem to be failing, the target is not being moved. Code below:

test("drag and drop test", async () => {
  await page.goto("https://www.w3schools.com/html/html5_draganddrop.asp");
  await page.waitForSelector("#accept-choices");
  await page.click("#accept-choices");
  await page.waitForSelector("#div1");
  let xStart, yStart, xFinish, yFinish, elementHandle, rect;
  elementHandle = await page.$("#div1");
  rect = await elementHandle.boundingBox();
  xStart = rect.x + rect.width / 2;
  yStart = rect.y + rect.height / 2;
  elementHandle = await page.$("#div2");
  rect = await elementHandle.boundingBox();
  xFinish = rect.x + rect.width / 2;
  yFinish = rect.y + rect.height / 2;
  console.log(`move from (${xStart}, ${yStart}) to (${xFinish},${yFinish})`);
  await page.screenshot({ path: "before drag.png" });
  await page.mouse.move(xStart, yStart);
  await page.mouse.down();
  await page.mouse.move(xFinish, yFinish);
  await page.mouse.up();
  await page.screenshot({ path: "after drag.png" });
});
Hume answered 6/11, 2020 at 17:12 Comment(3)
You can vote for drag and drop support here github.com/microsoft/playwright/issues/1094Oireachtas
Dropped my vote thanks, any thoughts on if the above code should work?Hume
The only working test is using the dispatchEvent function github.com/microsoft/playwright/blob/… but it could be quite tricky to find what's the dataTransfer object you'd need to pass.Oireachtas
F
9

Version 1.13.0 added API methods for that. Check out the documentation.
Copied from the docs:

page.dragAndDrop(source, target[, options])

  • source: string
  • target: string
  • options: Object
    • force boolean Whether to bypass the actionability checks. Defaults to false.
    • noWaitAfter boolean Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to inaccessible pages. Defaults to false.
    • timeout number Maximum time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
    • trial: boolean When set, this method only performs the actionability checks and skips the action. Defaults to false. Useful to wait until the element is ready for the action without performing it.
  • returns: Promise<void>
Fattal answered 22/7, 2021 at 11:50 Comment(0)
M
11

I've also struggled with how to make drag and drop possible with playwright.

I needed to move vertically, for example,

before reorder 1 2 3

after reorder applied 2 1 3

    const exampleOneDrag = await page.$(
      `[data-testid="${exampleOne}-drag-icon-button"]`
    )
    const exampleTwoDrag = await page.$(
      `[data-testid="${exampleTwo}-drag-icon-button"]`
    )
    const oneBoundingBox = await exampleOneDrag?.boundingBox()
    const twoBoundingBox = await exampleTwoDrag?.boundingBox()

    if (oneBoundingBox && twoBoundingBox) {
      await page.mouse.move(
        oneBoundingBox.x + oneBoundingBox.width / 2,
        oneBoundingBox.y + oneBoundingBox.height / 2,
        { steps: 5 }
      )
      await page.mouse.down()
      await page.mouse.move(
        twoBoundingBox.x + twoBoundingBox.width / 2,
        twoBoundingBox.y + twoBoundingBox.height / 2,
        { steps: 5 }
      )
      await page.mouse.up()
    }

The keypoint to be able to work this was steps:5 options. I am not entirely sure what it does, but I've found from here : https://github.com/codeceptjs/CodeceptJS/blob/e815d82af028e904051b5b6c70873164e1df1bfd/lib/helper/Playwright.js#L2289-L2307e

hope this works for you. I think your case, you should change like this

  await page.mouse.move(xStart, xFinish);
  await page.mouse.down();
  await page.mouse.move(yStart, yFinish);
Mosesmosey answered 9/11, 2020 at 6:18 Comment(3)
Thanks for your answer, even with step option defined, I am unable to get my example working, have you got a working example to share? Were you able to get drag and drop working in your code to move vertically?Hume
@Hume Sure, I've figured out that step breaks the path from start to endpoint into steps and do that many smaller moves. First, I would suggest increasing step (we did from 5 to 15). If you can see it is reordering but not applying, maybe it's too quick to finish their task, so I've added await page.waitForTimeout(4000) after await page.mouse.up(). Hope it helps your situation.Mosesmosey
looks like its a well known issue, HTML5 drag events not triggered by mouse movement. See this thread: github.com/puppeteer/puppeteer/issues/1376 As @Oireachtas commented, and seen here github.com/puppeteer/puppeteer/issues/… seems we need to dispatchEventHume
F
9

Version 1.13.0 added API methods for that. Check out the documentation.
Copied from the docs:

page.dragAndDrop(source, target[, options])

  • source: string
  • target: string
  • options: Object
    • force boolean Whether to bypass the actionability checks. Defaults to false.
    • noWaitAfter boolean Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to inaccessible pages. Defaults to false.
    • timeout number Maximum time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
    • trial: boolean When set, this method only performs the actionability checks and skips the action. Defaults to false. Useful to wait until the element is ready for the action without performing it.
  • returns: Promise<void>
Fattal answered 22/7, 2021 at 11:50 Comment(0)
M
1

The following code works for me in my own drag and drop library for vue vue-fluid-dnd:

async function dragDrop(
  page: Page,
  originSelector: string,
  destinationSelector: string
) {
  const originElement = await page.waitForSelector(originSelector);
  const destinationElement = await page.waitForSelector(destinationSelector);

  const originElementBox = await originElement.boundingBox();
  const destinationElementBox = await destinationElement.boundingBox();
  if (!originElementBox || !destinationElementBox) {
    return;
  }
  await page.mouse.move(
    originElementBox.x + originElementBox.width / 2,
    originElementBox.y + originElementBox.height / 2
  );
  await page.mouse.down();
  // I added more steps to see a smoother animation.
  await page.mouse.move(
    destinationElementBox.x + destinationElementBox.width / 2,
    destinationElementBox.y + destinationElementBox.height / 2,
    { steps: 20 }
  );
  await page.mouse.up();
}
Maxon answered 22/3 at 18:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.