Skip to content

Instantly share code, notes, and snippets.

@djangofan
Last active January 6, 2023 22:05
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save djangofan/5112655 to your computer and use it in GitHub Desktop.
Save djangofan/5112655 to your computer and use it in GitHub Desktop.
Exercise on safely waiting for unstable web elements with WebDriver.
// This is the official Selenium documention endorsed method of waiting for elements.
// This method is ineffective because it still suffers from
// the stale element exception.
public static void clickByLocator ( final By locator ) {
WebElement myDynamicElement = ( new WebDriverWait(driver, 10))
.until( ExpectedConditions.presenceOfElementLocated( locator ) );
myDynamicElement.click();
}
// I gleamed this method from the Selenium Google forum.
// The .ignoring fluent method was a huge hint
// to the eventual solution I found but it still didn't work
// because I needed the ExpectedCondition apply method to give
// me some kind of message on failures.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
@Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}
// This was my breakthrough. My first partially working code.
public static void clickByLocator( final By locator ) {
staticlogger.info( "Click by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(90000, TimeUnit.MILLISECONDS)
.pollingEvery(5500, TimeUnit.MILLISECONDS);
//.ignoring( StaleElementReferenceException.class );
wait.until( new ExpectedCondition<Boolean>() {
@Override
public Boolean apply( WebDriver webDriver ) {
try {
webDriver.findElement( locator ).click();
return true;
} catch ( StaleElementReferenceException e ) {
staticlogger.info( e.getMessage() + "\n");
staticlogger.info("Trying again...");
return false;
}
}
} );
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
}
// An extension of method3 although I am unsure if I trust it
// because it re-gets the element AND the element gets assigned
// again after returning from the method. A few opportunities
// to go stale.
public static WebElement getElementByLocator( final By locator ) {
staticlogger.info( "Get element by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(90000, TimeUnit.MILLISECONDS)
.pollingEvery(5500, TimeUnit.MILLISECONDS);
wait.until( new ExpectedCondition<Boolean>() {
@Override
public Boolean apply( WebDriver webDriver ) {
try {
webDriver.findElement( locator ).getTagName();
return true;
} catch ( StaleElementReferenceException e ) {
staticlogger.info( e.getMessage() + "\n");
staticlogger.info("Trying again for availability of element...");
return false;
}
}
} );
WebElement we = null;
try {
we = driver.findElement( locator ); // is this error prone?
} catch ( StaleElementReferenceException e ) {
staticlogger.info( "Stale element: \n" + e.getMessage() + "\n");
}
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
return we;
}
// I realized that the try-catch doesn't really need to be within
// the ExpectedCondition final block. By moving the try-catch outside
// I would have access to the WebElement returned from findElement().
// So, I can create my own Boolean expected condition while I loop to
// hopefully accomplish a similar thing as method3.
public static WebElement getElementByLocator( By locator ) {
staticlogger.info( "Get element by locator: " + locator.toString() );
long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 9, TimeUnit.SECONDS );
WebElement we = null;
boolean unfound = true;
int tries = 0;
while ( unfound && tries < 20 ) {
tries += 1;
staticlogger.info("Locating remaining time: " + (180-(9*(tries-1) )) + " seconds." );
try {
we = driver.findElement( locator );
unfound = false; // FOUND IT
} catch ( StaleElementReferenceException ser ) {
staticlogger.info( "ERROR: Stale element. " + locator.toString() );
unfound = true;
} catch ( NoSuchElementException nse ) {
staticlogger.info( "ERROR: No such element. " + locator.toString() );
unfound = true;
} catch ( Exception e ) {
staticlogger.info( e.getMessage() );
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
return we;
}
// Another approach, after everything I have learned, that might
// also be effective. With this method, a wait timeout occurs 3
// times within the 90 second limit. So, the method will run
// between 15-90 seconds, depending on the situation of failure.
public static WebElement getElementByLocator( final By locator ) {
LOGGER.info( "Get element by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring( StaleElementReferenceException.class ) ;
int tries = 0;
boolean found = false;
WebElement we = null;
while ( (System.currentTimeMillis() - startTime) < 91000 ) {
LOGGER.info( "Searching for element. Try number " + (tries++) );
try {
we = wait.until( ExpectedConditions.presenceOfElementLocated( locator ) );
found = true;
break;
} catch ( StaleElementReferenceException e ) {
LOGGER.info( "Stale element: \n" + e.getMessage() + "\n");
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
if ( found ) {
LOGGER.info("Found element after waiting for " + totalTime + " milliseconds." );
} else {
LOGGER.info( "Failed to find element after " + totalTime + " milliseconds." );
}
return we;
}
// create a demonstration of using a Closure instead of the typical inner class
// pattern for @Override that you see above in method #3
wait.until( webDriver -> webDriver.findElement( By.id( "foo" ) ) );
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout( 30, TimeUnit.SECONDS )
.pollingEvery( 5, TimeUnit.SECONDS )
.ignoring( NoSuchElementException.class, StaleElementReferenceException.class );
// using a customized expected condition
WebElement foo1 = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(By.id("foo1"));
}
});
// using a built-in expected condition
WebElement foo2 = wait.until( ExpectedConditions
.presenceOfElementLocated( By.id("foo2") ) );
// careful with this next one. it requires visibility attribute on html tag
WebElement foo3 = wait.until( ExpectedConditions
.visibilityOfElementLocated( By.id("foo3") ) );
@karvendhanp
Copy link

Wow.. it works awesome ..used method 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment