当通过 JS 事件加载新页面时,如何使 Selenium WebDriver 等待页面加载

How to make Selenium WebDriver wait for page to load when new page is loaded via JS event

我正在致力于自动化一个网站,该网站有许多 link 可以通过 JS 事件加载新页面。基本上,有些元素是可点击的,点击一个元素会导致一些 Java 脚本到 运行,这会导致提交表单并路由到新页面。

现在,如果这些只是标准的 HTML links 就没有问题,因为 Selenium 足够聪明,可以告诉您有一个新页面即将到来并等待做事。但尽管如此,Selenium 无法判断此实例中的点击是否导致新页面加载,因此它不会等待并继续加载。因此,它不会等待新页面,而是尝试查找不存在的元素,而我的测试都失败了。无赖。

作为临时解决方案,我只是将程序暂停三秒钟,如下所示:

oldPageDriver.clickThatButton();

try {
  Thread.sleep(3000);
} catch(InterruptedException ex) {
  Thread.currentThread().interrupt();
}

newPageDriver = new NewPageDriver(driver);
newPageDriver.doStuffOnNewPage();

这有点管用。我不喜欢它,因为它 "hacky," 只是中断程序而不是做一些更聪明的事情。因为延迟是在三秒内硬编码的,所以如果 link 正在工作但速度很慢,我会失败。我考虑过类似于隐式等待的方法,但它完成了同样的事情,而且在经过大量查找后,我在 Java 的任何地方都没有找到可靠、可行的答案。

那么,有人可以提出解决此问题的方法吗?具体来说,如何让 Selenium 知道需要一个新页面并等待它可用?

显式等待是您所需要的;

http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp

您可以直接将其添加到您的测试中,或者您可能希望将其干掉,尤其是在存在常见等待预期(例如旋转图标消失)的情况下。

您可以将点击方法扩展为始终在点击后等待,或者如果跟随页面对象,请将 wait_until_loaded 方法添加到基页 class。还有许多其他有效的方法,但取决于 AUT 的实现方式

简单的 ready2use 片段,非常适合我

static void waitForPageLoad(WebDriver wdriver) {
    WebDriverWait wait = new WebDriverWait(wdriver, 60);

    Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver input) {
            return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
        }

    };
    wait.until(pageLoaded);
}

等待 document.ready 事件并不是解决此问题的全部方法,因为此代码仍处于竞争状态:有时此代码会在处理点击事件之前触发,因此直接 returns,因为浏览器还没有开始加载新页面。

经过一番搜索,我在 Obay the testing goat 上找到了一个 post,它有解决这个问题的方法。该解决方案的 C# 代码是这样的:

 IWebElement page = null;
 ...
 public void WaitForPageLoad()
 {
    if (page != null)
    {
       var waitForCurrentPageToStale = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
       waitForCurrentPageToStale.Until(ExpectedConditions.StalenessOf(page));
    }

    var waitForDocumentReady = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    waitForDocumentReady.Until((wdriver) => (driver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));

    page = driver.FindElement(By.TagName("html"));

}

` 我在 driver.navigate.gotourl 之后直接触发此方法,以便它尽快获得页面的引用。玩得开心!