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

feat: Add cy.realDnd command #17

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft

Conversation

dmtrKovalenko
Copy link
Owner

Closes #11

@dmtrKovalenko dmtrKovalenko marked this pull request as draft December 19, 2020 13:22
@Andarist
Copy link
Collaborator

I would love to get this landed - anything in particular that I could help with?

@dmtrKovalenko
Copy link
Owner Author

dmtrKovalenko commented Sep 10, 2021

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!

@Andarist
Copy link
Collaborator

Andarist commented Dec 2, 2021

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?

@Andarist
Copy link
Collaborator

Andarist commented Dec 3, 2021

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.

@knownasilya
Copy link

Yes please! Or can we have a realMouseMove if this is not the way to go?

@DolevBitran
Copy link
Contributor

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.

@JessicaSachs
Copy link

@drecali any interest in picking this up? I see you're contributing to realMouseMove recently.

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 this pull request may close these issues.

Implement drag and drop
5 participants