4teamwork / cypress-drag-drop

A cypress child command for drag'n'drop support.
GNU General Public License v3.0
167 stars 45 forks source link

Dragend event is missing #145

Open BillyTom opened 11 months ago

BillyTom commented 11 months ago

Probably related to #18, but I created a new issue since the other is more the two years old.

Sometimes the event "dragend" or "drop" is not emitted. This means that the drag operation is not completed successfully, but no error is emitted.

I co-opted the page simple-drag-drop.glitch.me to create a test case to reproduce the issue:

describe("reproduce missing event bug", () => {
    it("should drag-and-drop C to A", () => {
        cy.visit("https://simple-drag-drop.glitch.me/");

        const dragAndDropTargetClass = 'drop-target';
        cy.contains<HTMLElement>('C').then(element => {
            element.addClass(dragAndDropTargetClass);
            cy.log('drop target class added')
        });
        cy.get('.' + dragAndDropTargetClass);

        cy.contains('A').drag('.' + dragAndDropTargetClass);
        cy.wait(1000);

        cy.get("div[draggable=true]").then((elements) => {
            cy.wrap(elements.text()).should("be.eq", "CBA");
        });
    });
});

This test fails to re-order the elements ABC:

image

Log entry:

01 visit https://simple-drag-drop.glitch.me/
02 contains C
03 log--- drop target class added ---
04 get .drop-target
05 contains A
06 get .drop-target
07 wrap <div.box>
08 trigger pointerdown, Object{3}
09 trigger mousedown, Object{3}
10 trigger dragstart, {datatransfer: {}, eventconstructor: DragEvent}
11 wrap <div.box.drop-target>
12 trigger dragover, {datatransfer: {}, eventconstructor: DragEvent}
13 trigger mousemove, {eventconstructor: MouseEvent}
14 trigger pointermove, {eventconstructor: PointerEvent}
15 wait 10
16 wrap <div.box.drop-target>
17 trigger dragover, {datatransfer: {}, eventconstructor: DragEvent}
18 trigger mousemove, {eventconstructor: MouseEvent}
19 trigger pointermove, {eventconstructor: PointerEvent}
20 wait 10
21 wrap <div.box.drop-target>
22 trigger dragover, {datatransfer: {}, eventconstructor: DragEvent}
23 trigger mousemove, {eventconstructor: MouseEvent}
24 trigger pointermove, {eventconstructor: PointerEvent}
25 wait 10
26 wrap <div.box.drop-target>
27 trigger dragover, {datatransfer: {}, eventconstructor: DragEvent}
28 trigger mousemove, {eventconstructor: MouseEvent}
29 trigger pointermove, {eventconstructor: PointerEvent}
30 wait 10
31 wrap <div.box.drop-target>
32 trigger dragover, {datatransfer: {}, eventconstructor: DragEvent}
33 trigger mousemove, {eventconstructor: MouseEvent}
34 trigger pointermove, {eventconstructor: PointerEvent}
35 wait 10
36 wait 1000
37 get div[draggable=true]3
38 wrap ABC
39 assert expected ABC to equal **CBA**

The events "dragstart" and "dragover" are emitted, but no "dragend" or "drop".

I copied a second test from this blog post that uses drag & drop with native javascript:

/**
 * Playground test. Used for debugging.
 * See https://reflect.run/articles/testing-drag-and-drop-workflows-using-cypress/
 */
describe("native dnd spec", () => {
    it("should drag-and-drop C to A", () => {
        cy.visit("https://simple-drag-drop.glitch.me/");

        // to hold the data that is
        // dragged during a drag-and-drop operation
        const dataTransfer = new DataTransfer();

        cy
            // getting "A"
            .get("div[draggable=true]")
            .first()
            // triggering the "dragstart" event to
            // initialize the dataTransfer object
            .trigger("dragstart", { dataTransfer });

        cy
            // getting "C"
            .get("div[draggable=true]")
            .eq(2)
            // dropping "C" into the "A" position defined in dataTransfer
            .trigger("drop", { dataTransfer });

        // now "C" is in the first position, and you need
        // to retrieve it again
        cy
            // getting "C"
            .get("div[draggable=true]")
            .first()
            .trigger("dragend")
            .then(() => {
                // "A" should now be in the position of "C" and vice versa
                cy.get("div[draggable=true]").then((elements) => {
                    cy.wrap(elements.text()).should("be.eq", "CBA");
                });
            });
    });
});

This second test works:

image

mandarkslab commented 11 months ago

I'm experiencing the same on cypress 13.5.1. "dropend" event never triggers once the element has been dragged over to the target.

I'm only seeing the following:

triggerdragover, Object{5} triggermousemove, Object{4} triggerpointermove, Object{4}

Currently using @4tw/cypress-drag-drop@2.2.5

As a quick and dirty workaround, it seems to work when I force a mouse-up event after the initial drag. The only issue is that you can't then assert the success of the "drag and drop" action using its '.then((status) => {})' chained method.

e.g.:

        cy.get(elementToDrag).drag(targetDropzone, {
            target: { position: 'center' },
            force: true
        })
        // Then add the below to force the end of the action
        cy.get(targetDropzone).trigger('mouseup', { force: true })