Selenium: пусть findElements ждет видимого элемента, хотя невидимый элемент существует

Мы хотим отправить некоторые ключи элементу, идентифицированному по имени. В приложении может быть несколько элементов с одинаковым именем, но в этом случае будет виден только один элемент. Для этого у нас есть такой фрагмент кода (упрощенный код, без производственного кода):

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 ответа

Решение

Основываясь на ответе от DebanjanB и JeffC, я смог создать собственную реализацию ожидания, которая ожидает первый видимый элемент, но также учитывает элементы, созданные во время ожидания:

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);
});

Поскольку ваш сценарий использования включает в себя:

  • может быть несколько элементов с одинаковым именем, но в этом случае будет виден только один элемент
  • отправить несколько ключей элементу, указанному по имени
  • ждать, пока оно появится
  • используя неявное ожидание

Многофункциональным решением для удовлетворения всех вышеупомянутых условий было бы использование WebDriverWait в сочетании с ExpectedConditions, установленным как 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 секунд.

Соответствующее обсуждение можно найти в разделе Замена неявного ожидания явным ожиданием (selenium webdriver & java).

Другие вопросы по тегам