From 19f340aaf2a2a196f6c0a71baf3bbdd43d4f829f Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 21 Feb 2025 12:36:39 +0100 Subject: [PATCH 1/3] fix(drag-n-drop): send two mousemove events to target to make Angular work --- packages/playwright-core/src/server/frames.ts | 4 + tests/assets/drag-n-drop-manual.html | 129 ++++++++++++++++++ tests/page/page-drag.spec.ts | 47 +++++++ 3 files changed, 180 insertions(+) create mode 100644 tests/assets/drag-n-drop-manual.html diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 69ef1ecba5..74c05e32d0 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -1210,6 +1210,10 @@ export class Frame extends SdkObject { // Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation. dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, options.strict, false /* performActionPreChecks */, async handle => { return handle._retryPointerAction(progress, 'move and up', false, async point => { + // NOTE: Normal browsers emit usually a lot of dragover/mousemove events during drag'n + // drop operations. We want to emit minimal to make the ecosystem work. When using + // Native drag'n drop the browser does emit dropover events instead. + await this._page.mouse.move(point.x, point.y); await this._page.mouse.move(point.x, point.y); await this._page.mouse.up(); }, { diff --git a/tests/assets/drag-n-drop-manual.html b/tests/assets/drag-n-drop-manual.html new file mode 100644 index 0000000000..d876a8ed21 --- /dev/null +++ b/tests/assets/drag-n-drop-manual.html @@ -0,0 +1,129 @@ + + + +
+

+ Select this element, drag it to the Drop Zone and then release the selection to move the element.

+
+
Drop Zone
+ + + \ No newline at end of file diff --git a/tests/page/page-drag.spec.ts b/tests/page/page-drag.spec.ts index c227978f23..1f618d5a76 100644 --- a/tests/page/page-drag.spec.ts +++ b/tests/page/page-drag.spec.ts @@ -53,6 +53,25 @@ it.describe('Drag and drop', () => { ]); }); + it('should send the right events when using dragTo', async ({ server, page, browserName }) => { + await page.goto(server.PREFIX + '/drag-n-drop.html'); + const events = await trackEvents(await page.$('body')); + await page.locator('#source').dragTo(page.locator('#target')); + expect(await events.jsonValue()).toEqual([ + 'mousemove at 120;86', + 'mousedown at 120;86', + browserName === 'firefox' ? 'dragstart at 120;86' : 'mousemove at 240;350', + browserName === 'firefox' ? 'mousemove at 240;350' : 'dragstart at 120;86', + 'dragenter at 240;350', + // NOTE: Normal browsers emit usually a lot of dragover events during drag'n + // drop operations. We want to emit minimal (2) to make the ecosystem work. + 'dragover at 240;350', + 'dragover at 240;350', + 'drop at 240;350', + 'dragend', + ]); + }); + it('should not send dragover on the first mousemove', async ({ server, page, browserName }) => { it.fixme(browserName !== 'chromium'); @@ -293,6 +312,34 @@ it.describe('Drag and drop', () => { expect(await page.$eval('#target', target => target.contains(document.querySelector('#source')))).toBe(true); // could not find source in target }); + it('should work with manual drag\'n drop and emit correct mousemove events', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/34688', + } + }, async ({ page, server }) => { + await page.goto(server.PREFIX + '/drag-n-drop-manual.html'); + const events = await page.evaluateHandle(() => { + const events = []; + document.addEventListener('drop', (event: MouseEvent) => { + events.push({ + type: event.type, + x: event.clientX, + y: event.clientY, + }); + }); + return events; + }); + await page.dragAndDrop('#source', '#target'); + expect(await events.jsonValue()).toEqual([ + { type: 'mousemove', x: 120, y: 86 }, + // NOTE: Normal browsers emit usually a lot of mousemove events during drag'n + // drop operations. We want to emit minimal(2) to make the ecosystem work. + { type: 'mousemove', x: 240, y: 350 }, + { type: 'mousemove', x: 240, y: 350 }, + ]); + }); + it('should allow specifying the position', async ({ page, server }) => { await page.setContent(`
From c425fcd4744d689593e5babb760a68910c2570e8 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 21 Feb 2025 13:33:20 +0000 Subject: [PATCH 2/3] fixes 1 --- tests/assets/drag-n-drop-manual.html | 17 +---------------- tests/page/page-drag.spec.ts | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/tests/assets/drag-n-drop-manual.html b/tests/assets/drag-n-drop-manual.html index d876a8ed21..424639bf94 100644 --- a/tests/assets/drag-n-drop-manual.html +++ b/tests/assets/drag-n-drop-manual.html @@ -39,42 +39,33 @@ div:not(.mouse-helper) {
Drop Zone
- \ No newline at end of file + diff --git a/tests/page/page-drag.spec.ts b/tests/page/page-drag.spec.ts index 1f618d5a76..7b25b4e604 100644 --- a/tests/page/page-drag.spec.ts +++ b/tests/page/page-drag.spec.ts @@ -321,7 +321,7 @@ it.describe('Drag and drop', () => { await page.goto(server.PREFIX + '/drag-n-drop-manual.html'); const events = await page.evaluateHandle(() => { const events = []; - document.addEventListener('drop', (event: MouseEvent) => { + document.addEventListener('mousemove', (event: MouseEvent) => { events.push({ type: event.type, x: event.clientX, From 1e6410ba6753ba7046f57d70110ca8a3a1ef8607 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 21 Feb 2025 14:35:42 +0100 Subject: [PATCH 3/3] fixes 2 --- packages/playwright-core/src/server/frames.ts | 3 +-- tests/assets/drag-n-drop-manual.html | 2 -- tests/page/page-drag.spec.ts | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 74c05e32d0..87685050ec 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -1211,8 +1211,7 @@ export class Frame extends SdkObject { dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, options.strict, false /* performActionPreChecks */, async handle => { return handle._retryPointerAction(progress, 'move and up', false, async point => { // NOTE: Normal browsers emit usually a lot of dragover/mousemove events during drag'n - // drop operations. We want to emit minimal to make the ecosystem work. When using - // Native drag'n drop the browser does emit dropover events instead. + // drop operations. We want to emit minimal (2) to make Angular CDK work. await this._page.mouse.move(point.x, point.y); await this._page.mouse.move(point.x, point.y); await this._page.mouse.up(); diff --git a/tests/assets/drag-n-drop-manual.html b/tests/assets/drag-n-drop-manual.html index 424639bf94..143b87ef9a 100644 --- a/tests/assets/drag-n-drop-manual.html +++ b/tests/assets/drag-n-drop-manual.html @@ -62,7 +62,6 @@ div:not(.mouse-helper) { sourceElement.style.border = 'dashed' }); - // Mouse move handler - move the element document.addEventListener('mousemove', function(e) { if (!isDragging) return; @@ -70,7 +69,6 @@ div:not(.mouse-helper) { sourceElement.style.top = (e.clientY - offsetY) + 'px'; }); - // Mouse up handler - stop dragging and check if dropped on target document.addEventListener('mouseup', function(e) { if (!isDragging) return; diff --git a/tests/page/page-drag.spec.ts b/tests/page/page-drag.spec.ts index 7b25b4e604..906e037d91 100644 --- a/tests/page/page-drag.spec.ts +++ b/tests/page/page-drag.spec.ts @@ -64,7 +64,7 @@ it.describe('Drag and drop', () => { browserName === 'firefox' ? 'mousemove at 240;350' : 'dragstart at 120;86', 'dragenter at 240;350', // NOTE: Normal browsers emit usually a lot of dragover events during drag'n - // drop operations. We want to emit minimal (2) to make the ecosystem work. + // drop operations. We want to emit minimal (2) to make Angular CDK work. 'dragover at 240;350', 'dragover at 240;350', 'drop at 240;350', @@ -334,7 +334,7 @@ it.describe('Drag and drop', () => { expect(await events.jsonValue()).toEqual([ { type: 'mousemove', x: 120, y: 86 }, // NOTE: Normal browsers emit usually a lot of mousemove events during drag'n - // drop operations. We want to emit minimal(2) to make the ecosystem work. + // drop operations. We want to emit minimal (2) to make Angular CDK work. { type: 'mousemove', x: 240, y: 350 }, { type: 'mousemove', x: 240, y: 350 }, ]);