Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] drag and drop #1094

Closed
JoelEinbinder opened this issue Feb 25, 2020 · 22 comments · Fixed by #6207
Closed

[Feature] drag and drop #1094

JoelEinbinder opened this issue Feb 25, 2020 · 22 comments · Fixed by #6207
Assignees

Comments

@JoelEinbinder
Copy link
Contributor

Drag and drop should work with playwright. Using the existing mouse apis should generate working dragstart, drag, and drop events. There should also be new APIs to simulate dropping in external content, and to inspect content dragged from the web page.

@JoelEinbinder
Copy link
Contributor Author

Chromium

I had a patch that tries to do this years ago. Maybe I can revive it? It ended up touching a lot of layers.
https://chromium-review.googlesource.com/c/chromium/src/+/1340803

WebKit

Blocked on WPE not having drag and drop enabled. I'll have to enable it first before deciding how to mock it with playwright.

Firefox

I haven't investigated firefox yet.

@spaceInvaders007
Copy link

Is it possible to drag and drop at this stage?

@shweta-kumar-acer
Copy link

Is there a way to drag and drop an element to a particular location on the page? I want to be able to drag and drop either horizontally or vertically by a certain pixels. The current dragAnddrop() method brings the dragger back to its original position as I'm not able to drag it enough to drop it in placeholder.

@MedBechirSaid
Copy link

Hi ! i wanna know if there is a solution to test drag and drop angular with playwright.
I tried to use this code:
await page.dispatchEvent('#target', 'a', { draggableElem });
but it didn't work !
any help plz !
i'm using angular 9 / playwright 1.3.0

@lironzizva
Copy link

Any update on this? when drag & drop is planned to be implemented?

@lind-eduard
Copy link

Want to bring this topic up....Are there any progress on this?

@hptabster
Copy link

Is there a workaround in Playwright 1.8?

@cscheffauer
Copy link

+1 for this

2 similar comments
@janpfajfr
Copy link

+1 for this

@chenshuichao
Copy link

+1 for this

@lind-eduard
Copy link

For all who struggling with this issue, I found a workaround for it:

async dragElementToOtherElement(originSelector, destinationSelector) {
		const origin = await page.waitForSelector(originSelector);
		const destination = await page.waitForSelector(destinationSelector);
		const originBox = await origin.boundingBox();
		const destinationBox = await destination.boundingBox();
		const lastPositionCoordenate = (box) => ({
			x: box.x + box.width / 2,
			y: box.y + box.height
		});
		const getPayload = (box) => ({
			bubbles: true,
			cancelable: true,
			screenX: lastPositionCoordenate(box).x,
			screenY: lastPositionCoordenate(box).y,
			clientX: lastPositionCoordenate(box).x,
			clientY: lastPositionCoordenate(box).y
		});
		// Function in browser.
		const pageFunction = async (args) => {
			// for some reasons arguments should be an object, otherwise page.evaluate complains about amount of arguments ¯\_(ツ)_/¯
			const _origin = document.querySelector(args[0]);
			const _destination = document.querySelector(args[1]);
			// Init Events
			_origin.dispatchEvent(new MouseEvent('pointerdown', args[2]));
			_origin.dispatchEvent(new DragEvent('dragstart', args[2]));
			await new Promise((resolve) => setTimeout(resolve, 2000));
			_destination.dispatchEvent(new MouseEvent('dragenter', args[3]));
			_origin.dispatchEvent(new DragEvent('dragend', args[3]));
		};
		// Init drag and drop.
		await page.evaluate(pageFunction, [
			originSelector,
			destinationSelector,
			getPayload(originBox),
			getPayload(destinationBox)
		]);
	}

This is basically was resolved for puppeteer here: puppeteer/puppeteer#1366, I just copied solution and adjusted a bit for playwright and seems like it works.
The only problem for me that it can accept only CSS selectors and I wasn't able to make it work with xpath(I believe it is possible, but don't want to spent much time on it)

@jperl
Copy link
Contributor

jperl commented Apr 2, 2021

For all who struggling with this issue, I found a workaround for it:

Thanks for this @lind-eduard! I tweaked your workaround to work with Playwright selectors, to include the data transfer object, and drop event.

