Search code examples
pythonseleniumdrag-and-drop

Unable to perform drag and drop with Selenium (python)


I am struggling to perform drag and drop or click and hold actions with Selenium in python on private web app.

I try to reproduce my mistake on a public exemple here : http://the-internet.herokuapp.com/drag_and_drop

Below is my basic piece of code for drag and drop / click and hold

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('http://the-internet.herokuapp.com/drag_and_drop')
dragged = driver.find_element(By.ID, "column-a")
dropped = driver.find_element(By.ID, "column-b")

#Drag and drop

actions = ActionChains(driver)
actions.drag_and_drop(dragged, dropped).perform() 
#column A is selected but not dragged

#Click and hold
actions = ActionChains(driver)
actions.move_to_element(dragged).click_and_hold().move_to_element(dropped).release().perform()
#Same result : column a is selected but not dragged

Searching through stackoverflow I came across a "solution" for the public exemple using javascript. How to simulate HTML5 Drag and Drop in Selenium Webdriver?

import os
with open(os.path.abspath('C:/Users/Admin/PycharmProjects/AutoTestIntro/drag_and_drop_helper.js'), 'r') as js_file:
    line = js_file.readline()
    script = ''
    while line:
        script += line 
        line = js_file.readline()

driver.execute_script(script+"$('#column-a').simulateDragDrop({ dropTarget: '#column-b'});")

This perfectly work directly in python but required to that dragged and dropped elements have ids. This is not the case on the private project I am currently working on.

Is there anything I am doing wrong with Selenium ? Is there any workaround in JS to specify xpath instead of id ?


Solution

  • I finally came across an answer !!!

    See below :

    https://gist.github.com/florentbr/60ef7cb8d9b1ae690cafc82aad52da73

    I use the function from drag-drop.min.js

    Here a short exemple in python :

    JS_DRAG_AND_DROP = "function h(a,b,c,d){var k=l.createEvent('DragEvent');k.initMouseEvent(b,!0,!0,l.defaultView,0,0,0,m,n,w,x,y,!1,0,null);Object.setPrototypeOf(k,null);k.dataTransfer=g;Object.setPrototypeOf(k,DragEvent.prototype);a.dispatchEvent(k);setTimeout(d,c)}var a=arguments,c=a[0],d=a[1],q=a[2]||0,r=a[3]||0,t=a[4]||1;a=a[5]||'';var x='alt'===a||'\ue00a'===a,w='ctrl'===a||'\ue009'===a,y='shift'===a||'\ue008'===a,l=c.ownerDocument;a=c.getBoundingClientRect();var e=d?d.getBoundingClientRect():a,m=a.left+a.width/2,n=a.top+a.height/2,u=e.left+(q?q:e.width/2),v=e.top+(r?r:e.height/2),p=l.elementFromPoint(m,n),f=l.elementFromPoint(u,v);for(d=p;d&&!d.draggable;)d=d.parentElement;if(!d||!c.contains(p))throw c=Error('source element is not interactable/draggable'),c.code=15,c;if(!f)throw c=Error('target element is not interactable'),c.code=15,c;var g={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(a,b){this[this.length]={_data:''+_data,kind:'string',type:b,getAsFile:function(){},getAsString:function(a){a(this._data)}};g.types.push(b)},remove:function(a){Array.prototype.splice.call(this,a&65535,1);g.types.splice(a&65535,1)},clear:function(a,b){this.length=0;g.types.length=0}}),setData:function(a,b){this.clearData(a);this._items.add(b,a)},getData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);return 0<=b?this._items[b]._data:null},clearData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);this._items.remove(b)},setDragImage:function(a){}};'items'in DataTransfer.prototype&&(g.items=g._items);e=f.getBoundingClientRect();h(p,'dragstart',t,function(){var a=f.getBoundingClientRect();m=a.left+u-e.left;n=a.top+v-e.top;h(f,'dragenter',1,function(){h(f,'dragover',t,function(){f=l.elementFromPoint(m,n);h(f,'drop',1,function(){h(p,'dragend',1,function(){})})})})})"
    
    def drag_and_drop(driver, source, target=None, offsetX=0, offsetY=0, delay=25, key=None) :
      driver.execute_script(JS_DRAG_AND_DROP, source, target, offsetX, offsetY, delay, key)
      time.sleep(delay * 2 / 1000)
    
    driver = webdriver.Chrome()
    driver.get("http://the-internet.herokuapp.com/drag_and_drop")
    
    
    # drag and drop Glass
    source = driver.find_element_by_xpath("//*[@id='column-a']")
    target = driver.find_element_by_xpath("//*[@id='column-b']")
    drag_and_drop(driver, source, target)