Failover to JavascriptExecutor when Selenium2 findElement fails? - java

I have this idea for failing over to a JavascriptExecutor if Selenium2 fails to retrieve a WebElement object after polling for a limited time. As you can see, the method has the limitation of needing the "failover" Javascript snippet to be pre-defined when calling getElementByLocator. I could not think of any way to dynamically do this. If anyone can help me improve on this, I will award the answer to the best suggestion, however small it is.
// failover example1: "document.getElementById('gbqfb')"
// failover example2: "document.querySelector("div#gbqfb")"
public static WebElement getElementByLocator(final By locator, String failover) {
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS);
.ignoring(NoSuchElementException.class,StaleElementReferenceException.class);
WebElement we = wait.until( ExpectedConditions
.presenceOfElementLocated( locator ) );
if ( we.isNull() ) {
JavascriptExecutor js = (JavascriptExecutor) driver;
if ( !failover.isEmpty() ) {
we = (WebElement)js.executeScript( failover );
if ( we.isNull() ) LOG.info("Still couldn't get element.");
} else {
LOG.info("No failover String available. Cannot try with " +
"a JavascriptExecutor.");
}
}
return we;
}

Actually answered a similar question here
I wouldn't recommend delegating anything to javascript like that.. Use what Selenium gives you.. it's very sufficient.
Something that I put in every single framework i've ever built, which is VERY efficient.. Here is an exerpt from the framework found here.
I implement sort of a pseudo-wait type method before i ever perform any actions on objects. Try it yourself. It's very efficient.
These are methods from the AutomationTest class
/**
* Checks if the element is present or not.<br>
* #param by
* #return <i>this method is not meant to be used fluently.</i><br><br.
* Returns <code>true</code> if the element is present. and <code>false</code> if it's not.
*/
public boolean isPresent(By by) {
if (driver.findElements(by).size() > 0) return true;
return false;
}
/**
* Private method that acts as an arbiter of implicit timeouts of sorts.. sort of like a Wait For Ajax method.
*/
private WebElement waitForElement(By by) {
int attempts = 0;
int size = driver.findElements(by).size();
while (size == 0) {
size = driver.findElements(by).size();
if (attempts == MAX_ATTEMPTS) fail(String.format("Could not find %s after %d seconds",
by.toString(),
MAX_ATTEMPTS));
attempts++;
try {
Thread.sleep(1000); // sleep for 1 second.
} catch (Exception x) {
fail("Failed due to an exception during Thread.sleep!");
x.printStackTrace();
}
}
if (size > 0) System.err.println("WARN: There are more than 1 " + by.toString() + " 's!");
return driver.findElement(by);
}
What I do, is anytime i perform something, like
getText(By.cssSelector("input#someId"))
if it doesn't find it the first time, it'll wait 1 second. if it finds it then, continues. does that subsequently 5 times, so a total of 5 seconds wait.. which is perfectly ok because if you DONT find the element you need, then your test effectively should fail at that point.
Also, from experience, i can tell you that using driver.findElements() is more effective than those WebDriverWait's.
That doesn't mean i don't use them.. just not for that. Unfortunately, I had not added this functionality to the getting started with selenium framework, so i'll just tell you when i used Webdriverwait's.
So my test would look like -
#Config(url="http://systemunder.test", browser=CHROME)
public class MyClass extends AutomationTest {
#Test
public void testSomething() {
setText(By.id("blah")) // if <* id="blah" /> doesn't exist, waits 1+ seconds for it to appear before interacting.
.click(By.id("Blah2")) // ^ same thing here.
.waitForPresent(By.cssSelector("ajaxy")); // this method right here would circumvent the hard waits, with webdriverwait's.
}
}
i don't remember exactly why it didn't work for me before, but using webdriverwaits in something like that, is flawless.

Related

Trying to resolve StaleElementReferenceException error [duplicate]

I am implementing a lot of Selenium tests using Java - sometimes, my tests fail due to a StaleElementReferenceException.
Could you suggest some approaches to making the tests more stable?
This can happen if a DOM operation happening on the page is temporarily causing the element to be inaccessible. To allow for those cases, you can try to access the element several times in a loop before finally throwing an exception.
Try this excellent solution from darrelgrainger.blogspot.com:
public boolean retryingFindClick(By by) {
boolean result = false;
int attempts = 0;
while(attempts < 2) {
try {
driver.findElement(by).click();
result = true;
break;
} catch(StaleElementException e) {
}
attempts++;
}
return result;
}
I was having this issue intermittently. Unbeknownst to me, BackboneJS was running on the page and replacing the element I was trying to click. My code looked like this.
driver.findElement(By.id("checkoutLink")).click();
Which is of course functionally the same as this.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();
What would occasionally happen was the javascript would replace the checkoutLink element in between finding and clicking it, ie.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();
Which rightfully led to a StaleElementReferenceException when trying to click the link. I couldn't find any reliable way to tell WebDriver to wait until the javascript had finished running, so here's how I eventually solved it.
new WebDriverWait(driver, timeout)
.ignoring(StaleElementReferenceException.class)
.until(new Predicate<WebDriver>() {
#Override
public boolean apply(#Nullable WebDriver driver) {
driver.findElement(By.id("checkoutLink")).click();
return true;
}
});
This code will continually try to click the link, ignoring StaleElementReferenceExceptions until either the click succeeds or the timeout is reached. I like this solution because it saves you having to write any retry logic, and uses only the built-in constructs of WebDriver.
Kenny's solution is good, however it can be written in a more elegant way
new WebDriverWait(driver, timeout)
.ignoring(StaleElementReferenceException.class)
.until((WebDriver d) -> {
d.findElement(By.id("checkoutLink")).click();
return true;
});
Or also:
new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();
But anyway, best solution is to rely on Selenide library, it handles this kind of things and more. (instead of element references it handles proxies so you never have to deal with stale elements, which can be quite difficult). Selenide
Generally this is due to the DOM being updated and you trying to access an updated/new element -- but the DOM's refreshed so it's an invalid reference you have..
Get around this by first using an explicit wait on the element to ensure the update is complete, then grab a fresh reference to the element again.
Here's some psuedo code to illustrate (Adapted from some C# code I use for EXACTLY this issue):
WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10));
IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
IWebElement editLink = aRow.FindElement(By.LinkText("Edit"));
//this Click causes an AJAX call
editLink.Click();
//must first wait for the call to complete
wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE));
//you've lost the reference to the row; you must grab it again.
aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
//now proceed with asserts or other actions.
Hope this helps!
The reason why the StaleElementReferenceException occurs has been laid out already: updates to the DOM between finding and doing something with the element.
For the click-Problem I've recently used a solution like this:
public void clickOn(By locator, WebDriver driver, int timeout)
{
final WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(ExpectedConditions.refreshed(
ExpectedConditions.elementToBeClickable(locator)));
driver.findElement(locator).click();
}
The crucial part is the "chaining" of Selenium's own ExpectedConditions via the ExpectedConditions.refreshed(). This actually waits and checks if the element in question has been refreshed during the specified timeout and additionally waits for the element to become clickable.
Have a look at the documentation for the refreshed method.
In my project I introduced a notion of StableWebElement. It is a wrapper for WebElement which is able to detect if element is Stale and find a new reference to the original element. I have added a helper methods to locating elements which return StableWebElement instead of WebElement and the problem with StaleElementReference disappeared.
public static IStableWebElement FindStableElement(this ISearchContext context, By by)
{
var element = context.FindElement(by);
return new StableWebElement(context, element, by, SearchApproachType.First);
}
The code in C# is available on my project's page but it could be easily ported to java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs
A solution in C# would be:
Helper class:
internal class DriverHelper
{
private IWebDriver Driver { get; set; }
private WebDriverWait Wait { get; set; }
public DriverHelper(string driverUrl, int timeoutInSeconds)
{
Driver = new ChromeDriver();
Driver.Url = driverUrl;
Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeoutInSeconds));
}
internal bool ClickElement(string cssSelector)
{
//Find the element
IWebElement element = Wait.Until(d=>ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver);
return Wait.Until(c => ClickElement(element, cssSelector));
}
private bool ClickElement(IWebElement element, string cssSelector)
{
try
{
//Check if element is still included in the dom
//If the element has changed a the OpenQA.Selenium.StaleElementReferenceException is thrown.
bool isDisplayed = element.Displayed;
element.Click();
return true;
}
catch (StaleElementReferenceException)
{
//wait until the element is visible again
element = Wait.Until(d => ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver);
return ClickElement(element, cssSelector);
}
catch (Exception)
{
return false;
}
}
}
Invocation:
DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10);
driverHelper.ClickElement("input[value='csharp']:first-child");
Similarly can be used for Java.
Kenny's solution is deprecated use this, i'm using actions class to double click but you can do anything.
new FluentWait<>(driver).withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
.ignoring(StaleElementReferenceException.class)
.until(new Function() {
#Override
public Object apply(Object arg0) {
WebElement e = driver.findelement(By.xpath(locatorKey));
Actions action = new Actions(driver);
action.moveToElement(e).doubleClick().perform();
return true;
}
});
I've found solution here. In my case element becomes inaccessible in case of leaving current window, tab or page and coming back again.
.ignoring(StaleElement...), .refreshed(...) and elementToBeClicable(...) did not help and I was getting exception on act.doubleClick(element).build().perform(); string.
Using function in my main test class:
openForm(someXpath);
My BaseTest function:
int defaultTime = 15;
boolean openForm(String myXpath) throws Exception {
int count = 0;
boolean clicked = false;
while (count < 4 || !clicked) {
try {
WebElement element = getWebElClickable(myXpath,defaultTime);
act.doubleClick(element).build().perform();
clicked = true;
print("Element have been clicked!");
break;
} catch (StaleElementReferenceException sere) {
sere.toString();
print("Trying to recover from: "+sere.getMessage());
count=count+1;
}
}
My BaseClass function:
protected WebElement getWebElClickable(String xpath, int waitSeconds) {
wait = new WebDriverWait(driver, waitSeconds);
return wait.ignoring(StaleElementReferenceException.class).until(
ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.xpath(xpath))));
}
Clean findByAndroidId method that gracefully handles StaleElementReference.
This is heavily based off of jspcal's answer but I had to modify that answer to get it working cleanly with our setup and so I wanted to add it here in case it's helpful to others. If this answer helped you, please go upvote jspcal's answer.
// This loops gracefully handles StateElementReference errors and retries up to 10 times. These can occur when an element, like a modal or notification, is no longer available.
export async function findByAndroidId( id, { assert = wd.asserters.isDisplayed, timeout = 10000, interval = 100 } = {} ) {
MAX_ATTEMPTS = 10;
let attempt = 0;
while( attempt < MAX_ATTEMPTS ) {
try {
return await this.waitForElementById( `android:id/${ id }`, assert, timeout, interval );
}
catch ( error ) {
if ( error.message.includes( "StaleElementReference" ) )
attempt++;
else
throw error; // Re-throws the error so the test fails as normal if the assertion fails.
}
}
}
StaleElementReferenceException
StaleElementReferenceException indicates that the reference to an element is now stale i.e. the element no longer appears within the HTML DOM of the page.
Details
Every DOM Tree element is identified by the WebDriver by a unique identifying reference, known as a WebElement. The web element reference is a UUID used to execute commands targeting specific elements, such as getting an element's tag name or retrieving a property off an element.
When an element is no longer attached to the DOM, i.e. it has been removed from the document or the document has changed, it is said to be got stale. Staleness occurs for example when you have a web element reference and the document it was retrieved from navigates and due to navigation, all web element references to the previous document will be discarded along with the document. This will cause any subsequent interaction with the web element to fail with the stale element reference error.
Solution
The best approach to avoid StaleElementReferenceException is to induce WebDriverWait for the elementToBeClickable() before invoking click as follows:
new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.cssSelector("elementCssSelector"))).click();
Note: You have to import the following:
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.By;
Create a wrapper function (Java)
As an alternative to the accepted answer, my approach is similar in that it catches the exception and makes a few attempts, but it's more generic, so you can throw any kinds of actions at it as long as they are wrapped in a void function.
Please feel free to copy and use this code:
public void doPreventingStaleElement(Runnable function)
{
int maxRetries = 3; // maximum number of retries
int retries = 0;
boolean stale;
// try/catch block attempts to fix a stale element
do {
try {
function.run();
stale = false;
}
catch (StaleElementReferenceException eStale) {
stale = true;
// Work-around for stale element reference when getting the first page
if (retries < maxRetries) {
retries++;
System.out.println(function.getClass().getSimpleName() + " failed due to stale element reference, retry=" + retries);
try {
// Exponential increase of wait time - 1, 4, 9, 16, 25 seconds
Thread.sleep(1000 * (int) Math.pow(retries,2));
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
else {
System.out.println(function.getClass().getSimpleName() + " failed due to stale element reference, too many retries");
eStale.printStackTrace();
throw(eStale);
}
}
} while (stale && retries < maxRetries);
return;
}
Note that it will still throw a StaleElementReferenceException after maxRetries attempts.
Example of usage
As an example I want to do this:
final List<WebElement> buttons = getDriver().findElements(By.xpath("//button[#testid='dismiss-me']"));
for (final WebElement closeButton : buttons) {
closeButton.click();
}
or this:
driver.findElement(By.id("login-form-username")).sendKeys(getUser());
driver.findElement(By.id("login-form-password")).sendKeys(getPassword());
driver.findElement(By.id("login-form-submit")).click();
Then I wrap them in void functions
private void clickButtons() {
final List<WebElement> buttons = getDriver().findElements(By.xpath("//button[#testid='dismiss-me']"));
for (final WebElement closeButton : buttons) {
closeButton.click();
}
}
private void performLogin() {
driver.findElement(By.id("login-form-username")).sendKeys(getUser());
driver.findElement(By.id("login-form-password")).sendKeys(getPassword());
driver.findElement(By.id("login-form-submit")).click();
}
and so I can just
doPreventingStaleElement(whateverObject::clickButtons);
doPreventingStaleElement(whateverObject::performLogin);
Try this
while (true) { // loops forever until break
try { // checks code for exceptions
WebElement ele=
(WebElement)wait.until(ExpectedConditions.elementToBeClickable((By.xpath(Xpath))));
break; // if no exceptions breaks out of loop
}
catch (org.openqa.selenium.StaleElementReferenceException e1) {
Thread.sleep(3000); // you can set your value here maybe 2 secs
continue; // continues to loop if exception is found
}
}
There could be a potential problem that leads to the StaleElementReferenceException that no one mentioned so far (in regard to actions).
I explain it in Javascript, but it's the same in Java.
This won't work:
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
But instantiating the actions again will solve it:
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
actions = driver.actions({ bridge: true }) // new
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
Usually StaleElementReferenceException when element we try to access has appeared but other elements may affect the position of element we are intrested in hence when we try to click or getText or try to do some action on WebElement we get exception which usually says element not attached with DOM.
Solution I tried is as follows:
protected void clickOnElement(By by) {
try {
waitForElementToBeClickableBy(by).click();
} catch (StaleElementReferenceException e) {
for (int attempts = 1; attempts < 100; attempts++) {
try {
waitFor(500);
logger.info("Stale element found retrying:" + attempts);
waitForElementToBeClickableBy(by).click();
break;
} catch (StaleElementReferenceException e1) {
logger.info("Stale element found retrying:" + attempts);
}
}
}
protected WebElement waitForElementToBeClickableBy(By by) {
WebDriverWait wait = new WebDriverWait(getDriver(), 10);
return wait.until(ExpectedConditions.elementToBeClickable(by));
}
In above code I first try to wait and then click on element if exception occurs then I catch it and try to loop it as there is a possibility that still all elements may not be loaded and again exception can occur.
This works for me using C#
public Boolean RetryingFindClick(IWebElement webElement)
{
Boolean result = false;
int attempts = 0;
while (attempts < 2)
{
try
{
webElement.Click();
result = true;
break;
}
catch (StaleElementReferenceException e)
{
Logging.Text(e.Message);
}
attempts++;
}
return result;
}
The problem is by the time you pass the element from Javascript to Java back to Javascript it can have left the DOM.
Try doing the whole thing in Javascript:
driver.executeScript("document.querySelector('#my_id')?.click()")
Maybe it was added more recently, but other answers fail to mention Selenium's implicit wait feature, which does all the above for you, and is built into Selenium.
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
This will retry findElement() calls until the element has been found, or for 10 seconds.
Source - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

WebDriverWait. Fast check element exist or not exist

I got a problem and I don't know how to solve it right. Situation: user enters login and password, and then he could be at one of two pages.
Question is: how to check correctly in which page we are?
1. I want to use WebDriverWait, so my implicitlyWait = 0 ms,
2. I use Page Object pattern, pages were initialized with AjaxElementLocatorFactory
a. So, if I'll do method for check some element like this:
#FindBy(id = "pushOutMessage")
private WebElement messageText;
public boolean pageIsPresent() {
return messageText.isDisplayed();
}
It will be not right because if page is wrong, then WebDriver will wait N seconds for this element. So it makes my simple tests slow, very-very slow.
b. If I will check element with "findElement" - my implicity waits is 0 ms, so if element was slowly-loaded pageIsPresent returns false, even if page is right.
I hope there is some other way to do this. Need your help!
There are multiple ways you can do that. but the easiest way will be to check the elemenet counts
I would rather do
#FindBy(id = "pushOutMessage")
private List<WebElement> elements;
public int pageIsPresent() {
return elements.size();
}
And, somewhere test the pageIsPresent() is 0 or greater. if greater than 0 we know the page element was returned
And, since you are using pageobject pattern and Java I would recommand you to create an overloading to the baseClass which will check for a selector everytime you instantiate a new pageobject. I have a git repo here with TestNG. that might help
After the user enters or clicks submit or login button you can do something like this:
public Object clickLogin() {
loginElement.click();
try{
new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementedLocatedBy(By.id("pushOutMessage")));
return PageFactory.initElements(driver, FirstPage.class);
} catch (TimeoutException te) {
return PageFactory.initElements(driver, SecondPage.class);
}
}

How to resolve, Stale element exception? if element is no longer attached to the DOM?

I have a question regarding "Element is no longer attached to the DOM".
I tried different solutions but they are working intermittent. Please suggest a solution that could be permanent.
WebElement getStaleElemById(String id, WebDriver driver) {
try {
return driver.findElement(By.id(id));
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElemById(id, driver);
}
}
WebElement getStaleElemByCss(String css, WebDriver driver) {
try {
return driver.findElement(By.cssSelector(css));
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElemByCss(css, driver);
} catch (NoSuchElementException ele) {
System.out.println("Attempting to recover from NoSuchElementException ...");
return getStaleElemByCss(css, driver);
}
}
Thanks,
Anu
The problem
The problem you are probably facing is that the method returns the right (and valid!) element, but when you're trying to access it a second later, it is stale and throws.
This usually arises when:
You click something that loads a new page asynchronously or at least changes it.
You immediatelly (before the page load could finish) search for an element ... and you find it!
The page finally unloads and the new one loads up.
You try to access your previously found element, but now it's stale, even though the new page contains it, too.
The solutions
There are four ways to solve it I know about:
Use proper waits
Use proper waits after every anticipated page-load when facing asynchronous pages. Insert an explicit wait after the initial click and wait for the new page / new content to load. Only after that you can try to search for the element you want. This should be the first thing you'll do. It will increase the robustness of your tests greatly.
The way you did it
I have been using a variant of your method for two years now (together with the technique above in solution 1) and it absolutely works most of the time and fails only on strange WebDriver bugs. Try to access the found element right after it is found (before returning from the method) via a .isDisplayed() method or something. If it throws, you already know how to search again. If it passes, you have one more (false) assurance.
Use a WebElement that re-finds itself when stale
Write a WebElement decorator that remembers how it was found and re-find it when it's accessed and throws. This obviously forces you to use custom findElement() methods that would return instances of your decorator (or, better yet, a decorated WebDriver that would return your instances from usual findElement() and findElemens() methods). Do it like this:
public class NeverStaleWebElement implements WebElement {
private WebElement element;
private final WebDriver driver;
private final By foundBy;
public NeverStaleWebElement(WebElement element, WebDriver driver, By foundBy) {
this.element = element;
this.driver = driver;
this.foundBy = foundBy;
}
#Override
public void click() {
try {
element.click();
} catch (StaleElementReferenceException e) {
// log exception
// assumes implicit wait, use custom findElement() methods for custom behaviour
element = driver.findElement(foundBy);
// recursion, consider a conditioned loop instead
click();
}
}
// ... similar for other methods, too
}
Note that while I think that the foundBy info should be accessible from the generic WebElements to make this easier, Selenium developers consider it a mistake to try something like this and have chosen not to make this information public. It's arguably a bad practice to re-find on stale elements, because you're re-finding elements implicitly without any mechanism for checking whether it's justified. The re-finding mechanism could potentially find a completely different element and not the same one again. Also, it fails horribly with findElements() when there are many found elements (you either need to disallow re-finding on elements found by findElements(), or remember the how-manyeth your element was from the returned List).
I think it would be useful sometimes, but it's true that nobody would ever use options 1 and 2 which are obviously much better solutions for the robustness of your tests. Use them and only after you're sure you need this, go for it.
Use a task queue (that can rerun past tasks)
Implement your whole workflow in a new way!
Make a central queue of jobs to run. Make this queue remember past jobs.
Implement every needed task ("find an element and click it", "find an element and send keys to it" etc.) via the Command pattern way. When called, add the task to the central queue which will then (either synchronously or asynchronously, doesn't matter) run it.
Annotate every task with #LoadsNewPage, #Reversible etc. as needed.
Most of your tasks will handle their exceptions by themselves, they should be stand-alone.
When the queue would encounter a stale element exception, it would take the last task from the task history and re-run it to try again.
This would obviously take a lot of effort and if not thought through very well, could backfire soon. I used a (lot more complex and powerful) variant of this for resuming failed tests after I manually fixed the page they were on. Under some conditions (for example, on a StaleElementException), a fail would not end the test right away, but would wait (before finally time-outing after 15 seconds), popping up an informative window and giving the user an option to manually refresh the page / click the right button / fix the form / whatever. It would then re-run the failed task or even give a possibility to go some steps back in history (e.g. to the last #LoadsNewPage job).
Final nitpicks
All that said, your original solution could use some polishing. You could combine the two methods into one, more general (or at least make them delegate to this one to reduce code repetition):
WebElement getStaleElem(By by, WebDriver driver) {
try {
return driver.findElement(by);
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElem(by, driver);
} catch (NoSuchElementException ele) {
System.out.println("Attempting to recover from NoSuchElementException ...");
return getStaleElem(by, driver);
}
}
With Java 7, even a single multicatch block would be sufficient:
WebElement getStaleElem(By by, WebDriver driver) {
try {
return driver.findElement(by);
} catch (StaleElementReferenceException | NoSuchElementException e) {
System.out.println("Attempting to recover from " + e.getClass().getSimpleName() + "...");
return getStaleElem(by, driver);
}
}
This way, you can greatly reduce the amount of code you need to maintain.
I solve this by 1. keeping the stale element and poll it until it throws an exception, and then 2. wait until the element is visible again.
boolean isStillOnOldPage = true;
while (isStillOnOldPage) {
try {
theElement.getAttribute("whatever");
} catch (StaleElementReferenceException e) {
isStillOnOldPage = false;
}
}
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("theElementId")));
If you are trying to Click on link, that taking you to new page. After that navigating back and clicking on other links. They below code may help you.
public int getNumberOfElementsFound(By by) {
return driver.findElements(by).size();
}
public WebElement getElementWithIndex(By by, int pos) {
return driver.findElements(by).get(pos);
}
/**click on each link */
public void getLinks()throws Exception{
try {
List<WebElement> componentList = driver.findElements(By.tagName("a"));
System.out.println(componentList.size());
for (WebElement component : componentList)
{
//click1();
System.out.println(component.getAttribute("href"));
}
int numberOfElementsFound = getNumberOfElementsFound(By.tagName("a"));
for (int pos = 0; pos < numberOfElementsFound; pos++) {
if (getElementWithIndex(By.tagName("a"), pos).isDisplayed()){
getElementWithIndex(By.tagName("a"), pos).click();
Thread.sleep(200);
driver.navigate().back();
Thread.sleep(200);
}
}
}catch (Exception e){
System.out.println("error in getLinks "+e);
}
}
Solutions to resolve them:
Storing locators to your elements instead of references
driver = webdriver.Firefox();
driver.get("http://www.github.com");
search_input = lambda: driver.find_element_by_name('q');
search_input().send_keys('hello world\n');
time.sleep(5);
search_input().send_keys('hello frank\n') // no stale element exception
Leverage hooks in the JS libraries used
# Using Jquery queue to get animation queue length.
animationQueueIs = """
return $.queue( $("#%s")[0], "fx").length;
""" % element_id
wait_until(lambda: self.driver.execute_script(animationQueueIs)==0)
Moving your actions into JavaScript injection
self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
Proactively wait for the element to go stale
# Wait till the element goes stale, this means the list has updated
wait_until(lambda: is_element_stale(old_link_reference))
This solution, which worked for me
When a Stale Element Exception occurs!!
Stale element exception can happen when the libraries supporting those textboxes/ buttons/ links has changed which means the elements are same but the reference has now changed in the website without affecting the locators. Thus the reference which we stored in our cache including the library reference has now become old or stale because the page has been refreshed with updated libraries.
for(int j=0; j<5;j++)
try {
WebElement elementName=driver.findElement(By.xpath(“somexpath”));
break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying :: ” + e.getMessage());
}
elementName.sendKeys(“xyz”);
For Fitnesse you can use:
|start |Smart Web Driver| selenium.properties|
#Fixture(name = "Smart Web Driver")
public class SmartWebDriver extends SlimWebDriver {
private final static Logger LOG = LoggerFactory.getLogger(SmartWebDriver.class);
/**
* Constructs a new SmartWebDriver.
*/
#Start(name = "Start Smart Web Driver", arguments = {"configuration"}, example = "|start |Smart Web Driver| selenium.properties|")
public SmartWebDriver(String configuration) {
super(configuration);
}
/**
* Waits for an element to become invisible (meaning visible and width and height != 0).
*
* #param locator the locator to use to find the element.
*/
#Command(name = "smartWaitForNotVisible", arguments = {"locator"}, example = "|smartWaitForNotVisible; |//path/to/input (of css=, id=, name=, classname=, link=, partiallink=)|")
public boolean smartWaitForNotVisible(String locator) {
try {
waitForNotVisible(locator);
} catch (StaleElementReferenceException sere) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a StaleElementReferenceException occurred, trying to continue...", locator);
} catch (NoSuchElementException ele) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a NoSuchElementException occurred, trying to continue...", locator);
} catch (AssertionError ae) {
if (ae.getMessage().contains("No element found")) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a AssertionError occurred, trying to continue...", locator);
} else {
throw ae;
}
}
return true;
}
}
https://www.swtestacademy.com/selenium-wait-javascript-angular-ajax/ here is a good article about dynamic waiter strategies.
Your problem is not waiting properly all the ajax, jquery or angular calls.
Then you end up with StaleElementException.
If your approach is to use Try-Catch mechanism, I guess it has a flaw. You shouldn't rely on that structure as you'll never know it's gonna work in the catch clause.
Selenium gives you the opportunity to make javascript calls.
You can execute
"return jQuery.active==0"
return
angular.element(document).injector().get('$http').pendingRequests.length
=== 0"
"return document.readyState"
"return angular.element(document).injector() === undefined"
commands just to check the existence and states of those calls.
You can do that before any findBy operation so you always work with the latest page

