簡體   English   中英

當輸入由 Javascript 在單擊時創建並且從未使用 Selenium 附加到 HTML 文檔時,如何自動選擇文件?

[英]How do I automate file selecting when the input is created by Javascript on click and never attached to the HTML document using Selenium?

例如,我有一個網頁的簡單復制(實際上代碼不是我的,我無法控制):

<!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 解決此問題,但可能有一種解決方法。 .Net提供的Automation API可以為您提供一種監控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>type屬性值為fileWebElement ,如下所示:

<input type="file" ...>

但是,一旦在HTML DOM中添加相同的<input>標記,我看不到任何問題。 理想情況下,您應該能夠為element_to_be_clickable()定位誘導WebDriverWait的元素,並且您可以使用以下任一定位器策略

  • 考慮到元素是 DOM 樹中唯一的<input>元素,使用CssSelector

     new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("input[type='file']"))).SendKeys("/filename/with/absolute/path");
  • 考慮到元素是 DOM 樹中唯一的<input>元素,使用XPath

     new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//input[@type='file']"))).SendKeys("/filename/with/absolute/path");
  • 考慮到 DOM 樹中存在多個<input>元素,使用XPath

     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.WaitHelpers

如果您需要SeleniumExtras.WaitHelpers

new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(By.XPath("//input[@type='file']"))).SendKeys("/filename/with/absolute/path");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM