简体   繁体   中英

Selenium WebDriver: wait for element to be present when locating with WebDriver.findElement is impossible

It's convenient to wait for an WebElement to be present with WebDriverWait and ExpectedConditions .

The problem is, what if WebElement.findElment was the only possible way to locate the element , 'cause it has no id, no name, no unique class?

WebDriverWait 's constructor accepts only WebDriver as arguments, not WebElement .

I've set the implicitlyWait time, so it seems not a good idea to use try{} catch(NoSuchElementException e){} , 'cause I don't want to wait that long time for this element.

Here's the scenario:

There's one web page with a form containing many input tags. Each input tag has a format requirement.

A dynamic div tag would be present after this input tag when the format requirement is not satisfied.

As there're so many input tags, I create a general method like:

public WebElement txtBox(String name) {
    return driver.findElement(By.name(name));
}

instead of creating a data member for each input tag.

Then I create a method isValid to check whether user inputs in some input are valid. All I should do in isValid is to check whether a div tag is present after inputboxToCheck , with code like this:

public boolean isValid(WebElement inputboxToCheck) {
    WebElementWait wait = new WebElementWait(inputboxToCheck, 1);
    try {
        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("./following-sibling::div")));
        return false;
    } catch (TimeOutException e) {
        return true;
    }    
}

WebElementWait is an imaginary (not exist) class which works the same way as WebDriverWait .

The WebElementWait class as metioned above:

package org.openqa.selenium.support.ui;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebElement;

public class WebElementWait  extends FluentWait<WebElement>  {
    public final static long DEFAULT_SLEEP_TIMEOUT = 500;

      public WebElementWait(WebElement element, long timeOutInSeconds) {
            this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, DEFAULT_SLEEP_TIMEOUT);
      }

      public WebElementWait(WebElement element, long timeOutInSeconds, long sleepInMillis) {
            this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, sleepInMillis);
      }

      protected WebElementWait(WebElement element, Clock clock, Sleeper sleeper, long timeOutInSeconds,
              long sleepTimeOut) {
            super(element, clock, sleeper);
            withTimeout(timeOutInSeconds, TimeUnit.SECONDS);
            pollingEvery(sleepTimeOut, TimeUnit.MILLISECONDS);
            ignoring(NotFoundException.class);
      }

}

It's the same as WebDriverWait, except that the WebDriver argument is replaced with WebElement .

Then, the isValid method:

//import com.google.common.base.Function;
    //import org.openqa.selenium.TimeoutException;

public boolean isValid(WebElement e) {
    try {
        WebElementWait wait = new WebElementWait(e, 1);
        //@SuppressWarnings("unused")
        //WebElement icon = 
        wait.until(new Function<WebElement, WebElement>() {
                    public WebElement apply(WebElement d) {
                        return d.findElement(By
                                .xpath("./following-sibling::div[class='invalid-icon']"));
                    }
                });
        return false;
    } catch (TimeoutException exception) {
        return true;
    }
}

I don't know if this help you, but it permits to wait element how much time do you want.

public WebElement findDynamicElement(By by, int timeOut) {
    WebDriverWait wait = new WebDriverWait(driver, timeOut);
    WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(by));
    return element;
}

findDynamicElement(By.xpath("//body") , 30);

A more universal variant of user2432405's solution would be using SearchContext type rather then WebElement:

public class SearchContextWait  extends FluentWait<SearchContext>  {
    ...

This allows to do waits on both WebDriver and WebElement similarly as the SearchContext interface is the ancestor of both WebDriver and WebElement. The isValid method needs adjustment too:

...
        WebElement icon = wait
                .until(new Function<SearchContext, WebElement>() {
                    public WebElement apply(SearchContext d) {
...

Unfortunately, you lose all conveniences of ExpectedConditions.xxxx() methods as they use the WebDriver interface internally.

I found this blog: Checking for an element – exists?, visible?, present? - https://jkotests.wordpress.com/2012/11/02/checking-for-an-element-exists-visible-present/

And it brought up the differences between exists, visible, and present.

  • exists? – Returns whether this element actually exists.
  • present? – Returns true if the element exists and is visible on the page
  • visible? – If any parent element isn't visible then we cannot write to the element. The only reliable way to determine this is to iterate up the DOM element tree checking every element to make sure it's
    visible.

Exists will tell you if what you are searching for is anywhere in the DOM; however, WebDriver does not seem to have a built in way to check if an element exists similar to plain driver.findElement(By.name(name)) .

And, as explained in the blog, Exists is not the same as Present . So I can't use ExpectedConditions.presenceOfAllElementLocatedBy(By.cssSelector(cssSelector)

My solution: (looking for feedback here :)

public WebElement waitForElementExists(String selector, String timeout) {
    Wait<WebDriver> wait = new WebDriverWait(driver, timeout);

    WebElement element = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return driver.findElement(By.cssSelector(selector));
        }
    });

    return element;
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM