Skip to content

Instantly share code, notes, and snippets.

@florentbr
Last active March 9, 2023 14:48
Show Gist options
  • Select an option

  • Save florentbr/60ef7cb8d9b1ae690cafc82aad52da73 to your computer and use it in GitHub Desktop.

Select an option

Save florentbr/60ef7cb8d9b1ae690cafc82aad52da73 to your computer and use it in GitHub Desktop.
Selenium - HTML5 drag and drop
from selenium import webdriver
import time
# JavaScript: HTML5 Drag and drop script
# param1 (WebElement): Source element to drag
# param2 (WebElement): Optional - target element for the drop
# param3 (int): Optional - Drop offset x relative to the target if any or source element
# param4 (int): Optional - Drop offset y relative to the target if any or source element
# param4 (int): Optional - Delay in milliseconds (default = 1ms) for dragging and dropping
# param5 (string): Optional - Key pressed (alt or ctrl or shilf)
JS_DRAG_AND_DROP = "var t=arguments,e=t[0],n=t[1],i=t[2]||0,o=t[3]||0,r=t[4]||1,a=t[5]||'',s='alt'===a||'\ue00a'===a,l='ctrl'===a||'\ue009'===a,c='shift'===a||'\ue008'===a,u=e.ownerDocument,f=e.getBoundingClientRect(),g=n?n.getBoundingClientRect():f,p=f.left+f.width/2,d=f.top+f.height/2,h=g.left+(i||g.width/2),m=g.top+(o||g.height/2),v=u.elementFromPoint(p,d),y=u.elementFromPoint(h,m);if(!v||!y){var E=new Error('source or target element is not interactable');throw E.code=15,E}var _={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(t,e){this[this.length]={_data:''+t,kind:'string',type:e,getAsFile:function(){},getAsString:function(t){t(this._data)}},_.types.push(e)},remove:function(t){Array.prototype.splice.call(this,65535&t,1),_.types.splice(65535&t,1)},clear:function(t,e){this.length=0,_.types.length=0}}),setData:function(t,e){this.clearData(t),this._items.add(e,t)},getData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);return e>=0?this._items[e]._data:null},clearData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);this._items.remove(e)},setDragImage:function(t){}};function w(t,e,n,i){for(var o=0;o<e.length;++o){var r=u.createEvent('MouseEvent');r.initMouseEvent(e[o],!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),t.dispatchEvent(r)}i&&setTimeout(i,n)}function D(t,e,n,i){var o=u.createEvent('DragEvent');o.initMouseEvent(e,!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),Object.setPrototypeOf(o,null),o.dataTransfer=_,Object.setPrototypeOf(o,DragEvent.prototype),t.dispatchEvent(o),i&&setTimeout(i,n)}'items'in DataTransfer.prototype&&(_.items=_._items),w(v,['pointerdown','mousedown'],1,function(){for(var t=v;t&&!t.draggable;)t=t.parentElement;if(t&&t.contains(v)){var e=y.getBoundingClientRect();D(v,'dragstart',r,function(){var t=y.getBoundingClientRect();p=t.left+h-e.left,d=t.top+m-e.top,D(y,'dragenter',1,function(){D(y,'dragover',r,function(){D(u.elementFromPoint(p,d),'drop',1,function(){D(v,'dragend',1,function(){w(u.elementFromPoint(p,d),['mouseup','pointerup'])})})})})})}})"
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://react-dnd.github.io/react-dnd/examples-dustbin-copy-or-move.html")
# drag and drop Glass
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Glass']")
target = driver.find_element_by_xpath("//*[text()[contains(.,'Works with any drop effect')]]")
drag_and_drop(driver, source, target)
# drag and drop Banana with alt key pressed
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Banana']")
target = driver.find_element_by_xpath("//*[text()[contains(.,'Works with copy drop effect')]]")
drag_and_drop(driver, source, target, key='alt')
# drag and drop Paper by offset
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Paper']")
drag_and_drop(driver, source, offsetX=250, offsetY=-100)
var args = arguments,
elemSrc = args[0],
elemDst = args[1],
offsetX = args[2] || 0,
offsetY = args[3] || 0,
delay = args[4] || 1,
key = args[5] || '',
alt = key === 'alt' || key === '\uE00A',
ctrl = key === 'ctrl' || key === '\uE009',
shift = key === 'shift' || key === '\uE008',
doc = elemSrc.ownerDocument,
box1 = elemSrc.getBoundingClientRect(),
box2 = elemDst ? elemDst.getBoundingClientRect() : box1,
x = box1.left + (box1.width / 2),
y = box1.top + (box1.height / 2),
x2 = box2.left + (offsetX ? offsetX : box2.width / 2),
y2 = box2.top + (offsetY ? offsetY : box2.height / 2),
source = doc.elementFromPoint(x, y),
target = doc.elementFromPoint(x2, y2);
if (!source || !target) {
var ex = new Error('source or target element is not interactable');
ex.code = 15;
throw ex;
}
var dataTransfer = {
constructor : DataTransfer,
effectAllowed : null,
dropEffect : null,
types : [ ],
files : Object.setPrototypeOf([], null),
_items : Object.setPrototypeOf([], {
add : function add(data, type) {
this[this.length] = {
_data : '' + data,
kind : 'string',
type : type,
getAsFile : function () { },
getAsString : function (callback) { callback(this._data) }
};
dataTransfer.types.push(type);
},
remove : function remove(i) {
Array.prototype.splice.call(this, i & 65535, 1);
dataTransfer.types.splice(i & 65535, 1);
},
clear : function clear(data, type) {
this.length = 0;
dataTransfer.types.length = 0;
}
}),
setData : function setData(format, data) {
this.clearData(format);
this._items.add(data, format);
},
getData : function getData(format) {
for (var i = this._items.length; i-- && this._items[i].type !== format;);
return i >= 0 ? this._items[i]._data : null;
},
clearData : function clearData(format) {
for (var i = this._items.length; i-- && this._items[i].type !== format;);
this._items.remove(i);
},
setDragImage : function setDragImage(format) { }
};
if ('items' in DataTransfer.prototype)
dataTransfer.items = dataTransfer._items;
emit_mouse(source, [ 'pointerdown', 'mousedown' ], 1, function () {
var elem = source;
while (elem && !elem.draggable)
elem = elem.parentElement;
if (!elem || !elem.contains(source))
return;
var box2 = target.getBoundingClientRect();
emit_drag(source, 'dragstart', delay, function () {
var box3 = target.getBoundingClientRect();
x = box3.left + x2 - box2.left;
y = box3.top + y2 - box2.top;
emit_drag(target, 'dragenter', 1, function () {
emit_drag(target, 'dragover', delay, function () {
emit_drag(doc.elementFromPoint(x, y), 'drop', 1, function () {
emit_drag(source, 'dragend', 1, function () {
emit_mouse(doc.elementFromPoint(x, y), [ 'mouseup', 'pointerup' ]);
})})})})})});
function emit_mouse(element, types, delay, callback) {
for (var i=0; i < types.length; ++i) {
var event = doc.createEvent('MouseEvent');
event.initMouseEvent(types[i], true, true, doc.defaultView, 0, 0, 0, x, y, ctrl, alt, shift, false, 0, null);
element.dispatchEvent(event);
}
callback && setTimeout(callback, delay);
}
function emit_drag(element, type, delay, callback) {
var event = doc.createEvent('DragEvent');
event.initMouseEvent(type, true, true, doc.defaultView, 0, 0, 0, x, y, ctrl, alt, shift, false, 0, null);
Object.setPrototypeOf(event, null);
event.dataTransfer = dataTransfer;
Object.setPrototypeOf(event, DragEvent.prototype);
element.dispatchEvent(event);
callback && setTimeout(callback, delay);
}
var t=arguments,e=t[0],n=t[1],i=t[2]||0,o=t[3]||0,r=t[4]||1,a=t[5]||'',s='alt'===a||'\ue00a'===a,l='ctrl'===a||'\ue009'===a,c='shift'===a||'\ue008'===a,u=e.ownerDocument,f=e.getBoundingClientRect(),g=n?n.getBoundingClientRect():f,p=f.left+f.width/2,d=f.top+f.height/2,h=g.left+(i||g.width/2),m=g.top+(o||g.height/2),v=u.elementFromPoint(p,d),y=u.elementFromPoint(h,m);if(!v||!y){var E=new Error('source or target element is not interactable');throw E.code=15,E}var _={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(t,e){this[this.length]={_data:''+t,kind:'string',type:e,getAsFile:function(){},getAsString:function(t){t(this._data)}},_.types.push(e)},remove:function(t){Array.prototype.splice.call(this,65535&t,1),_.types.splice(65535&t,1)},clear:function(t,e){this.length=0,_.types.length=0}}),setData:function(t,e){this.clearData(t),this._items.add(e,t)},getData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);return e>=0?this._items[e]._data:null},clearData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);this._items.remove(e)},setDragImage:function(t){}};function w(t,e,n,i){for(var o=0;o<e.length;++o){var r=u.createEvent('MouseEvent');r.initMouseEvent(e[o],!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),t.dispatchEvent(r)}i&&setTimeout(i,n)}function D(t,e,n,i){var o=u.createEvent('DragEvent');o.initMouseEvent(e,!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),Object.setPrototypeOf(o,null),o.dataTransfer=_,Object.setPrototypeOf(o,DragEvent.prototype),t.dispatchEvent(o),i&&setTimeout(i,n)}'items'in DataTransfer.prototype&&(_.items=_._items),w(v,['pointerdown','mousedown'],1,function(){for(var t=v;t&&!t.draggable;)t=t.parentElement;if(t&&t.contains(v)){var e=y.getBoundingClientRect();D(v,'dragstart',r,function(){var t=y.getBoundingClientRect();p=t.left+h-e.left,d=t.top+m-e.top,D(y,'dragenter',1,function(){D(y,'dragover',r,function(){D(u.elementFromPoint(p,d),'drop',1,function(){D(v,'dragend',1,function(){w(u.elementFromPoint(p,d),['mouseup','pointerup'])})})})})})}})
@as3379
Copy link
Copy Markdown

as3379 commented Sep 20, 2019

Hi I'm currently working on an application which is developed in HTML5, so automating the drag and drops functionality has been a nightmare. I found your work to be really helpful. When implemented the code in my local test framework, I'm facing this error "TypeError: Object of type WalkthroughClient is not JSON serializable"

I have not include the drag_drop.js in the project folder. Is it because of this ?? If not please give me steps to implement

@gouthamdatla
Copy link
Copy Markdown

gouthamdatla commented Sep 25, 2019

"drag-drop.min.js"
This worked for me....I'm trying to drag and drop elements in my application from past three days....your awsome :)
Im using Protractor Type script.
Implimentation:

const DragAndDrop = "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(){})})})})})";

let dragElement : ElementFinder = element(by.xpath("your element xpath))
let dropElement: ElementFinder = element(by.xpath("your element xpath"))
await browser.executeScript(DragAndDrop, dragElement ,dropElement);

Note: need to do few changes to make code look good

@gouthamdatla
Copy link
Copy Markdown

Implimentation of "drag-drop.min.js" in Protractor typescript framework:

const DND = "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(){})})})})})";

await browser.executeScript(DND , dragingElement, droppingElement);

Note: Need to do changes to make your code look good.

@gouthamdatla
Copy link
Copy Markdown

Hi I'm currently working on an application which is developed in HTML5, so automating the drag and drops functionality has been a nightmare. I found your work to be really helpful. When implemented the code in my local test framework, I'm facing this error "TypeError: Object of type WalkthroughClient is not JSON serializable"

I have not include the drag_drop.js in the project folder. Is it because of this ?? If not please give me steps to implement

Hope below implementation steps by me helps you...:) happy coding

@manikumarnune123
Copy link
Copy Markdown

Were anyone able to drag and drop in this site with above piece od code?

https://app.appsmith.com/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment