Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript workaround for drag and drop in Selenium WebDriver

clickAndHold is not working for me in my test environment setup. I keep getting this error when trying to execute it using the Advanced User Interactions:

"Cannot press more than one button or an already pressed button.' when calling method: [wdIMouse::down]"

I've tested numerous versions of Firefox with selenium versions 2.31.0-2.35.0 and Firefox 21 with selenium 2.35 has the fewest issues. Other combinations have problems with click() failing silently and visible elements being treated as invisible.

I want to use a JavaScript workaround for drag and drop of one element to another, but I can't find any decent examples anywhere after googling extensively.

like image 205
Selena Avatar asked Oct 15 '13 15:10

Selena


2 Answers

Since I posted this question, I've found a few different solutions to this problem. I'm posting them here for reference so that this question is useful to others:

When drag and drop in Selenium failed to work for HTML5, a defect was entered against Selenium and some people commented on the defect with suggested Javascript work around solutions:

JQuery/HTML5 Solution

http://code.google.com/p/selenium/issues/detail?id=3604#c9

This is a ruby-based solution, which could be translated into Java, C#, or any other language supported by Selenium. It requires JQuery to be on the page, as is clear from many of the comments from other users trying to use this solution, and it seems this only works for HTML5 pages:

The workaround we put together is working for us. It's been a life saver for testing our Ember.js app.

attached is the latest version of what we are using...

this is what we have in our test_helper:

  def drag_and_drop(source,target)

    js_filepath=File.dirname(__FILE__)+"/drag_and_drop_helper.js"
    js_file= File.new(js_filepath,"r")
    java_script=""

    while (line=js_file.gets)
      java_script+=line
    end

    js_file.close

    @driver.execute_script(java_script+"$('#{source}').simulateDragDrop({ dropTarget: '#{target}'});")

    rescue Exception => e
      puts "ERROR :" + e.to_s

  end

The source code for the referenced Javascript drag and drop simulation is posted here:

https://gist.github.com/rcorreia/2362544

(function( $ ) {
    $.fn.simulateDragDrop = function(options) {
        return this.each(function() {
            new $.simulateDragDrop(this, options);
        });
    };
    $.simulateDragDrop = function(elem, options) {
        this.options = options;
        this.simulateEvent(elem, options);
    };
    $.extend($.simulateDragDrop.prototype, {
        simulateEvent: function(elem, options) {
            /*Simulating drag start*/
            var type = 'dragstart';
            var event = this.createEvent(type);
            this.dispatchEvent(elem, type, event);

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

            /*Simulating drag end*/
            type = 'dragend';
            var dragEndEvent = this.createEvent(type, {});
            dragEndEvent.dataTransfer = event.dataTransfer;
            this.dispatchEvent(elem, type, dragEndEvent);
        },
        createEvent: function(type) {
            var event = document.createEvent("CustomEvent");
            event.initCustomEvent(type, true, true, null);
            event.dataTransfer = {
                data: {
                },
                setData: function(type, val){
                    this.data[type] = val;
                },
                getData: function(type){
                    return this.data[type];
                }
            };
            return event;
        },
        dispatchEvent: function(elem, type, event) {
            if(elem.dispatchEvent) {
                elem.dispatchEvent(event);
            }else if( elem.fireEvent ) {
                elem.fireEvent("on"+type, event);
            }
        }
    });
})(jQuery);

Another user posted additional code when it didn't work completely for him, along with some instructions for injecting JQuery into the page:

http://code.google.com/p/selenium/issues/detail?id=3604#c25

We had trouble with the original drag_and_drop_helper.js posted as a workaround for this issue. The workaround is 99% correct, but I needed to modify the workaround to include the dropTarget in the options propagated via the 'coord' object in simulateDrag.

i.e. I need to change:

  coord = { clientX: x, clientY: y }

to:

coord = { clientX: x, clientY: y , dropTarget: options.dropTarget || undefined }

Also, a note for those following the example usage, if the app under test does not already alias the jQuery function to $, you will need to spell-out jQuery:

i.e., after injecting the drag and drop helper onto the page, we have a method that accepts jQuery selectors to use the simulated drag and drop functions (Java):

/**
 * Drag and drop via the JQuery-based drag and drop helper -- the helper
 * must have been injected onto the page prior to calling this method.
 *
 * @param dragSourceJQuerySelector a JQuery-style selector that identifies the source element to drag;
 * <em>will be passed directly to jQuery(), perform all quoting yourself</em>
 * @param dropTargetJQuerySelector a JQuery-style selector that identifies the target element to drop the source onto;
 * <em>will be passed directly to jQuery(), perform all quoting yourself</em>
 */
protected void dragAndDropViaJQueryHelper(String dragSourceJQuerySelector, String dropTargetJQuerySelector) {
    String javascript =
        "var dropTarget = jQuery(" + dropTargetJQuerySelector + ");" +
        "\n" +
        "jQuery("+ dragSourceJQuerySelector + ").simulate('drag', { dropTarget: dropTarget });";

    getLogger().info("executing javascript:\n" + javascript);
    this.executeScript(javascript);
    getLogger().info("executed drag-n-drop action via javascript");
}

Another, non-jQuery solution

http://ynot408.wordpress.com/2011/09/22/drag-and-drop-using-selenium-webdriver/

Javascript based drag and drop which works across browsers.

Mouse move stopped working after version 2.3 while using RemoteWebDriver in selenium. The below function drags element 1 to element 2 position and releases the mouse down.

public void dragdrop(By ByFrom, By ByTo) {
    WebElement LocatorFrom = driver.findElement(ByFrom);
    WebElement LocatorTo = driver.findElement(ByTo);
    String xto=Integer.toString(LocatorTo.getLocation().x);
    String yto=Integer.toString(LocatorTo.getLocation().y);
    ((JavascriptExecutor)driver).executeScript("function simulate(f,c,d,e){var b,a=null;for(b in eventMatchers)if(eventMatchers[b].test(c)){a=b;break}if(!a)return!1;document.createEvent?(b=document.createEvent(a),a==\"HTMLEvents\"?b.initEvent(c,!0,!0):b.initMouseEvent(c,!0,!0,document.defaultView,0,d,e,d,e,!1,!1,!1,!1,0,null),f.dispatchEvent(b)):(a=document.createEventObject(),a.detail=0,a.screenX=d,a.screenY=e,a.clientX=d,a.clientY=e,a.ctrlKey=!1,a.altKey=!1,a.shiftKey=!1,a.metaKey=!1,a.button=1,f.fireEvent(\"on\"+c,a));return!0} var eventMatchers={HTMLEvents:/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,MouseEvents:/^(?:click|dblclick|mouse(?:down|up|over|move|out))$/}; " +
    "simulate(arguments[0],\"mousedown\",0,0); simulate(arguments[0],\"mousemove\",arguments[1],arguments[2]); simulate(arguments[0],\"mouseup\",arguments[1],arguments[2]); ",
    LocatorFrom,xto,yto);
}
like image 148
Selena Avatar answered Oct 14 '22 07:10

Selena


I had exactly the same issue with Firefox which led me here. Playing around with the suggestions on Stack, finally I was able to resolve the issue using

actions.DragAndDropToOffset(element, x, y).Perform();

I know that's not the Javascript workaround you asked for but hopefully it will work for you, as it did for me. These quirky issues can be really frustrating.

like image 20
jibbs Avatar answered Oct 14 '22 07:10

jibbs