例如,我有一个简单的网页复制(实际上代码不是我的,我无法控制它(:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<button>Click me!</button>
<script>
document.querySelector("button").addEventListener("click", () => {
const txt = document.createElement("input");
txt.type = "file";
txt.addEventListener("change", () => {
console.log(txt.files[0]);
});
txt.click();
});
</script>
</body>
</html>
正如你所看到的,我们看到的只是一个按钮。当单击按钮时,它会创建一个输入并激活它,而不会将其附加到HTML文档。
请告诉我在这种情况下如何自动化(即单击按钮,选择一个文件(。我正在使用C#。NET,如果它是相关的,但我认为总的方向是可以的。
如果您的输入被添加到DOM中,这将是一个简单的问题,我相信您已经知道了。正常的过程看起来像这样:
<input type="file" id="uploadhere" />
IWebElement element = driver.FindElement(By.Id("uploadhere"));
element.SendKeys("C:\Some_Folder\MyFile.txt");
SendKeys
处理实际上传的魔力,但它不是通过访问文件上传菜单来实现的,而是通过直接与输入元素交互来实现的。
您的问题是输入元素不在DOM中并且不可见。WebDriver API设计用于处理用户可见的动态变化的DOM元素,模仿与UI的交互。它可以点击你的按钮,因为它是可见的,但它看不到输入元素,因为它不在DOM中或不可见。
你会遇到困难,无法直接用Selenium解决这个问题,但可能有一个变通办法。自动化API由提供。Net可以为您提供一种监视FileDialog本身的方法。
像这样的东西可能会提供一条路径:
System.Windows.Forms.SendKeys.SendWait("pathToFile")
甚至是直接行动,但这是脆弱的:
Actions action = new Actions(driver);
action.SendKeys(pObjElement, Keys.Space).Build().Perform();
Thread.Sleep(TimeSpan.FromSeconds(2));
var dialogHWnd = FindWindow(null, "Select a file to upload..."); // Here goes the title of the dialog window
var setFocus = SetForegroundWindow(dialogHWnd);
if (setFocus)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
System.Windows.Forms.SendKeys.SendWait(pFile);
System.Windows.Forms.SendKeys.SendWait("{DOWN}");
System.Windows.Forms.SendKeys.SendWait("{TAB}");
System.Windows.Forms.SendKeys.SendWait("{TAB}");
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
}
通过脚本注入预先抑制单击。文件输入仍将在单击时创建,但模式文件对话框不会出现。您还必须在DOM中插入输入,以便通过Selenium:进行检索
string JS_PREP_FILE_INPUT = @"
HTMLInputElement.prototype.click = function () {
if (!this.parentNode) {
this.style.display = 'none';
document.documentElement.appendChild(this);
this.addEventListener('change', () => this.remove());
}
}
";
driver.ExecuteScript(JS_PREP_FILE_INPUT);
driver.FindElement(By.CssSelector("button"))
.Click();
driver.FindElement(By.CssSelector("input[type=file]"))
.SendKeys("C:\myfile.txt");
根据您共享的脚本:
<body>
<button>Click me!</button>
<script>
document.querySelector("button").addEventListener("click", () => {
const txt = document.createElement("input");
txt.type = "file";
txt.addEventListener("change", () => {
console.log(txt.files[0]);
});
txt.click();
});
</script>
</body>
将在DOM树中添加<input>
类型的WebElement,其中type
属性的值为file
,如下所示:
<input type="file" ...>
然而,我认为在将相同的<input>
标记添加到HTMLDOM中后,查找它不会有任何问题。理想情况下,您应该能够定位element_to_be_clickable()
的元素诱导WebDriverWait,并且您可以使用以下定位器策略之一:
使用CssSelector考虑元素是DOM树中唯一的
<input>
元素:new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("input[type='file']"))).SendKeys("/filename/with/absolute/path");
使用XPath考虑元素是DOM树中唯一的
<input>
元素:new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//input[@type='file']"))).SendKeys("/filename/with/absolute/path");
使用XPath考虑DOM树中存在多个
<input>
元素:new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//button[text()='Click me!']//following::input[@type='file']"))).SendKeys("/filename/with/absolute/path");
使用SeleniumExtras。服务员
如果您需要SeleniumExtras.WaitHelpers
:
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(By.XPath("//input[@type='file']"))).SendKeys("/filename/with/absolute/path");