How To Drag And Drop In Selenium With Python?

How To Drag And Drop In Selenium With Python?

Drag and Drop- one of the most beloved and commonly used gestures in the world of GUIs. Many actions are associated with drag and drop, including moving an object from one location to another to creating associations between them. Be it an image editing tool like Canva, a cloud-storage manager like Google Drive, or project planning & collaboration tools like Trello, Asana, Jira, drag, and drop functionality can be found everywhere.

You might have used it for positioning multimedia objects like text, images, video clips, etc., moving files to directories, and moving HTML elements/buttons within tables or between different pixel locations. Additionally, modern websites have started implementing sophisticated drag and drop captcha or bot detection. In all such applications and services, web developers and QA engineers face challenges in writing test cases for performing drag and drop in Selenium Python.

In this article, we’ll look in detail at how to perform drag and drop in Selenium with Python.

What Is Drag And Drop Operation In HTML?

Operation In HTML

Drag and drop is natively a graphical user interface (GUI) based software feature. The world wide web (w3) working draft HTML specifications, which declares standard practices for the modern web to promote interoperability and conformance, includes support for drag and drop operations for progressive web applications in desktop and mobile browser windows.

Basically, drag and drop is a chain of pointing device (mouse) events/gestures on GUI software applications (including web browsers). It involves –

  • Moving the cursor/pointer to where the object is located.
  • Clicking or pressing the pointer device (mouse) to grab the object of interest.
  • Moving the pointer device in a pressed state to the desired location.
  • Releasing the pointer device button to drop the grabbed object.

Drag and drop are sometimes also referred to as “click and drag,” “click and drop” and on touch-screen applications, its equivalent would be “long press, drag, and drop.”

Overview Of Python-Selenium WebDriver

Selenium Python WebDriver bindings is a simple open-source API based interface to automate web browsers like Firefox, Chrome, Edge, Safari, Opera, and several others. Using Python Selenium bindings, software testing engineers write automated functional or acceptance test cases and have access to all the features of Selenium WebDriver.

This is an overview of command line commands for Windows to install and start Selenium with Python:

pip install virtualenv (only if virtualenv is not globally installed on your system)
mkdir your_test_directory_name (create a directory for testing scripts if you don’t have one already. If you have a testing directory, skip this step.)
cd your_test_directory_name (move into your test directory)
virtualenv venv (create a virtual environment for your directory)
venv/scripts/activate (activate the virtualenv)
pip install selenium (install selenium within the activated virtualenv)

To download browser web drivers, hit the respective links for Chrome, Edge, Firefox, Safari. While developing scripts on the local system, to invoke these WebDrivers using Selenium-Python, you need to ensure that the respective web drivers are in the same directory or you have added PATH environment variables to their respective executables (.exe files).

Now, you’re all set to write and run your test scripts to drag and drop in Selenium Python WebDriver bindings inside a web browser.

How To Implement Drag And Drop In Selenium-Python?

We can implement drag and drop in Selenium Python with the help of ActionChains class. Selenium WebDriver APIs expose ActionChains class to automate low level user interactions with the website using a mouse, keyboard, etc. We shall use the same class to imitate and write test cases for drag and drop interaction using Selenium with Python.

What Is ActionChains Class?

Often for writing automated test cases, testing engineers have to perform mouse and keyboard operations; the ActionChains class comprises intuitive methods to perform these interactions. For instance, using ActionChains, we can implement clicking on HTML elements or double click on an object. We can even send keyboard keys, pause inputs, move cursor to a specific location, move mouse by the desired offset and obviously, we can perform drag and drop in Selenium Python.

The methods exposed using ActionChains –

click(), click_and_hold(), context_click(), double_click(), drag_and_drop(source, target), drag_and_drop_by_offset(source, x_offset, y_offset), key_down(value), key_up(value), move_by_offset(x_offset, y_offset), move_to_element(target_element), move_to_element_with_offset(target_element, x_offset, y_offset), pause(seconds), perform(), release(), reset_actions(), send_keys(keys_to_send), send_keys_to_element(element, keys_to_send)

Performing Drag And Drop Using ActionChains Class

In this article, we detail the drag_and_drop( source, target ) and drag_and_drop_by_offset(source, x_offset, y_offset) functions of ActionChains class. We will cover all other ActionChains methods in another article.

  1. drag_and_drop(source, target)

This method accepts two arguments: source & target. As the name goes, drag and drop ActionChains involves first locating the “source” element, then dragging it to the target location and dropping it. To select a source and target element, you may use XPath or CSS Selectors.

  1. drag_and_drop_by_offset(source, x_offset, y_offset)

This method requires three arguments: source, x_offset, and y_offset. drag_and_drop_by_offset is an alternative to using drag_and_drop. It involves identifying the source element and x,y offsets of the target location. After that, it drops the selected source to the desired offset location.

  1. Performing chained click_and_hold(element), move_by_offset(xOffset, yOffset), pause(sec) and release() methods

click_and_hold(argument) method simulates the “left mouse button press and hold” operation. move_by_offset(x,y) resembles moving the cursor by specified x and y pixels on respective axes. Pause(sec) function pauses the action for a declared number of seconds. Release() method frees the remotely pressed mouse button.

  1. Performing chained click_and_hold(element), move_to_element(target), pause(sec), move_by_offset(xOffset, yOffset) and release() methods

move_to_element(arg) is similar to moving the cursor to the specified argument. In our case, we’ll use these operations for drag and drop in Selenium Python by simulating click using click_and_hold, then dragging by using move_to_element or move_by_offset or combo, and by finally releasing, i.e., dropping the selected element.

Demonstration- Drag And Drop Using Python Selenium WebDriver

Python Selenium WebDriver

Here we’ll use “jqueryui.com/droppable” and “https://jqueryui.com/draggable/” page to demonstrate the automation of drag and drop testing using Selenium – Python WebDriver bindings.

Demo for drag_and_drop(source, target) and drag_and_drop_by(source, xoffset, yoffset)

For demonstrating “drag_and_drop_by_offset,” we will use the “draggable” link, and to demonstrate “drag_and_drop,” we shall use the “droppable“ link. In the draggable link, we have a box that can be freely moved across its canvas. We have two boxes in the “droppable” link; we will move box one over box2.

A sample automation test script using Selenium with Python for dragging and dropping would look like this-

import unittest
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time

class DragTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_drag_drop(self):
        driver = self.driver
        driver.maximize_window()
        driver.get('https://jqueryui.com/draggable/') 
        driver.switch_to.frame(0)
        source1 = driver.find_element_by_id('draggable')
        action = ActionChains(driver)
        action.drag_and_drop_by_offset(source1, 100, 100).perform()
        time.sleep(5)
        driver.get('https://jqueryui.com/droppable/')
        driver.switch_to.frame(0)
        source1 = driver.find_element_by_id('draggable')
        target1 = driver.find_element_by_id('droppable')
        actions2 = ActionChains(driver)
        actions2.drag_and_drop(source1, target1).perform()
        time.sleep(5)
        self.assertEqual("Dropped!", target1.text)
        time.sleep(2)
    def tearDown(self):
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()

Code Walkthrough

Let’s understand the script in detail –

import unittest
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time

We import Python’s inbuilt testing library “unittest” to write standard test cases. Next, we import “Python Selenium WebDriver bindings” to remotely control browser activity and automate interactions. We also explicitly import the “ActionChains” class from the Selenium WebDriver package, and lastly, we import “time” to invoke explicit pauses so that you can visualize the interactions. In production, you won’t need sleep time, at least not for this use-case.

class DragTest(unittest.TestCase):

We create a class DragTest, which extends on or, say, inherits the TestCase class of unittest.

def setUp(self): 
    self.driver = webdriver.Firefox()

A Python program or application can have multiple test cases, and they may have a repetitive set-up code. For example, configurations of intended browsers to test the application on. We can factor out such repetitive set-up codes by implementing them in setup() function. This function gets automatically invoked by the testing framework with every run of a new test. In our case, we are launching Selenium FirefoxDriver.

def test_drag_drop(self):
    driver = self.driver
    driver.maximize_window()

Next, we write the core logic of our automated test for drag and drop interactions. Here, we are creating a short reference variable “driver” for “self.driver”. And we invoke maximize_window() method of our driver to launch the browser in full-screen.

driver.get('https://jqueryui.com/draggable/')

get(url) method of the WebDriver, loads the specified url in the browser.

driver.switch_to.frame(0)

For your information, jqueryui[dot]com is using iframes to demo drag-and-drop functionality. WebDriver’s get_element_by_* (ID, name, css) method will identify the element after switching to our target iframe. Here we are switching to the target iframe by using the index parameter.

source1 = driver.find_element_by_id('draggable')

Now, we find the draggable element (inspecting the url, you’ll find the element has an id equal to “draggable.”

action = ActionChains(driver)

This creates an instance of ActionChains.

action.drag_and_drop_by_offset(source1, 100, 100).perform()

Now, we use drag_and_drop_by_offset(source, xOffset, yOffset) method to move the draggable element by 100 pixels on the x-axis and 100 on y-axis.
You may create multiple lines for dragging the draggable element based on test requirements.

time.sleep(5)

This is to pause the script execution for 5 seconds, just to visualize things. In headless browsers, we can ignore this line for faster drag and drop testing using Python Selenium WebDriver.

   driver.get('https://jqueryui.com/droppable/')
        driver.switch_to.frame(0)
        source1 = driver.find_element_by_id('draggable')
        target1 = driver.find_element_by_id('droppable')
        actions2 = ActionChains(driver)
        actions2.drag_and_drop(source1, target1).perform()
        time.sleep(5)

Similarly, for the drag_and_drop(source, target) method, we first load the required url in the browser, switch to the target iframe using the index, identify the source element, i.e., the element which needs to be dragged. And the target element, i.e., the target location where the draggable element needs to be dropped. Lastly, we create a stance of ActionChains and invoke drag_and_drop(src, dest) method. perform() function carries out all the chained interactions.

If the test case executes successfully, you shall see this output on your computer screen –

click_and_hold(element), move_to_element(target), pause(sec), move_by_offset(xOffset, yOffset), release()

To demonstrate drag and drop in Selenium Python using the above methods, we need to write another test with modifications in the test_drag_drop(self) function.

def test_drag_drop(self):

        driver = self.driver
        driver.maximize_window()
        driver.get('https://jqueryui.com/draggable/')
        driver.switch_to.frame(0)
        source1 = driver.find_element_by_id('draggable')
        action = ActionChains(driver)
        action.click_and_hold(source1).move_by_offset(150, 100).pause(2).move_by_offset(-10, -10).release().perform()
        print("Dragging & dropping test case successful\n")
        time.sleep(5)
        driver.get('https://jqueryui.com/droppable/')
        driver.switch_to.frame(0)
        source1 = driver.find_element_by_id('draggable')
        target1 = driver.find_element_by_id('droppable')
        actions2 = ActionChains(driver)
actions2.click_and_hold(source1).move_to_element(target1).pause(2).move_by_offset(20, 20).release().perform()
        print("Dragging & dropping test case successful\n")
        time.sleep(5)
        self.assertEqual("Dropped!", target1.text)
        time.sleep(2)

Code Walkthrough

Let’s understand how it differs from the earlier code –

action.click_and_hold(source1).move_by_offset(150, 100).pause(2).move_by_offset(-10, -10).release().perform()

Here instead of using drag_and_drop_by_offset(), we simulate the same set of interactions using chained methods. We have first identified the draggable element on the jqueryUI page, remotely clicked on it, moved it by 150 pixels on the x-axis and 100 on the y, paused for two seconds, and again moved the clicked element by 10 pixels in the negative x,y direction. Finally, dropped the element.

actions2.click_and_hold(source1).move_to_element(target1).pause(2).move_by_offset(20, 20).release().perform()

Similar to the above one, we execute the same set of chained operations. The difference is that this time we locate a target droppable element and drop our clicked element on it by using move_to_element and release. We again used the move_by_offset here to adjust the position so that the asserted message “dropped!” is visible.

Using Scalable Automation Testing On Cloud

To carry out automated tests in a highly scalable manner on a wide number of browsers and operating system environments, you can use LambdaTest’s automated cross browser testing solution on the cloud.

To execute the above script on LambdaTest, we need to modify setUp() function to the following –

def setUp(self):
        username = "your_lambdatest_username"
        # Register on lambdatest, if you’ve not already. It hardly takes 30 seconds to 2 minutes to get started.
        accessToken = "your_lambdatest_accessToken"
        # You can find access token post registration on your LambdaTest profile page.
        gridUrl = "hub.lambdatest.com/wd/hub"
        desired_cap = {
            'platform' : "win10",
            'browserName' : "chrome",
            'version' :  "67.0",
            "resolution": "1024x768",
            "name": "LambdaTest python selenium drag and drop test ",
            "build": "LambdaTest python selenium drag and drop actionChains",
            "network": True,
            "video": True,
            "visual": True,
            "console": True,
        }
        # Desired capabilities dictionary specifies details of the testing environment. Manipulate this  dictionary to cross-test on multiple browsers and OS’s.
        url = "https://"+username+":"+accessToken+"@"+gridUrl 
        print("Initiating remote driver on platform: "+desired_cap["platform"]+" browser: "+desired_cap["browserName"]+" version: "+desired_cap["version"])
        self.driver = webdriver.Remote(
            desired_capabilities=desired_cap,
            command_executor= url
        )

Registration and setup have been explained in this JavaScript alert handling with Selenium article.

The above script, when executed in LambdaTest cloud and under specified desired capabilities, yields –

Wrapping Up!

We hope you are all set to start dragging and dropping without any manual effort. The best part is that you need not worry about maintaining environments and browser versions. Just focus on the crucial part, i.e., writing the test cases using a manageable cross browser testing environment provider like LambdaTest. Not to mention, cross browser testing using Selenium WebDriver by self hosting in the cloud is a cost-ineffective and resource-intensive process.

Using a cloud-based platform like LambdaTest takes away all Ops pain and enables automation testing engineers to do what they do the best- write the application testing scripts and automate the cross browser testing process on over 2000+ browsers on the cloud.

We hope that this post convinces you to try this drag and drop gesture. And if you did find it valuable, we’ll be grateful if you could share it on your LinkedIn or Twitter. Remember, sharing is caring!

Happy Testing!