在 Selenium WebDriver 中拖放的 JavaScript 解决方法
Posted
技术标签:
【中文标题】在 Selenium WebDriver 中拖放的 JavaScript 解决方法【英文标题】:JavaScript workaround for drag and drop in Selenium WebDriver 【发布时间】:2013-10-23 11:07:09 【问题描述】:clickAndHold 在我的测试环境设置中不适合我。尝试使用高级用户交互执行它时,我不断收到此错误:
“不能按下多个按钮或一个已经按下的按钮。”调用方法时:[wdIMouse::down]"
我已经测试了许多带有 selenium 版本 2.31.0-2.35.0 的 Firefox 版本,而带有 selenium 2.35 的 Firefox 21 的问题最少。其他组合存在 click() 静默失败和可见元素被视为不可见的问题。
我想使用 javascript 解决方法将一个元素拖放到另一个元素,但在广泛搜索之后,我在任何地方都找不到任何合适的示例。
【问题讨论】:
你从哪里得到clickAndHold
?我在 Selenium2 或 Selenium1 中没有看到它
来自 Actions 包:Actions actions = new Actions(driver); actions.clickAndHold(element).moveToElement(targetElement).release(element).perform();
有可能有两个按钮具有相同的定位器,或者您在之前的拖放中没有释放元素。
我将代码拆分为单独执行每个操作。调用 clickAndHold 操作时会引发错误。没有两个元素具有相同的 id - 它绝对是唯一的。
你试过actions.dragAndDrop(element, targetElement).perform();
吗?
【参考方案1】:
我认为您应该采用的方式与 Richard 在原始问题的评论中建议的方式相同。使用动作类。只需在此论坛中搜索“Action Selenium”即可。如果您遇到静默失败,那么您需要做的就是确保您使用的是最新的 Selenium(您引用的 2.35 不是最新的)。此外,如果您使用 IE 或 Chrome,请确保您使用的二进制文件也对应于最新版本。
IE11 和 Chrome 和 Firefox 都会自动升级,因此如果您不进行驱动程序更新,您可能会从 Actions 类中获得不引发任何人类可读错误的奇怪行为。例如,在最新的 Chrome 31 上使用 2.30 驱动程序会打开一个浏览器,并且 .get() 会挂在你身上。
【讨论】:
【参考方案2】:试试这个
Actions act = new Actions(driver); act.clickAndHold(scroll).moveByOffset(x, 200).build().perform();似乎是麦汁
【讨论】:
【参考方案3】:我在 Firefox 上遇到了完全相同的问题,导致我来到这里。玩弄 Stack 上的建议,最后我能够使用
解决问题actions.DragAndDropToOffset(element, x, y).Perform();
我知道这不是您要求的 Javascript 解决方法,但希望它对您有用,就像对我一样。这些古怪的问题真的很令人沮丧。
【讨论】:
【参考方案4】:自从我发布这个问题以来,我已经找到了一些不同的解决方案来解决这个问题。我将它们发布在这里以供参考,以便这个问题对其他人有用:
当 Selenium 中的拖放无法用于 html5 时,针对 Selenium 输入了一个缺陷,一些人用建议的 Javascript 解决方案评论了该缺陷:
JQuery/HTML5 解决方案
http://code.google.com/p/selenium/issues/detail?id=3604#c9
这是一个基于 ruby 的解决方案,可以翻译成 Java、C# 或 Selenium 支持的任何其他语言。它需要 JQuery 出现在页面上,从其他用户尝试使用此解决方案的许多 cmets 中可以清楚地看出,这似乎仅适用于 HTML5 页面:
我们制定的解决方法对我们有用。这是一生 用于测试我们的 Ember.js 应用程序的保护程序。
附件是我们正在使用的最新版本...
这就是我们在 test_helper 中的内容:
def drag_and_drop(source,target)
js_filepath=File.dirname(__FILE__)+"/drag_and_drop_helper.js"
js_file= File.new(js_filepath,"r")
java_script=""
while (line=js_file.gets)
java_script+=line
end
js_file.close
@driver.execute_script(java_script+"$('#source').simulateDragDrop( dropTarget: '#target');")
rescue Exception => e
puts "ERROR :" + e.to_s
end
引用的Javascript拖放模拟的源代码贴在这里:
https://gist.github.com/rcorreia/2362544
(function( $ )
$.fn.simulateDragDrop = function(options)
return this.each(function()
new $.simulateDragDrop(this, options);
);
;
$.simulateDragDrop = function(elem, options)
this.options = options;
this.simulateEvent(elem, options);
;
$.extend($.simulateDragDrop.prototype,
simulateEvent: function(elem, options)
/*Simulating drag start*/
var type = 'dragstart';
var event = this.createEvent(type);
this.dispatchEvent(elem, type, event);
/*Simulating drop*/
type = 'drop';
var dropEvent = this.createEvent(type, );
dropEvent.dataTransfer = event.dataTransfer;
this.dispatchEvent($(options.dropTarget)[0], type, dropEvent);
/*Simulating drag end*/
type = 'dragend';
var dragEndEvent = this.createEvent(type, );
dragEndEvent.dataTransfer = event.dataTransfer;
this.dispatchEvent(elem, type, dragEndEvent);
,
createEvent: function(type)
var event = document.createEvent("CustomEvent");
event.initCustomEvent(type, true, true, null);
event.dataTransfer =
data:
,
setData: function(type, val)
this.data[type] = val;
,
getData: function(type)
return this.data[type];
;
return event;
,
dispatchEvent: function(elem, type, event)
if(elem.dispatchEvent)
elem.dispatchEvent(event);
else if( elem.fireEvent )
elem.fireEvent("on"+type, event);
);
)(jQuery);
另一位用户发布了其他代码,当它不能完全为他工作时,以及一些将 JQuery 注入页面的说明:
http://code.google.com/p/selenium/issues/detail?id=3604#c25
我们在将原始的 drag_and_drop_helper.js 发布为 此问题的解决方法。解决方法是 99% 正确,但我 需要修改解决方法以将 dropTarget 包含在 通过模拟拖动中的“坐标”对象传播的选项。
即我需要改变:
coord = clientX: x, clientY: y
到:
coord = clientX: x, clientY: y , dropTarget: options.dropTarget || undefined
另外,如果应用在 test 尚未将 jQuery 函数别名为 $,您需要 拼出 jQuery:
即,在将拖放助手注入页面后,我们有 一种接受 jQuery 选择器以使用模拟拖动和 删除函数(Java):
/**
* Drag and drop via the JQuery-based drag and drop helper -- the helper
* must have been injected onto the page prior to calling this method.
*
* @param dragSourceJQuerySelector a JQuery-style selector that identifies the source element to drag;
* <em>will be passed directly to jQuery(), perform all quoting yourself</em>
* @param dropTargetJQuerySelector a JQuery-style selector that identifies the target element to drop the source onto;
* <em>will be passed directly to jQuery(), perform all quoting yourself</em>
*/
protected void dragAndDropViaJQueryHelper(String dragSourceJQuerySelector, String dropTargetJQuerySelector)
String javascript =
"var dropTarget = jQuery(" + dropTargetJQuerySelector + ");" +
"\n" +
"jQuery("+ dragSourceJQuerySelector + ").simulate('drag', dropTarget: dropTarget );";
getLogger().info("executing javascript:\n" + javascript);
this.executeScript(javascript);
getLogger().info("executed drag-n-drop action via javascript");
另一种非 jQuery 解决方案
http://ynot408.wordpress.com/2011/09/22/drag-and-drop-using-selenium-webdriver/
基于 Javascript 的拖放,可跨浏览器使用。
2.3 版之后鼠标移动在使用时停止工作 硒中的 RemoteWebDriver。下面的函数将元素 1 拖到 元素 2 定位并释放鼠标。
public void dragdrop(By ByFrom, By ByTo)
WebElement LocatorFrom = driver.findElement(ByFrom);
WebElement LocatorTo = driver.findElement(ByTo);
String xto=Integer.toString(LocatorTo.getLocation().x);
String yto=Integer.toString(LocatorTo.getLocation().y);
((JavascriptExecutor)driver).executeScript("function simulate(f,c,d,e)var b,a=null;for(b in eventMatchers)if(eventMatchers[b].test(c))a=b;breakif(!a)return!1;document.createEvent?(b=document.createEvent(a),a==\"HTMLEvents\"?b.initEvent(c,!0,!0):b.initMouseEvent(c,!0,!0,document.defaultView,0,d,e,d,e,!1,!1,!1,!1,0,null),f.dispatchEvent(b)):(a=document.createEventObject(),a.detail=0,a.screenX=d,a.screenY=e,a.clientX=d,a.clientY=e,a.ctrlKey=!1,a.altKey=!1,a.shiftKey=!1,a.metaKey=!1,a.button=1,f.fireEvent(\"on\"+c,a));return!0 var eventMatchers=HTMLEvents:/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,MouseEvents:/^(?:click|dblclick|mouse(?:down|up|over|move|out))$/; " +
"simulate(arguments[0],\"mousedown\",0,0); simulate(arguments[0],\"mousemove\",arguments[1],arguments[2]); simulate(arguments[0],\"mouseup\",arguments[1],arguments[2]); ",
LocatorFrom,xto,yto);
【讨论】:
非 jQuery 解决方案有很大帮助。非常感谢您发布它。【参考方案5】:在这篇关于Safari 14 的帖子的后面,我正在测试一个剑道应用程序并尝试了 Selena 回答中的解决方案,但这是一个不同的解决方案,最终对我有用。
Test application.
Working drap and drop(使用 WebElements 而不是 CSS 字符串的小改动):
var triggerDragAndDrop = function (elemDrag, elemDrop)
/* function for triggering mouse events */
var fireMouseEvent = function (type, elem, centerX, centerY)
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent(
type,
true,
true,
window,
1,
1,
1,
centerX,
centerY,
false,
false,
false,
false,
0,
elem
);
elem.dispatchEvent(evt);
;
/* calculate positions*/
var pos = elemDrag.getBoundingClientRect();
var center1X = Math.floor((pos.left + pos.right) / 2);
var center1Y = Math.floor((pos.top + pos.bottom) / 2);
pos = elemDrop.getBoundingClientRect();
var center2X = Math.floor((pos.left + pos.right) / 2);
var center2Y = Math.floor((pos.top + pos.bottom) / 2);
/* mouse over dragged element and mousedown*/
fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
fireMouseEvent('mouseenter', elemDrag, center1X, center1Y);
fireMouseEvent('mouseover', elemDrag, center1X, center1Y);
fireMouseEvent('mousedown', elemDrag, center1X, center1Y);
/* start dragging process over to drop target*/
fireMouseEvent('dragstart', elemDrag, center1X, center1Y);
fireMouseEvent('drag', elemDrag, center1X, center1Y);
fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
fireMouseEvent('drag', elemDrag, center2X, center2Y);
fireMouseEvent('mousemove', elemDrop, center2X, center2Y);
/* trigger dragging process on top of drop target*/
fireMouseEvent('mouseenter', elemDrop, center2X, center2Y);
fireMouseEvent('dragenter', elemDrop, center2X, center2Y);
fireMouseEvent('mouseover', elemDrop, center2X, center2Y);
fireMouseEvent('dragover', elemDrop, center2X, center2Y);
/* release dragged element on top of drop target*/
fireMouseEvent('drop', elemDrop, center2X, center2Y);
fireMouseEvent('dragend', elemDrag, center2X, center2Y);
fireMouseEvent('mouseup', elemDrag, center2X, center2Y);
return true;
;
根据上面 Selena 答案中的解决方案注入 javascript(将文件读入变量 java_script
)。针对现有的 WebElements(drag
和 drop
)执行:
String js = "var src = arguments[0];var dest = arguments[1];";
js += "triggerDragAndDrop(src, dest);";
JavascriptExecutor executor = (JavascriptExecutor) webdriver;
executor.executeScript(java_script + js, drag, drop);
【讨论】:
以上是关于在 Selenium WebDriver 中拖放的 JavaScript 解决方法的主要内容,如果未能解决你的问题,请参考以下文章