Wednesday, August 12, 2015

Selenium Webdriver Drag and drop for HMTL5 elements in C#

Introduction

Recently, I was confronted with the necessity to perform drag and drop actions for a html5 element in a selenium webdriver test written in c #.
That is why In this article I will show you how we can simulate this action by inserting javascript code.

'Simple' approach

Normally, a drag and drop is performed in C# --> selenium webdriver by using Actions class, like this:


or

but for html5 elements, this may not work.

'Javascript' approach

That is why, an alternative is to simulate the drag and drop by inserting javascript code. 
Copy this file, call it for example 'drag.js' somewhere on your your computer. The content of the file is:


(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);

Create a function like below:



Use this function like this:



where
'.timelineList:nth-of-type(3)>div > div' is the CSS locator of the element from which the drag will be performed. Replace it with your css selector.
'.bread-crumbs.bread-crumbs-home.btn.btn-default' is the CSS locator of the element to which the drop will be performed. Replace it with your css selector.

That's it! Don't forget to use CSS locators. It may not work with xpath, class name, etc.

Many thanks to Bogdan whose contribution in achieving this functionality was important.

Happy testing and... make it green, becomes a dream :).
For every post I'll recommend something to read. The link for today is: Selenium Design Patterns and Best Practices

7 comments:

  1. I want to drag and drop to XY , how to modify these scripts to drag element one to say 100 in x direction and 0 or 10 in y direction and drop it there?

    ReplyDelete
  2. if it's not a html element you can use MoveToElement(toElement, x , y ). E.g.
    public static void DragAndDrop(IWebElement from, IWebElement to)
    {
    var action = new Actions(Instance);
    action.ClickAndHold(from).MoveToElement(to, -100, -10).Release(to).Build().Perform();
    }

    ReplyDelete
  3. I don't have toElement , the scenario is to drag a progress bar in Gantt Chart along the x-axis (direction by say 100 relatively) and y kept same. Gantt Chart is embedded in our application and our application extensively uses JQuery.

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Unfortunately none of the examples doesn't work in my case

    ReplyDelete
  6. Hi,
    I am getting following exception
    unknown error: Cannot read property 'dispatchEvent' of undefined
    (Session info: chrome=56.0.2924.87)
    (Driver info: chromedriver=2.27.440174 (e97a722caafc2d3a8b807ee115bfb307f7d2cfd9),platform=Windows NT 6.1.7601 SP1 x86_64)

    Could you please help in resolving this?
    I copied the js file and wrote method like this

    public static void DragAndDrop(string source, IWebDriver driver)
    {
    string current = Environment.CurrentDirectory;
    string filepath = Path.GetFullPath(Path.Combine(current, @"..\..\EducatorUtilities\"));
    string fullpath = Path.GetFullPath(Path.Combine(filepath, @"DragandDrop.js"));
    var dragItem = File.ReadAllText(fullpath);
    IJavaScriptExecutor exe = driver as IJavaScriptExecutor;
    exe.ExecuteScript(dragItem + source);

    }
    And calling it as
    DragAndDrop("$('ul.learners li:nth-child(1)').simulateDragDrop({droptarget:'ed-group-edit.ng-isolate-scope div:nth-child(1)'});",driver);


    ReplyDelete
  7. I'm getting a Syntax Error when I try to implement this, it appears to be caused by my chromedriver.

    The QuickWatch window lists the Source as "WebDriver", which is the name of my non-static IWebDriver instance.

    public void DragItemInHome(string locatorsFromTo)
    {
    var dragString = System.IO.Directory.GetCurrentDirectory() + @"\drag.js'";


    IJavaScriptExecutor js = (IJavaScriptExecutor)WebDriver;
    js.ExecuteScript(dragString + locatorsFromTo);
    }

    DragItemInHome("$('body > survey-builder > div > div > div.survey-builder-area.row > div.question-selection.col-sm-4.p-left-10.p-right-10 > question-picker > div > div.row.m-top-15.user-select-none > div:nth-child(11) > div').simulateDragDrop({ dropTarget: 'body > survey-builder > div > div > div.survey-builder-area.row > div.survey-questions.col-sm-8 > question-list > div > div'});");

    ReplyDelete

Popular Posts