硒:尽管存在不可见元素,但让findElements等待可见元素

时间:2019-06-19 19:41:01

标签: java selenium selenium-webdriver webdriverwait implicitwait

我们想将一些键发送到按名称标识的元素。在应用程序中,可能有多个具有相同名称的元素,但是在那种情况下,只有一个是可见的。为此,我们有一个这样的代码片段(简单的代码,没有生产代码):

List<WebElement> list = driver.findElements(By.xpath("//[@name='title']"));
for (WebElement elem : list) {
    try {
         elem.sendKeys(value);
         break;
    } catch (Exception e) {
         // ignore
    }
}

如果title元素还不存在,我们使用隐式等待来等待它出现。因此通常这可以正常工作。无论如何,有时我们会遇到这样的情况,即已经有具有该名称的元素(但是它们是隐藏的),而正确的元素将仅由异步代码创建。但是在这种情况下,代码将无法工作。由于findElements()将立即返回(没有隐式等待),仅返回不可见元素。在那种情况下,sendKeys()将等待该元素变为可见,但是这种情况永远不会发生(忽略在findElements之后创建的新元素),因此它在隐式等待超时后失败。

基本上,我们需要告诉findElements()我们只想拥有可见元素。如果没有可见元素,Selenium应该等待隐式等待时间。这可能吗?

2 个答案:

答案 0 :(得分:0)

您的用例涉及:

  • 可能有多个具有相同名称的元素,但在这种情况下,只有一个元素可见
  • 将一些键发送到按名称标识的元素
  • 等待它出现
  • 使用隐式等待

为满足上述所有条件的多功能解决方案是将WebDriverWaitExpectedConditions设置为elementToBeClickable()一起使用。

  • elementToBeClickable():可见并启用了检查元素的期望,以便您可以单击它。

  • 代码示例:

    try {
        new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='nsg-button']"))).sendKeys(value);
    }
    catch (TimeoutException e) {
        System.out.println("Desired element was not present");
    }
    

此外,您还必须删除ImplicitWait的所有实例

  

注意请勿混用 implicit and explicit waits 。这样做可能导致 unpredictable wait times 。例如,将隐式等待设置为10秒,将显式等待设置为15秒,则可能导致20秒后发生超时。

您可以在Replace implicit wait with explicit wait (selenium webdriver & java)

中找到相关的讨论

答案 1 :(得分:0)

基于DebanjanB和JeffC的回答,我能够创建自己的wait实现,该实现等待第一个可见元素,但也考虑了在等待期间创建的元素:

new WebDriverWait(driver, 5).until(drv -> {
    List<WebElement> elements = drv.findElements(By.xpath("//[@name='title']"));
    for (WebElement element : elements) {
        if (element.isDisplayed()) {
            return element;
        }
    }
    return null;
});

或带有流;-)

new WebDriverWait(driver, 5).until(drv -> {
    List<WebElement> elements = drv.findElements(By.xpath("//[@name='title']"));
    return elements.stream()
        .filter(WebElement::isDisplayed)
        .findFirst()
        .orElse(null);
});