解决Selenium元素拖拽不见效Bug

打印 上一主题 下一主题

主题 1043|帖子 1043|积分 3129

前几天在利用Selenium进行元素拖拽操作时,发现Selenium自带的元素拖拽方法(dragAndDrop())不见效,网上的回答也是五花八门,比较混乱,尝试了以下几种方法均无法解决。
方案1:通过dragAndDrop()方法将元素拖放到特定地区上——无效
  1. // 要拖拽的元素
  2. WebElement draggable = driver.findElement(By.xpath(""));
  3. // 目标元素/区域
  4. WebElement droppable = driver.findElement(By.xpath(""));
  5. new Actions(driver).dragAndDrop(draggable, droppable).build().perform();
复制代码
方案2:通过dragAndDropBy()方法将元素进行指定像素位移,从而实现拖放到特定地区,该方法需要先找到元素的像素——无效
  1. new Actions(driver).dragAndDropBy(draggable,135, 40).build().perform();
复制代码
方案3:先通过clickAndHold()方法点击并按住元素,然后利用moveByOffset()方法将元素拖拽到目标地区,再利用release()方法将按住的元素释放——无效
  1. new Actions(driver).clickAndHold(draggable).moveByOffset(400, 0).release().build().perform();
复制代码
方案4:先通过clickAndHold()方法点击并按住元素,然后利用moveToElement()方法将元素拖拽到指定元素上,再利用release()方法将元素释放——无效
  1. new Actions(driver).clickAndHold(draggable).moveToElement(droppable).release(droppable).build().perform();
复制代码
方案5:借助Robot类实现拖拽——无效
  1. Point coordinates1 = draggable.getLocation();
  2. Point coordinates2 = droppable.getLocation();
  3. Robot robot = new Robot();
  4. robot.mouseMove(coordinates1.getX(), coordinates1.getY());
  5. robot.mousePress(InputEvent.BUTTON1_MASK);
  6. robot.mouseMove(coordinates2.getX(), coordinates2.getY());
  7. robot.mouseRelease(InputEvent.BUTTON1_MASK);
  8. ……
复制代码
以上方案均未见效,详细表现为运行均无任何报错,但在应用步伐中未发生拖放。
颠末一顿操作,最终在「Selenium Drag and Drop Bug Workaround:https://www.softwaretestingmagazine.com/knowledge/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()方法即可。
  1. pip install seletools
复制代码
  1. from seletools.actions import drag_and_drop
  2. source = driver.find_element(By.CSS_SELECTOR, "#column-a")
  3. target = browser.find_element(By.CSS_SELECTOR, "#column-b")
  4. drag_and_drop(driver, source, target)
复制代码
如果利用的是Java+Selenium技能栈,则可以利用以下代码实现:
  1. // 要拖拽的元素
  2. WebElement draggable = driver.findElement(By.xpath(""));
  3. // 目标元素
  4. WebElement droppable = driver.findElement(By.xpath(""));
  5. // 拖动前先点击并按住要拖拽的元素,避免在elementui,拖放前draggable属性才会变成true,目的是让draggable变成true,如果一开始就是true也可不加这句
  6. new Actions(driver).clickAndHold(draggable).perform();
  7. 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);" + "}";
  8. // 默认拖拽到中心点位置,第3个参数是X坐标偏移量(左负右正),第4个参数为Y坐标偏移量(上负下正),第5个参数是延迟时间(单位为毫秒,表示当鼠标点下后,延迟指定时间后才开始激活拖拽动作,用来防止误点击)
  9. ((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中提供任何暂时避开网络的方法并不是一个好主意。
最后: 下方这份完备的软件测试视频学习教程已经整理上传完成,朋侪们如果需要可以自行免费领取 【保证100%免费】

这些资料,对于【软件测试】的朋侪来说应该是最全面最完备的备战仓库,这个仓库也伴随上万个测试工程师们走过最艰难的旅程,盼望也能帮助到你!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

圆咕噜咕噜

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表