Cannot drag and drop with protractor in website us

2019-06-05 04:52发布

问题:

My company project website has a drag and drop block based on ng2-dragula. This is fundamental function but i cannot use protractor to drag and drop successfully even i tried a lot of ways. Someone has the same issue with me. As i know there is no way to proceed E2E test with dragula

  • https://github.com/bevacqua/angularjs-dragula/issues/86.

    WebDriver Drag and Drop in application using Dragula

    Protractor/Jasmine drag and drop only grabbing text not the element

    https://github.com/bevacqua/dragula/issues/452

========================================================================

But because ng2-dragula is quite popular now, i think that we will have a lot of website using this library and if protractor cannot do, it must be a big issue?

   import {code as dragAndDrop} from 'html-dnd';
...........

p_Login.openUrl("http://valor-software.com/ng2-dragula/");
            let ele1=element(by.xpath("//example-app//div[contains(text(),'Whenever an')]"));
            let ele2=element(by.xpath("//example-app//div[contains(text(),'DOM as a result')]/.."));
            browser.driver.executeScript(dragAndDrop, ele1.getWebElement(), ele2.getWebElement());

回答1:

Can do it!!

browser.executeScript

The workaround was detected by my co-worker inside the same dragula.js test

https://github.com/bevacqua/dragula/blob/master/test/drag.js

Exactly

events.raise(o.containerClick ? div : item, 'mousedown', eventOptions);

Raise function was declarated on

https://github.com/bevacqua/dragula/blob/master/test/lib/events.js

We are going to send a script to execute which will dispatch custom events with the correct reference elements.

You could use something like this (typescript version):

import { browser } from 'protractor';

/*
* @param target css selector of element to drag & drop
* @param destination css selector of destination element 
*/
async function simulateDragAndDrop(target: string,destination: string): Promise<void> {
    await browser.executeScript(() => {
        let getEventOptions = (el, relatedElement) => {
            //coordinates from destination element
            const coords = el.getBoundingClientRect();
            const x = coords.x || coords.left;
            const y = coords.y || coords.top;
            return {
                x: x,
                y: y,
                clientX: x,
                clientY: y,
                screenX: x,
                screenY: y,
                //target reference
                relatedTarget: relatedElement
            };
        };

        let raise = (el, type, options?) => {
            const o = options || { which: 1 };
            const e = document.createEvent('Event');
            e.initEvent(type, true, true);
            Object.keys(o).forEach(apply);
            el.dispatchEvent(e);
            function apply(key) {
                e[key] = o[key];
            }
        };

        let targetEl = document.querySelector(target);
        let destinationEl = document.querySelector(destination);
        let options = getEventOptions(destinationEl, targetEl);

        //start drag
        raise(targetEl, 'mousedown');
        raise(targetEl, 'mousemove');
        //set event on location
        raise(destinationEl, 'mousemove', options);
        //drop
        raise(destinationEl, 'mouseup', options);

    }, target, destination);
}

Or in javascript version to test directly on browser:

https://valor-software.com/ng2-dragula/

var target = 'body > example-app > div > example-a div.container:first-child > div:first-child'; //css selector of element to drag & drop
var destination = 'body > example-app > div > example-a div.container:last-child'; //css selector of destination element
var getEventOptions = function (el, relatedElement) {
    //coordinates from element
    var coords = el.getBoundingClientRect();
    var x = coords.x || coords.left;
    var y = coords.y || coords.top;
    return {
        x: x,
        y: y,
        clientX: x,
        clientY: y,
        screenX: x,
        screenY: y,
        relatedTarget: relatedElement
    };
};
var raise = function (el, type, options) {
    var o = options || { which: 1 };
    var e = document.createEvent('Event');
    e.initEvent(type, true, true);
    Object.keys(o).forEach(apply);
    el.dispatchEvent(e);
    function apply(key) {
        e[key] = o[key];
    }
};
var targetEl = document.querySelector(target);
var destinationEl = document.querySelector(destination);
var options = getEventOptions(destinationEl, targetEl);
//start drag
raise(targetEl, 'mousedown');
raise(targetEl, 'mousemove');
//set event on location
raise(destinationEl, 'mousemove', options);
//drop
raise(destinationEl, 'mouseup', options);

This solution work for me, maybe should be adapted to ng2-dragula tests.



回答2:

I have just made an e2e test of our Angular 5 app which uses ng2-dragula and it worked by using the default protractor method of:

browser.actions()
       .dragAndDrop(sourceElem, targetElem)
       .perform();

I'm running protractor v5.3.2 with chromedriver v2.38 and chrome v67.0.3396.87.

Not sure who fixed what but my answer is to use the versions I have gave above and it works!