Using webdriver to see if an element exists WITHOUT throwing an exception?

I am new to Selenium webdriver, maybe this question is obvious. I am after situation like this:
If the element exists, click it and go back to index page:
driver.findElement(By.id("...."])).click();
if doesn't exit, skip it and go back to index page. The test still goes on without any exception thrown.
I know one solution to this:
driver.findElements( By.id("...") ).size() != 0
so i tried:
if(driver.findElements(By.id("....")).size() > 0)
{
driver.findElement(By.id("....")).click();
driver.findElement(By.cssSelector("...")).click();
}
else
{
driver.findElement(By.cssSelector("....")).click();
}
This turned out really ugly though because if I have 10 elements to verify, this IF condition needs to be written 10 times.
Any workaround to make it neat?
There are ways to find elements without throwing exceptions by using try-catch conditions inside of loops. For example, this method I wrote (which can be simplified depending on what you use if for) will return a WebElement and it makes sure that it's clickable before returning it to you:
public static WebElement getElementByLocator( By locator ) {
driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
WebElement we = null;
boolean unfound = true;
int tries = 0;
while ( unfound && tries < 10 ) {
tries += 1;
try {
we = driver.findElement( locator );
unfound = false; // FOUND IT
} catch ( StaleElementReferenceException ser ) {
unfound = true;
} catch ( NoSuchElementException nse ) {
unfound = true;
} catch ( Exception e ) {
staticlogger.info( e.getMessage() );
}
}
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT,
TimeUnit.SECONDS );
return we;
}
Solution could be many but that may hinder your architecture.
So easiest solution could be as follows:
Just create a method like optionalClick() in some utility class or somewhere with the arguments as:
locator_keyword: {values : id or cssSelector or xpath etc}
locator : {values : "q" }
Steps in method:
Get element based on the locator_keyword and locator
Check if element is there and click it
Otherwise don't do anything
This method can be used as a generic kind of thing for any type of objects.
Thanks for the pointer on using findelements. I think you'll also need wait logic though if you dont want it to return too early.
here is my solution in c#.
It basically reimplements the wait logic in the selenium library - unfortunately the method that does the waiting isnt exposed (annoyingly it also rethrows exceptions the wrong way and loses the stack traces!).
You'd probably have to modify it a little for java - not sure what the selenium API is there when you cant just pass around functions.
Wait reimplementation:
private IWebElement WaitAndSeeIf(Func<IWebElement> canGet)
{
var end = DateTime.Now.AddSeconds(1);
IWebElement element;
while (true)
{
element = canGet();
if (element != null)
break;
var time = DateTime.Now;
if (time > end)
break;
Thread.Sleep(200);
}
return element;
}
calling code:
var dashboardButton = WaitAndSeeIf(() =>
{
var elements = Driver.FindElements(By.XPath("//button[contains(.//*,'Dashboard')]"));
return elements.Any() ? elements.Single() : null;
});
So far I've found this useful in a couple of places (checking for the presence of dialogs in extjs etc) but have yet had the need to muck around and make it configurable. I supppose the nicest thing to do would be to have it take the implicit wait time as mentioned in the other answer.

Wait for page load in Selenium

How do you make Selenium 2.0 wait for the page to load?
You can also check pageloaded using following code
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Use class WebDriverWait
Also see here
You can expect to show some element. something like in C#:
WebDriver _driver = new WebDriver();
WebDriverWait _wait = new WebDriverWait(_driver, new TimeSpan(0, 1, 0));
_wait.Until(d => d.FindElement(By.Id("Id_Your_UIElement")));
If you set the implicit wait of the driver, then call the findElement method on an element you expect to be on the loaded page, the WebDriver will poll for that element until it finds the element or reaches the time out value.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
source: implicit-waits
In general, with Selenium 2.0 the web driver should only return control to the calling code once it has determined that the page has loaded. If it does not, you can call waitforelemement, which cycles round calling findelement until it is found or times out (time out can be set).
Ruby implementation:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
#driver.execute_script("return document.readyState;") == "complete"
}
All of these solutions are OK for specific cases, but they suffer from at least one of a couple of possible problems:
They are not generic enough -- they want you to know, ahead of time, that some specific condition will be true of the page you are going to (eg some element will be displayed)
They are open to a race condition where you use an element that is actually present on the old page as well as the new page.
Here's my attempt at a generic solution that avoids this problem (in Python):
First, a generic "wait" function (use a WebDriverWait if you like, I find them ugly):
def wait_for(condition_function):
start_time = time.time()
while time.time() < start_time + 3:
if condition_function():
return True
else:
time.sleep(0.1)
raise Exception('Timeout waiting for {}'.format(condition_function.__name__))
Next, the solution relies on the fact that selenium records an (internal) id-number for all elements on a page, including the top-level <html> element. When a page refreshes or loads, it gets a new html element with a new ID.
So, assuming you want to click on a link with text "my link" for example:
old_page = browser.find_element_by_tag_name('html')
browser.find_element_by_link_text('my link').click()
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
For more Pythonic, reusable, generic helper, you can make a context manager:
from contextlib import contextmanager
#contextmanager
def wait_for_page_load(browser):
old_page = browser.find_element_by_tag_name('html')
yield
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
And then you can use it on pretty much any selenium interaction:
with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()
I reckon that's bulletproof! What do you think?
More info in a blog post about it here
You may remove the System.out line. It is added for debug purposes.
WebDriver driver_;
public void waitForPageLoad() {
Wait<WebDriver> wait = new WebDriverWait(driver_, 30);
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
System.out.println("Current Window State : "
+ String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")));
return String
.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"))
.equals("complete");
}
});
}
Here is a Java 8 version of the currently most upvoted answer:
WebDriverWait wait = new WebDriverWait(myDriver, Duration.ofSeconds(15));
wait.until(webDriver -> "complete".equals(((JavascriptExecutor) webDriver)
.executeScript("return document.readyState")));
Where myDriver is a WebDriver object (declared earlier).
Note: Be aware that this method (document.readyState) only checks the DOM.
You can also use the class: ExpectedConditions to explicitly wait for an element to show up on the webpage before you can take any action further actions
You can use the ExpectedConditions class to determine if an element is visible:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
See ExpectedConditions class Javadoc for list of all conditions you are able to check.
Imran's answer rehashed for Java 7:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState"
).equals("complete");
}
});
This seems to be a serious limitation of WebDriver. Obviously waiting for an element will not imply the page being loaded, in particular the DOM can be fully build (onready state) whereby JS is still executing and CSS and images are still loading.
I believe the simplest solution is to set a JS variable upon the onload event after everything is initialized and check and wait for this JS variable in Selenium.
If you want to wait for a specific element to load, you can use the isDisplayed() method on a RenderedWebElement :
// Sleep until the div we want is visible or 5 seconds is over
long end = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < end) {
// Browsers which render content (such as Firefox and IE) return "RenderedWebElements"
RenderedWebElement resultsDiv = (RenderedWebElement) driver.findElement(By.className("gac_m"));
// If results have been returned, the results are displayed in a drop down.
if (resultsDiv.isDisplayed()) {
break;
}
}
(Example from The 5 Minute Getting Started Guide)
Man all these answers require too much code. This should be a simple thing as its pretty common.
Why not just inject some simple Javascript with the webdriver and check.
This is the method I use in my webscraper class. The Javascript is pretty basic even if you don't know it.
def js_get_page_state(self):
"""
Javascript for getting document.readyState
:return: Pages state. See doc link below.
"""
ready_state = self.driver.execute_script('return document.readyState')
if ready_state == 'loading':
self.logger.info("Loading Page...")
elif ready_state == 'interactive':
self.logger.info("Page is interactive")
elif ready_state == 'complete':
self.logger.info("The page is fully loaded!")
return ready_state
More Info in "Document.readyState" of MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
Explicitly wait or conditional wait in this wait until given this condition.
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
This will wait for every web element for 60 seconds.
Use implicitly wait for wait of every element on page till that given time.
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
This will wait for every web element for 60 seconds.
I'm surprised that predicates weren't the first choice as you typically know what element(s) you will next interact with on the page you're waiting to load. My approach has always been to build out predicates/functions like waitForElementByID(String id) and waitForElemetVisibleByClass(String className), etc. and then use and reuse these wherever I need them, be it for a page load or page content change I'm waiting on.
For example,
In my test class:
driverWait.until(textIsPresent("expectedText");
In my test class parent:
protected Predicate<WebDriver> textIsPresent(String text){
final String t = text;
return new Predicate<WebDriver>(){
public boolean apply(WebDriver driver){
return isTextPresent(t);
}
};
}
protected boolean isTextPresent(String text){
return driver.getPageSource().contains(text);
}
Though this seems like a lot, it takes care of checking repeatedly for you
and the interval for how often to check can be set along with the ultimate
wait time before timing out. Also, you will reuse such methods.
In this example, the parent class defined and initiated the WebDriver driver and the WebDriverWait driverWait.
I hope this helps.
Use implicitly wait for wait of every element on page till given time.
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
this wait for every element on page for 30 sec.
Another wait is Explicitly wait or conditional wait in this wait until given condition.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
In id give static element id which is diffidently display on the page, as soon as page is load.
The best way to wait for page loads when using the Java bindings for WebDriver is to use the Page Object design pattern with PageFactory. This allows you to utilize the AjaxElementLocatorFactory which to put it simply acts as a global wait for all of your elements. It has limitations on elements such as drop-boxes or complex javascript transitions but it will drastically reduce the amount of code needed and speed up test times. A good example can be found in this blogpost. Basic understanding of Core Java is assumed.
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
Call below Function in your script , this will wait till page is not loaded using javascript
public static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
NodeJS Solution:
In Nodejs you can get it via promises...
If you write this code, you can be sure that the page is fully loaded when you get to the then...
driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));
If you write this code, you will navigate, and selenium will wait 3 seconds...
driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...
From Selenium Documentation (Nodejs):
this.get( url ) → Thenable<undefined>
Schedules a command to navigate to the given URL.
Returns a promise that will be resolved when the document has finished loading.
You can use the below existing method to set the pageLoadTimeout. In below example if the page is taking more than 20 seconds to load, then it will throw an exception of page reload:
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
/**
* Call this method before an event that will change the page.
*/
private void beforePageLoad() {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("document.mpPageReloaded='notYet';");
}
/**
* Call this method after an event that will change the page.
*
* #see #beforePageLoad
*
* Waits for the previous page to disappear.
*/
private void afterPageLoad() throws Exception {
(new WebDriverWait(driver, 10)).until(new Predicate<WebDriver>() {
#Override
public boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
Object obj = js.executeScript("return document.mpPageReloaded;");
if (obj == null) {
return true;
}
String str = (String) obj;
if (!str.equals("notYet")) {
return true;
}
return false;
}
});
}
You can change from the document to an element, in the case of where only part of a document is being changed.
This technique was inspired by the answer from sincebasic.
SeleniumWaiter:
import com.google.common.base.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
public class SeleniumWaiter {
private WebDriver driver;
public SeleniumWaiter(WebDriver driver) {
this.driver = driver;
}
public WebElement waitForMe(By locatorname, int timeout){
WebDriverWait wait = new WebDriverWait(driver, timeout);
return wait.until(SeleniumWaiter.presenceOfElementLocated(locatorname));
}
public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {
// TODO Auto-generated method stub
return new Function<WebDriver, WebElement>() {
#Override
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
};
}
}
And to you use it:
_waiter = new SeleniumWaiter(_driver);
try {
_waiter.waitForMe(By.xpath("//..."), 10);
}
catch (Exception e) {
// Error
}
You can explicitly wait for an element to show up on the webpage before you can take any action (like element.click()):
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>() {
#Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}
}
);
This is what I used for a similar scenario and it works fine.
My simple way:
long timeOut = 5000;
long end = System.currentTimeMillis() + timeOut;
while (System.currentTimeMillis() < end) {
if (String.valueOf(
((JavascriptExecutor) driver)
.executeScript("return document.readyState"))
.equals("complete")) {
break;
}
}
You can use this snippet of code for the page to load:
IWait wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver,TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Or you can use waiter for any element to be loaded and become visible/clickable on that page, most probably which is going to be load at the end of loading like:
Wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath(xpathOfElement));
var element = GlobalDriver.FindElement(By.XPath(xpathOfElement));
var isSucceededed = element != null;
The best way I've seen is to utilize the stalenessOf ExpectedCondition, to wait for the old page to become stale.
Example:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
It'll wait for ten seconds for the old HTML tag to become stale, and then throw an exception if it doesn't happen.
I use node + selenium-webdriver(which version is 3.5.0 now). what I do for this is:
var webdriver = require('selenium-webdriver'),
driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
return state === 'complete';
}))
You can use wait. there are basically 2 types of wait in selenium
Implicit wait
Explicit wait
- Implicit wait
This is very simple please see syntax below:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- Explicit wait
Explicitly wait or conditional wait in this wait until given condition is occurred.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
You can use other properties like visblityOf(), visblityOfElement()
If someone uses selenide:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
More Conditions can be found here:
http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
In my case , I used the following to know the page load status. In our application loading gif(s) are present and, I listen to them as follows to eliminate unwanted wait time in the script.
public static void processing(){
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
}
Where the xpath locates the gif in the HTML DOM.
After this, You may also implement your action methods Click.
public static void click(WebElement elementToBeClicked){
WebDriverWait wait = new WebDriverWait(driver, 45);
wait.until(ExpectedConditions.visibilityOf(element));
wait.until(ExpectedConditions.elementToBeClickable(element));
wait.ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class); elementToBeClicked.click();
}

Categories

Resources