I've been using @FindBy
for a while now, and I love the fact that the element doesn't get located until its necessary (not on instantiation).
However, the webpage may have anywhere from 2-10 of a certain element, and the id's on the elements are numbered (so the first element has an id of "element1" and so forth)
I would like to write a function where I can pass in an integer, and it will return a WebElement with the appropriate ID, AND is lazily instantiated. That means having a function like the following won't work:
public WebElement getElement(int numOnPage){
return driver.findElement(By.id("element"+numOnPage));
}
Because the instant I call that function the WebElement gets located. (The reason why it can't be instantiated is because I have a function that waits until it the element exists by calling isDisplayed() over and over on it, catching NoSuchElementException
s).
I also realize that I could create a List<WebElement>
that selects via CSS every element whose ID starts with "element" but I have had other cases where I've wanted to return a dynamically generated element, and had to use a workaround there as well.
Thanks!
First, I don't really understand why you absolutely need to get a WebElement
reference before the element is really in the page. In a normal case, you could check that the page is completely loaded and then find for a WebElement
. First would be typically done with a loop and a catch for NoSuchElementException
as you mentioned.
However, if you need a reference for a WebElement
before it can't be found in the page, I would simply create a proxy which loads lazily (only when first time needed) the real WebElement
instance. Something like this:
public WebElement getElement(final int numOnPage) {
return (WebElement) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[] { WebElement.class }, new InvocationHandler() {
// Lazy initialized instance of WebElement
private WebElement webElement;
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (webElement == null) {
webElement = driver.findElement(By.id("element" + numOnPage));
}
return method.invoke(webElement, args);
}
});
}
By calling getElement
, you retrieve an object of type WebElement
. As soon as you call one of its method, it will be retrieved using WebDriver.findElement
. Note that, if you call a method on the proxy instance, the element must be in the page otherwise you get of course a NoSuchElementException
.
If i'm understanding the question correctly, you can't do this with the @FindBy annotation. The problem is that Annotations in Java are processed during compile time and as a consequence you cannot modify them on the fly:
http://docs.oracle.com/javase/tutorial/java/annotations/
It does however sound like your problem could be easily fixed by using an explicit wait:
public WebElement getElement(int numOnPage){
WebDriverWait waiting= new WebDriverWait(driver, 15, 100);
return waiting.until(ExpectedConditions.visibilityOfElementLocated(By.id("element"+numOnPage)));
}
This would scan the page waiting for the element to exist and be visible and when it is return a WebElement to you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With