async function dragDrop(page, { origin, destination }) {
  const originElement = await page.waitForSelector(origin);
  const destinationElement = await page.waitForSelector(destination);

  return page.evaluate(async ({ originElement, destinationElement }) => {
    const dataTransfer = new DataTransfer();

    const getPayload = (element) => {
      const rect = element.getBoundingClientRect();
      const [x, y] = [rect.x + rect.width / 2, rect.y + rect.height / 2];
      return {
        bubbles: true,
        cancelable: true,
        clientX: x,
        clientY: y,
        dataTransfer,
        screenX: x,
        screenY: y,
      }
    };

    const originPayload = getPayload(originElement);
    const destinationPayload = getPayload(destinationElement);

    originElement.dispatchEvent(new MouseEvent('pointerdown', originPayload));
    originElement.dispatchEvent(new DragEvent('dragstart', originPayload));
    await new Promise((resolve) => setTimeout(resolve, 2000));
    destinationElement.dispatchEvent(new MouseEvent('dragenter', destinationPayload));
    originElement.dispatchEvent(new DragEvent('dragend', destinationPayload)); 
    destinationElement.dispatchEvent(new DragEvent('drop', destinationPayload));

    return { x: destinationPayload.clientX, y: destinationPayload.clientY };
  }, { originElement, destinationElement });
}

@sergioariveros
Copy link

Hi, is there any news on this feature? Thanks

@krokofant
Copy link

I tried to use the solution from @jperl but it wasn't working. After a bit of debugging i noticed the web application was dependent on the dragover event to have fired.

Adding a dragover event just after dragenter seems to provide better coverage.

Improving it even more I would think this is a proper order of events, it might be improved even more

  1. pointerdown
  2. dragstart
  3. dragenter
  4. pointermove
  5. dragover
  6. pointerup
  7. dragend
  8. drop

@cscheffauer
Copy link

Anyone actively working on this? would be awesome to have this :)

@klhex
Copy link
Contributor

klhex commented Jun 5, 2021

@cscheffauer Yes, just have a look at the linked references of this issue:
image
Therefore, for the latest activities on the implementation of this feature, please see #6207.

aslushnikov pushed a commit that referenced this issue Jun 7, 2021
Waiting for #6203 to percolate to the cdn. But this all works locally.

Fixes #1094
@bsteffensTempus
Copy link

bsteffensTempus commented Jun 22, 2021

As of today, what's the correct way to implement drag and drop functionality from within a test? I cannot find a working example and it looks like the feature was fixed.

EDIT:

This is working for me

    await page.waitForSelector(origin);
    await page.waitForSelector(destination);
    const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
    await page.dispatchEvent(origin, 'dragstart', dataTransfer)
    await page.dispatchEvent(destination, 'dragend', dataTransfer)
    await page.dispatchEvent(destination, 'drop')
    await page.dispatchEvent(destination, 'dragexit')

@krokofant
Copy link

krokofant commented Jun 22, 2021

@bsteffensTempus I was missing some clear documentation as well and I implemented this based on the new features:

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

	await originElement.hover();
	await this.page.mouse.down();
	const box = (await destinationElement.boundingBox())!;
	await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
	await destinationElement.hover();
	await this.page.mouse.up();
}

or this more standalone

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

	await originElement.hover();
	await page.mouse.down();
	const box = (await destinationElement.boundingBox())!;
	await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
	await destinationElement.hover();
	await page.mouse.up();
}

For reference in 1.13.0 we have page.dragAndDrop()
https://github.com/microsoft/playwright/releases/tag/v1.13.0
https://playwright.dev/docs/next/api/class-page/#page-drag-and-drop

@john-cyndx
Copy link

i took this and used most of it to work for me, this is sort of janky and isn't very reusable but it can be worked on
export async function dragAndDrop(page: Page, originSelector: string, destinationSelector: string): Promise<void> {
const originElement: ElementHandle<SVGElement | HTMLElement> = await page.waitForSelector(originSelector);
const destinationElement: ElementHandle<SVGElement | HTMLElement> = await page.waitForSelector(destinationSelector);
const orgBox = (await originElement.boundingBox())!;
const dstBox = (await destinationElement.boundingBox())!;
await page.mouse.move(orgBox.x + orgBox.width * 3, orgBox.y);
await page.mouse.down({ button: 'left' });
await page.mouse.move(dstBox.x + dstBox.width / 2, orgBox.y, { steps: 15 });
await page.mouse.up({ button: 'left' });
}

@krokofant
Copy link

@john-cyndx Drag and drop is an official api since almost a year back now... 🤔

@pranaymore926
Copy link

Drag and drop is not working in hamburger menu. do we any working around for it ?

@TheDevelolper
Copy link

@john-cyndx Drag and drop is an official api since almost a year back now... 🤔

The official inbuilt drag drop didn't work in my case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.