feat: Add cy.realDnd command
Closes #11
I would love to get this landed - anything in particular that I could help with?
I stopped in the implementation because it has some underlying problems with pupeteer itself. Just 3 actions touch -> touchMove -> touchEnd don't behave as drag and drop for some reason.
But if you will find a solution that would be perfect!
I didn't yet have time to look into this yet - it seems that you have been testing this with native [draggable] element and maybe this doesn't work for some reason.
In our app, we are implementing drag & drop in a custom way, using pointer events, and a solution similar to yours works just OK for us:
Custom `cy.drag` command
import { fireCdpCommand } from 'cypress-real-events/fireCdpCommand';
import { getCypressElementCoordinates } from 'cypress-real-events/getCypressElementCoordinates';
type Point = { x: number; y: number };
const clamp = (v: number, min: number, max: number) =>
Math.min(Math.max(v, min), max);
const getDirection = (start: number, end: number) => {
switch (true) {
case end > start:
return 1;
case end < start:
return -1;
default:
return 0;
}
};
export const drag = (
subject: JQuery<HTMLElement>,
options: { target: () => Cypress.Chainable<JQuery<HTMLElement>> },
) => {
const { target } = options;
const sourceCoordinates = getCypressElementCoordinates(subject, 'center');
cy.wrap(subject).realMouseDown();
return target()
.then(async ($target) => {
const targetCoordinates = getCypressElementCoordinates($target, 'center');
const direction = {
x: getDirection(sourceCoordinates.x, targetCoordinates.x),
y: getDirection(sourceCoordinates.y, targetCoordinates.y),
};
const clampPoint = (point: Point): Point => ({
x: clamp(point.x, sourceCoordinates.x, targetCoordinates.x),
y: clamp(point.y, sourceCoordinates.y, targetCoordinates.y),
});
// just fire a single pointermove event very close to ther source point
await fireCdpCommand('Input.dispatchMouseEvent', {
type: 'mouseMoved',
button: 'left',
pointerType: 'mouse',
...clampPoint({
x: sourceCoordinates.x + direction.x * 1,
y: sourceCoordinates.y + direction.y * 1,
}),
});
let nextPoint = { ...sourceCoordinates };
while (true) {
// this doesn't move in a straight line at the moment but the movement line shouldn't matter for most of our scenarios
// we just progress at each axis with a 10px step until we reach the target point on a given axis
// when target points are met on both axes then we exit the "movement" phase
nextPoint = clampPoint({
x: nextPoint.x + direction.x * 10,
y: nextPoint.y + direction.y * 10,
});
if (
nextPoint.x === targetCoordinates.x &&
nextPoint.y === targetCoordinates.y
) {
break;
}
await fireCdpCommand('Input.dispatchMouseEvent', {
type: 'mouseMoved',
button: 'left',
pointerType: 'mouse',
...nextPoint,
});
}
return $target;
})
.then(($target) => {
cy.wrap($target).realMouseUp();
return $target;
});
};
So maybe even if this doesn't work for [draggable] it would still be OK to land this feature with a documentation note about this caveat?
I see that there is an experimental Input.dispatchDragEvent command and maybe this is supposed to be used to emulate drag on [draggable] elements?
Btw. based on this test: https://github.com/microsoft/playwright/blob/d70e37de80f99c3fe953921abd5ad7ba1a7f1da8/tests/page/page-drag.spec.ts#L34-L51 and this fixture: https://github.com/microsoft/playwright/blob/d70e37de80f99c3fe953921abd5ad7ba1a7f1da8/tests/assets/drag-n-drop.html it looks like it should work as this is using roughly the same stuff under the hood: https://github.com/microsoft/playwright/blob/1bfc473bc8b88a708c29bfab2bb26c2efc3b2706/packages/playwright-core/src/server/chromium/crInput.ts#L99-L137
However, the stuff here is wrapped with the drag manager that dispatched explicit drag commands to Chrome: https://github.com/microsoft/playwright/blob/1bfc473bc8b88a708c29bfab2bb26c2efc3b2706/packages/playwright-core/src/server/chromium/crDragDrop.ts#L28
Mouse moves are actually wrapped with drag detection here: https://github.com/microsoft/playwright/blob/1bfc473bc8b88a708c29bfab2bb26c2efc3b2706/packages/playwright-core/src/server/chromium/crInput.ts#L99-L109
So I guess somewhat similar shenanigans are needed to enable this here. It seems that the mouse move event itself starts the drag session but the session itself has to be managed manually~ with explicit drag commands.
Yes please! Or can we have a realMouseMove if this is not the way to go?
Hey @dmtrKovalenko ! Please let me know if there's anything I can help with on this one or with realMouseMove ! Tested mouse move on my PR, working great.
@drecali any interest in picking this up? I see you're contributing to realMouseMove recently.