How we tested drag & drop in HTML5

    Anyway, everyone was faced with situations when something unusual happened in a banal atmosphere. About such a case happened to us when testing a new application on a proven environment a hundred times. A surprise for us was the use of some HTML5 features in the work of the front-end, or rather the impossibility of using standard tools of Selenium WebDriver to automate the testing of drag & drop operations. We want to tell about this experience.

    Imagine a project that is technologically very similar to the previous one (in our opinion, it had a slight negative effect from the point of view of correct understanding and analysis of the problem that appeared), but the Angular version between projects changed from 1.x to 5.x and the Selenide library was added for UI autotests .

    The developed web application had a page with a certain set of entities that could be moved between each other. What was our surprise when the attempt to perform an autotest of checking the drag & drop function by means of Selenide did not succeed. It would seem that could go wrong? On the previous project on a similar test environment, everything worked fine.

    First of all, we checked the operation of the drag & drop functions of Selenide and Selenium in the current browser using the example of another web application. Everything is working. Updated versions, you never know ...
    We decided to check whether we drag and go there. And it’s pretty easy to make a mistake when choosing elements when using Angular. They sat down with the front-end developer and figured out that the drag and drop elements were selected correctly.

    In general, the test environment is working, test methods are written correctly, drag & drop “hands” work, but the autotest does not work. And at first glance there are no reasons for this.

    Finally, we put up with the fact of the problem and went to look for a solution on the Internet. What was our surprise when we found an open issue Chrome WebDriver # 3604 from 03/04/2016. Just think, since the spring of 2016 there has officially been a problem with idle drag & drop in Chrome WebDriver, not to mention other browsers. No, it certainly works, but not when using HTML5. And as it turned out in the process of analyzing the problem, our application used the drag & drop implementation in HTML5.

    What are the options for implementing drag & drop for testing in HTML5? On the Internet, two solutions were found:

    • Use the Java library awt.Robot (or some third-party clicker);
    • Use javascript.

    Probably, we have slightly earned or dug in the problem, but at once I will make a reservation that the first chosen solution did not suit us :)

    What can be said about the implementation on Robot:

    • We intercept the mouse, emulating full user actions;
    • Use Selenium to determine the coordinates of the elements;
    • Since the Selenium elements are used, there is no need to change the locators. We on the project try to use xpath;
    • Written in Java, the syntax is intuitive, good documentation.

    But something about the implementation on JavaScript came up with something like this:

    • Everything happens on JavaScript inside the browser (the actions are hidden from the tester's eyes and these actions interfere with the code);
    • From the js libraries for drag & drop testing on the Internet, one was found, the source of which was not so easy to find;
    • Found the library will have to finish the file to fit your needs, as it implements only pure drag & drop. And we, for example, needed drag -> move -> hold -> drop;
    • The library is implemented as a jQuery add-on, and therefore you will need to understand the jQuery structure;
    • We'll have to bring the locators to css (jquery does not work with xpath);
    • It is impossible to use the search for Selenium elements, it is necessary to glue the locators with "handles".

    At first glance, the first solution was much more convenient and was tested.

    //Setup robot
            Robot robot = new Robot();
            //Fullscreen page so selenium coordinates work
            //Get size of elements
            Dimension fromSize = dragFrom.getSize();
            Dimension toSize = dragTo.getSize();
            //Get centre distance
            int xCentreFrom = fromSize.width / 2;
            int yCentreFrom = fromSize.height / 2;
            int xCentreTo = toSize.width / 2;
            int yCentreTo = toSize.height / 2;
            //Get x and y of WebElement to drag to
            Point toLocation = dragTo.getLocation();
            Point fromLocation = dragFrom.getLocation();
            //Make Mouse coordinate centre of element
            toLocation.x += xOffset + xCentreTo;
            toLocation.y += yCentreTo;
            fromLocation.x += xCentreFrom;
            fromLocation.y += yCentreFrom;
            //Move mouse to drag from location
            robot.mouseMove(fromLocation.x, fromLocation.y);
            //Click and drag
            //Move to final position
            robot.mouseMove(toLocation.x, toLocation.y);

    In general, the solution is working ... However, in the process of its study, its problem areas became clear.

    • The movement of the mouse or the collapse of the browser during the execution of tests leads to interference in the course of tests and their fall;
    • It is impossible to run tests in parallel using JUnit / TestNG. Unless to parallelize through separate in CI.
    • It is impossible to control the mouse on a remote machine through the Selenium Grid / Selenoid;
    • If the browser crashes, Robot can easily click / drag something on the desktop or in another open application.

    As a result, all the same JavaScript implementation ...

    I just want to say that the problem of using xpath locators was solved using the jQuery plugin jquery.xpath.js.

    And the main tool for js drag & drop operations management is the library drag_and_drop_helper.js (the source is here ). There is not much sense in analyzing her work, but about how we finished it a little later.

    Now directly on the implementation of the tests. At Selenide, everything is simple. Before using drag & drop, you need to load the used JS libraries:

    StringBuilder sb = new StringBuilder();

    Naturally, jQuery needs to be loaded if it is not already in the application.

    In the initial version of the library it is enough to register the following:

    executeJavaScript("$('" + source + "') .simulateDragDrop({ dropTarget: '" + target + "'});");

    source and target are css-locators of drag and drop elements.

    As mentioned above, we often use xpath-locators in the project, so after a little refinement the library began to accept them:

    executeJavaScript("$(document).xpath('" + source + "').simulateDragDrop({ dropTarget: '" + target + "'});");

    Now, actually, about the drag_and_drop_helper.js library. In the simulateEvent code block, there are chunks responsible for certain mouse events. It makes no sense to list the possible events of drag & drop operations in HTML5, this information is easy to find.

    For testing, we needed to implement a function that moves the element and holds the mouse on the target element. And this is not provided in the source library.

    By analogy, add a dragenter event to the library (between dragstart and drop).

    /*Simulating dragenter*/
    type = 'dragenter';
    var dragenterEvent = this.createEvent(type, {});
    dragenterEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], type, dragenterEvent);

    However, this is not enough. After all, the hold event will be instantly over. Putting a fixed pause between dragEnter and drop events seemed not the most convenient option. After all, it is not initially known how long the application takes to process an event, the number and time of checks in the tests is unknown. The delay between these events should be at least controllable. Instead, we decided to split the testing of drag & drop into stages and not to emulate the full set of mouse events, that is, to add the ability to manage the list of involved events through a parameter.

    And everything seems to be good, the new flaws did not manifest, and some of the old ones are no longer as such, and the main task is being fulfilled. It would seem that everything is perfect. However, modern development tools provide for the processing of more than two events and use different parameters of the moving element. Suppose we have this solution when performing drag & drop causes errors dragStartListener. But since it does not break anything, then we have nothing more and did not change. However, in some other application you will probably have to finish this moment as well.

    We want to summarize the above. Surprisingly, but a fact! HTML5 was released in the distant 2013, browsers have been supporting it for a long time already, for the first time, applications sharpened for it are being developed, but webDriver, alas, still does not know how to use its capabilities. And testing of drag & drop operations has to be implemented by third-party tools, complicate the architecture and go for all sorts of tricks. Yes, there are such means and “dancing with a tambourine” only makes us stronger, but I still want to have a working solution out of the box.

    In our experience, we can say that such problems are not so common today, although drag & drop is used everywhere. It’s probably a matter of choosing a web application development technology. However, the percentage of applications using HTML5 is growing steadily, frameworks are evolving, and it would be great if the developers of browsers and drivers for them also did not lag behind.

    PS And finally, some lyrics. I would like to advise everyone if possible not to take into account the banality of the situation or the proximity of the test environment to some kind of pattern when analyzing problems. This can lead to incorrect conclusions or loss of time.

    Also popular now: