我必须自动化一个 Web 应用程序,其中包含一个用于从本地文件系统上传文件的拖放区域。我的测试环境是使用Python开发的。对于自动化测试,我使用了 Selenium,但是一旦上传区域是 div 标签(没有输入标签 - 这样我知道这会很容易),就不可能从文件系统中拖动文件。
我读了很多不同的文章,但目前没有一篇对我有用。需要强调的是,我对使用 AutoIT 不感兴趣,只对带有 selenium 的原生 python 感兴趣。
我发现了这个 Selenium: Drag and Drop from file system to webdriver? 看起来很有前途,但我不知道如何适应 Python。
提前非常感谢!
这是通过脚本进行输入注入的 Python 版本技巧。
JS_DROP_FILE = """
var target = arguments[0],
offsetX = arguments[1],
offsetY = arguments[2],
document = target.ownerDocument || document,
window = document.defaultView || window;
var input = document.createElement('INPUT');
input.type = 'file';
input.onchange = function () {
var rect = target.getBoundingClientRect(),
x = rect.left + (offsetX || (rect.width >> 1)),
y = rect.top + (offsetY || (rect.height >> 1)),
dataTransfer = { files: this.files };
['dragenter', 'dragover', 'drop'].forEach(function (name) {
var evt = document.createEvent('MouseEvent');
evt.initMouseEvent(name, !0, !0, window, 0, 0, 0, x, y, !1, !1, !1, !1, 0, null);
evt.dataTransfer = dataTransfer;
target.dispatchEvent(evt);
});
setTimeout(function () { document.body.removeChild(input); }, 25);
};
document.body.appendChild(input);
return input;
"""
def drag_and_drop_file(drop_target, path):
driver = drop_target.parent
file_input = driver.execute_script(JS_DROP_FILE, drop_target, 0, 0)
file_input.send_keys(path)
当
drop_target
向其传递一些在页面上可见的元素时,您可以使用 driver.get_element_by_...
系列中的任何函数来获取该元素。
该方法是使用 selenium 的
execute_script
函数调用 javascript 来模拟拖放事件。代码的工作原理如下:
dragenter
、dragover
、drop
。我知道这可能是一个迟到的答案,但以防万一找到答案的人!
如果您使用 Chrome,请前往此网站下载 Chrome 驱动程序。 (尝试找到您的 chrome 版本通过此并选择合适的版本)
还有一件事你需要做 我现在就展示给大家看
第1步:转到您想要的站点并复制“拖放”的完整XPath,右键单击拖放区域,然后单击检查。
请执行此操作两次,以防万一它检查到正确的位置
第2步: 您将看到突出显示的颜色,再次右键单击它们
然后你会发现“复制”->“复制fullXpath”
等等!!! 请再提一个建议: 如果您发现粘贴“Xpath”或“链接到文件夹”时出现问题例如
您可以使用“ ”代替“”
from selenium import webdriver
driver = webdriver.Chrome('D:\Folder\chromedriver')
driver.get('https://exmaple.com')
drag_&_drop = driver.find_element_by_xpath('paste-the-full-xpath-here')
drag_&_drop.send_keys('D:\Folder\picture.png')
#python 3.9
距离上次回复已经有几年了,但我会为那些尝试使用带有 selenium、机器人框架或类似框架的 React Dropzone 的人添加自己的回复。在您的情况下,接受的答案可能仍然适用于纯硒,但由于我使用带有非标准库的机器人框架,我的答案可能不通用。这有点偏离主题,但当我参考这个来创建我自己的关键字时,我希望它有所帮助。
在我的例子中,我使用的是 Qweb,它需要一些额外的设置,例如获取库实例和 webElement。
from robot.libraries.BuiltIn import BuiltIn
JS_DROP_FILE = """
var target = arguments[0],
offsetX = arguments[1],
offsetY = arguments[2],
// I did not need to fetch the document separately, it was already accessible in my case.
window = document.defaultView;
var input = document.createElement('INPUT');
input.type = 'file';
target.appendChild(input);
input.onchange = function () {
var rect = target.getBoundingClientRect(),
x = rect.left + (offsetX || (rect.width / 2)),
y = rect.top + (offsetY || (rect.height / 2));
var dataTransfer = new DataTransfer();
for (var i = 0; i < this.files.length; i++) {
dataTransfer.items.add(this.files[i]);
}
['dragenter', 'dragover', 'drop'].forEach(function (name) {
var evt = new MouseEvent(name, {
bubbles: true,
cancelable: true,
clientX: x,
clientY: y,
dataTransfer: dataTransfer
});
target.dispatchEvent(evt);
});
var fileDropEvent = new CustomEvent("file-drop", {
bubbles: true,
detail: { dataTransfer: dataTransfer }
});
target.dispatchEvent(fileDropEvent);
setTimeout(function () { target.removeChild(input); }, 25);
};
return input; // Return the input element
// # debugging: console.log(var) prints to chrome console
"""
def drag_and_drop_file(drop_target, path):
qweb = BuiltIn().get_library_instance('Qweb')
driver = qweb.return_browser() # debug: expect <selenium.webdriver.chrome.webdriver.WebDriver (session="alphabet-soup")>
# for selenium library, you will use a something along these lines
# selib = BuiltIn().get_library_instance("SeleniumLibrary")
# driver = selib.driver
# element = selib.get_webelement(drop_target)
element = qweb.get_webelement(drop_target)
if isinstance(element, list):
element = element[0] # debug: expect <selenium.webdriver.remote.webelement.WebElement (session="alphabet-soup", element="longer-alphabet-soup")>
file_input = driver.execute_script(JS_DROP_FILE, element, 0, 0)
file_input.send_keys(path)
# debugging: Builtin().log_to_console(var) prints to terminal
一些显着的变化:
new DataTransfer()
和new MouseEvent()
是在铬中处理这些操作的更现代的方法。
其他注意事项:在我的例子中,元素附加到目标而不是主体,这使得调试更容易一些。如果您愿意,您仍然可以使用
body.appendChild
和 body.removeChild
。
使用 React:您最好需要对相关 React 代码有一定的访问权限,至少能够与开发人员合作,因为代码需要有一个事件监听器。
fileDropEvent
被分派到目标,因此 React 代码需要有一个 useEffect() 来检查目标,并向其添加 "file-drop"
的事件侦听器。这不是理想的情况,因为您不必为了 QA 目的而修改太多代码,但 React-dropzone 的自动化测试有点困难。
const handlePhotosUpload = useCallback(
// Your photo upload logic here
);
useEffect(() => {
const target = document.getElementById("upload-photos");
const handleFileDrop = (event: any) => {
// Prevent default browser handling
event.preventDefault();
if (event.detail?.dataTransfer?.files) {
handlePhotosUpload(Array.from(event.detail.dataTransfer.files));
}
};
if (target) {
target.addEventListener("file-drop", handleFileDrop);
}
return () => {
if (target) {
target.removeEventListener("file-drop", handleFileDrop);
}
};
}, [handlePhotosUpload]);
在机器人框架中的使用:
*** Settings ***
Library QWeb # Or SeleniumLibrary or Browser, whichever you use
Library drag-n-drop.py
*** Keywords ***
Upload Photo
[Arguments] ${photo}
[Documentation] Executes a custom script to drag and drop a photo into the dropzone.
Scroll to //*[@id\="upload-photos"].
# note: Qweb requires escape chars in xpath.
# In selenium, xpath://div[@id="example"] or id:example may work.
# I did not test this in selenium.
Drag And Drop File //*[@id\="upload-photos"] ${EXECDIR}/files/images/${photo}
Sleep 2s # wait for photo to upload
*** Test Cases ***
Upload A Photo
Upload Photo my_picture.jpeg