解决Selenium元素拖拽不生效Bug
转载请注明出处️
作者:测试蔡坨坨
原文链接:caituotuo.top/e8aa6c6f.html
你好,我是测试蔡坨坨。
前几天在使用Selenium进行元素拖拽操作时,发现Selenium自带的元素拖拽方法(dragAndDrop())不生效,网上的回答也是五花八门,比较混乱,尝试了以下几种方法均无法解决。
方案1:通过dragAndDrop()方法将元素拖放到特定区域上——无效
// 要拖拽的元素
WebElement draggable = driver.findElement(By.xpath(""));
// 目标元素/区域
WebElement droppable = driver.findElement(By.xpath(""));
new Actions(driver).dragAndDrop(draggable, droppable).build().perform();
方案2:通过dragAndDropBy()方法将元素进行指定像素位移,从而实现拖放到特定区域,该方法需要先找到元素的像素——无效
new Actions(driver).dragAndDropBy(draggable,135, 40).build().perform();
方案3:先通过clickAndHold()方法点击并按住元素,然后使用moveByOffset()方法将元素拖拽到目标区域,再使用release()方法将按住的元素释放——无效
new Actions(driver).clickAndHold(draggable).moveByOffset(400, 0).release().build().perform();
方案4:先通过clickAndHold()方法点击并按住元素,然后使用moveToElement()方法将元素拖拽到指定元素上,再使用release()方法将元素释放——无效
new Actions(driver).clickAndHold(draggable).moveToElement(droppable).release(droppable).build().perform();
方案5:借助Robot类实现拖拽——无效
Point coordinates1 = draggable.getLocation();
Point coordinates2 = droppable.getLocation();
Robot robot = new Robot();
robot.mouseMove(coordinates1.getX(), coordinates1.getY());
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseMove(coordinates2.getX(), coordinates2.getY());
robot.mouseRelease(InputEvent.BUTTON1_MASK);
……
以上方案均未生效,具体表现为运行均无任何报错,但在应用程序中未发生拖放。
经过一顿操作,最终在「Selenium Drag and Drop Bug Workaround」上找到了问题原因及解决方案。
经了解,Selenium的拖放功能在某些情况下无效的错误已经存在多年。
原因是拖放功能包含三个动作:单击并按住(click and hold)、将鼠标移动到其他元素或位置(move mouse to other element/location)、释放鼠标(release mouse),问题在于最后一步释放鼠标的操作,当Webdriver API发送释放鼠标的请求时,在某些情况下它会一直按住它,所以导致拖放功能无效。
解决方法就是通过Webdriver API将JavaScript代码发送到浏览器,利用JavaScript模拟拖放操作,而不使用Webdriver自带的拖放方法。
其工作原理是将浏览器实例和CSS选择器找到的两个Web元素作为参数,然后在浏览器端执行JavaScript代码。
如果你是使用Python+Selenium技术栈实现的Web UI自动化,可以直接下载seletools(Selenium Tools,作者:Dmitrii Bormotov)包,并将它导入到需要执行拖放的地方,然后简单地调用它的drag_and_drop()方法即可。
pip install seletools
from seletools.actions import drag_and_drop
source = driver.find_element(By.CSS_SELECTOR, "#column-a")
target = browser.find_element(By.CSS_SELECTOR, "#column-b")
drag_and_drop(driver, source, target)
如果使用的是Java+Selenium技术栈,则可以使用以下代码实现:
// 要拖拽的元素
WebElement draggable = driver.findElement(By.xpath(""));
// 目标元素
WebElement droppable = driver.findElement(By.xpath(""));
// 拖动前先点击并按住要拖拽的元素,避免在elementui,拖放前draggable属性才会变成true,目的是让draggable变成true,如果一开始就是true也可不加这句
new Actions(driver).clickAndHold(draggable).perform();
final String java_script = "var args = arguments," + "callback = args[args.length - 1]," + "source = args[0]," + "target = args[1]," + "offsetX = (args.length > 2 && args[2]) || 0," + "offsetY = (args.length > 3 && args[3]) || 0," + "delay = (args.length > 4 && args[4]) || 1;" + "if (!source.draggable) throw new Error('Source element is not draggable.');" + "var doc = source.ownerDocument," + "win = doc.defaultView," + "rect1 = source.getBoundingClientRect()," + "rect2 = target ? target.getBoundingClientRect() : rect1," + "x = rect1.left + (rect1.width >> 1)," + "y = rect1.top + (rect1.height >> 1)," + "x2 = rect2.left + (rect2.width >> 1) + offsetX," + "y2 = rect2.top + (rect2.height >> 1) + offsetY," + "dataTransfer = Object.create(Object.prototype, {" + "  _items: { value: { } }," + "  effectAllowed: { value: 'all', writable: true }," + "  dropEffect: { value: 'move', writable: true }," + "  files: { get: function () { return undefined } }," + "  types: { get: function () { return Object.keys(this._items) } }," + "  setData: { value: function (format, data) { this._items[format] = data } }," + "  getData: { value: function (format) { return this._items[format] } }," + "  clearData: { value: function (format) { delete this._items[format] } }," + "  setDragImage: { value: function () { } }" + "});" + "target = doc.elementFromPoint(x2, y2);" + "if(!target) throw new Error('The target element is not interactable and need to be scrolled into the view.');" + "rect2 = target.getBoundingClientRect();" + "emit(source, 'dragstart', delay, function () {" + "var rect3 = target.getBoundingClientRect();" + "x = rect3.left + x2 - rect2.left;" + "y = rect3.top + y2 - rect2.top;" + "emit(target, 'dragenter', 1, function () {" + "  emit(target, 'dragover', delay, function () {" + "\ttarget = doc.elementFromPoint(x, y);" + "\temit(target, 'drop', 1, function () {" + "\t  emit(source, 'dragend', 1, callback);" + "});});});});" + "function emit(element, type, delay, callback) {" + "var event = doc.createEvent('DragEvent');" + "event.initMouseEvent(type, true, true, win, 0, 0, 0, x, y, false, false, false, false, 0, null);" + "Object.defineProperty(event, 'dataTransfer', { get: function () { return dataTransfer } });" + "element.dispatchEvent(event);" + "win.setTimeout(callback, delay);" + "}";
// 默认拖拽到中心点位置,第3个参数是X坐标偏移量(左负右正),第4个参数为Y坐标偏移量(上负下正),第5个参数是延迟时间(单位为毫秒,表示当鼠标点下后,延迟指定时间后才开始激活拖拽动作,用来防止误点击)
((JavascriptExecutor) driver).executeScript(java_script, draggable, droppable, -200, -300, 500);
以上就是在Python和Java中的解决方案,至于为什么不在Selenium中直接修改程序,而是创建单独的包来处理,以下是Dmitrii Bormotov的说法:
The drag and drop bug is a webdriver issue, so all you can do on the Selenium side is to simply perform the same workaround that I did. I spoke with David Burnes (core Selenium committer) about pushing that workaround into Selenium, but he said that it is not a good idea to have any workarounds in Selenium itself. That is why I had to create a separate package to help the test automation community with this problem.
大概的意思就是拖放错误是一个webdriver网络驱动问题,David Burnes(核心 Selenium 提交者)认为在Selenium中提供任何暂时避开网络的方法并不是一个好主意。
解决Selenium元素拖拽不生效Bug的更多相关文章
- Selenium - 实现网页元素拖拽
		
