Last active
January 6, 2023 22:05
-
-
Save djangofan/5112655 to your computer and use it in GitHub Desktop.
Exercise on safely waiting for unstable web elements with WebDriver.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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."); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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" ) ) ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") ) ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Wow.. it works awesome ..used method 5