当输入是由Javascript点击创建的,并且从未使用Selenium附加到HTML文档时,我如何自动选择文件



例如,我有一个简单的网页复制(实际上代码不是我的,我无法控制它(:

<!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");

相关内容

  • 没有找到相关文章

最新更新