Drag and Drop, 使用鼠标实现元素拖拽的操作貌似很复杂, 在Selenium中, 借助OpenQA.Selenium.Interactions.Actions类库中提供的方法, 实现起来还 ...
 - Selenium WebDriver-通过ActionChains实现页面元素拖拽
		
#encoding=utf-8 import unittest import time import chardet from selenium import webdriver class Visi ...
 - Js元素拖拽功能实现
		
Js元素拖拽功能实现 需要解决的问题 最近项目遇到了一个问题,就是用户某个操作需要弹出一个自定义的内容输入框,但是有个缺点,当浏览太大的时候没办法点击确认和取消按钮,应为这个弹出框是采用绝对定位的,取 ...
 - VUE 元素拖拽、移动
		
元素拖拽 作者:一粒尘土 时间:2019-10-30 使用范围:两个元素位置交换,移动元素到指定位置 涉及函数 属性 解释 draggable 是否允许元素进行拖拽 dragstart 拖拽开始触发的 ...
 - WPF中元素拖拽的两个实例
		
今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListV ...
 - vue全局自定义指令-元素拖拽
		
小白我用的是vue-cli的全家桶,在标签中加入v-drap则实现元素拖拽, 全局指令我是写在main.js中 Vue.directive('drag', { inserted: function ( ...
 - html5的元素拖拽
		
今天学习了妙味课堂的课程: 在html5中有支持元素拖拽的一些属性和方法: 一些实例代码如下: <div id="div1"></div> <ul&g ...
 - selenium学习-拖拽页面元素
		
一.ActionChains包 模拟鼠标的操作要首先引入ActionChains的包 from selenium.webdriver.common.action_chains import Actio ...
 - IE8利用setCapture和releaseCapture解决iframe的拖拽事件
		
最近有个需求须要实现左右拖拽功能,页面右边是个iframe页面,在chrome测试通过之后,发现在ie8上面效果不是很理想,最后查找资料得知可以使用ie自带的setCapture和releaseCap ...
 - jQuery网页元素拖拽插件
		
效果说明:配合已有CSS样式,载入插件后,网页元素可以随意在窗口内拖拽,设置了原位置半透明和拖拽半透明的效果选项,可根据需要选择.另外,当页面上有多个可拖拽元素时,可以载入另外一个用于设置z-inde ...
 
随机推荐
- Docker容器 获取宿主机CPU信息等或重启网卡命令操作介绍
			
一.作用场景及知识点 1. 使用场景(SSH方式除外): A. 运行在Docker容器里面的程序,怎么采集宿主机的CPU.内存.磁盘等信息: B. 容器内更改宿主机的网卡信息,怎么操作宿主机执行命令: ...
 - element ui 分页
			
修改选中页码的样式: .el-pager .active{ color:red !important;//选中 } .el-pager .number:hover{ color:red !import ...
 - elk安装(简)
			
1.到官网下载对应的版本注意最好使用版本相同的3个软件 elasticsearch-6.8.7.rpm kibana-6.8.7-x86_64.rpm logstash-6.8.7.rpm 我这里使用 ...
 - URL Scheme获取
			
支付宝链接或小程序获取URL Scheme 小程序(以行程卡小程序为例): URL解码https://c.runoob.com/front-end/695/ JS网址解码https://jsrun.n ...
 - java通信
			
RPC:同步 http webservice RMI:同步 RMI 是面向对象方式的 Java RPC . 类要extends java.rmi.Remote接口 JMS:异步 需要有消息队列 act ...
 - 记一次mybatis中like 不执行sql的问题,单引号 与双引号的坑
			
使用环境: springboot 2.0.4 mybatis-spring:1.3.2 错误也是比较蛋疼: 不报错,但是查询出来的结果不对. 原错误sql 示意: select * from ...
 - js本地时钟
			
js本地时钟,如上图所示,秒是跳动的 1 // 本地时钟 2 function clockon() { 3 var now = new Date(); 4 var year = now.getFull ...
 - 作业三:CART回归树
			
作业三:CART回归树 20大数据三班 博客链接 学号 201613336 问题一: 表1为拖欠贷款人员训练样本数据集,使用CART算法基于该表数据构造决策树模型,并使用表2中测试样本集确定剪枝后的最 ...
 - 【python】python,PyQt5以及Qt designer的使用_做界面(用vscode)
			
1.PyQt5和Qt designer准备 win+r cmd 输入以下 python -m pip install -i http://pypi.douban.com/sim ...
 - 项目实训 DAY8
			
功能页面开发完毕,实现了两种工具(d3.js/echarts.js)的可视化效果,并与前端整合完毕. 项目实训步入尾声,最后的几(一)天大概就是完善最后的工作,准备答辩.