Selenium: StaleElementReferenceException - java

I've been researching this error for a while and have tried many things and nothing seems to work...
while(!driver.findElements(By.className("next")).isEmpty()) {
//elements = driver.findElements(By.xpath("//a[#class='name']"));
elements = findDynamicElements("//a[#class='name']");
for (WebElement e : elements) {
userName = e.getText(); //<--EXCEPTION HERE
check_visitor_profile(userName);//<--WE LEAVE THE PAGE HERE
Thread.sleep(3000); //<--NO TRY/CATCH BLOCK FOR READABILITY
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
elements = findDynamicElements("//a[#class='name']");
}
driver.findElement(By.xpath("VisitsNext")).click();
}
protected List<WebElement> findDynamicElements(String path) {
List<WebElement> result;
String xPath = path;
new WebDriverWait(driver, 25).until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(xPath)));
//new WebDriverWait(driver, 25).until(elementIdentified(By.id(path)));
try {
result = driver.findElements(By.xpath(xPath));
return result;
}
catch(WebDriverException e) {
return null;
}
);
}
My code craps out on the first line of the for loop where userName is assigned. I've seen on this forum that you should use 'presenceOfElementLocated' and explicitly wait for the element to come back but that doesn't work either. I used 'presenceOfAllElementsLocatedBy' for a list but I have a method that uses 'presenceOfElementLocated' which doesn't work either.
I know stuff like the Thread.sleep and the implicitlyWait line is probably unnecessary at this point but I've literally tried everything and it doesn't work...
The error occurs because when I call 'check_visitor_profile' it leaves the page - when it comes back the elements are out of place so I have to find them again. Which I do but it still throws the exception.
Any Ideas?
Thanks.

The problem might occur because you are changing elements in the middle of the loop. It will cause you trouble even without the StaleElementReferenceException. Use a for loop instead of the for each loop
elements = findDynamicElements("//a[#class='name']");
int size = elements.size();
for (int i = 0 ; i < size ; ++i) {
elements = findDynamicElements("//a[#class='name']");
userName = elements.get(i).getText();
check_visitor_profile(userName);
}

Handle the exception explicitly as the element is no longer attached to the DOM or has changed at that moment you call "check_visitor_profile"
See the link below might help
catch(StateElementException e){
System.out.println("StaleElement dealt with since you successfully left page ");
}
http://docs.seleniumhq.org/exceptions/stale_element_reference.jsp

Related

Selenium Webdriver - how to apply explicit wait in table (in the for loop) using java

from the table each row collect the td[3] value, below is my java source code
WebElement biboSection = driver.findElement(By.xpath("//*#id='Label1']/div/table[2]/tbody"));
List<WebElement>rowsCount = biboSection.findElements(By.tagName("tr"));
for (int k =1;k<=rowsCount.size();k++){
String biblioTable = driver.findElement(By.xpath("//*#id='Label1']/div/table[2]/tbody/tr["+k+"]/td[3]")).getText().trim();
}
problem is if any one of the row td[3] tag not available, so its becomes failed, getting the below error
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"xpath","selector":"//*[#id='Label1']/div/table[2]/tbody/tr[5]/td[3]"}
In general - single element we can use explicit wait to avoid the above exception, but in table how can i continue with rest of the rows if particular tab not available (i.e if tr[5]/td[3] not available then move to next set tr[6]/td[3])
What you need to do is, you need to return a value when you get a NoSuchElementException. The risk of this is, you can get a false positive, so you need to make sure you do something with the exception.
What you could do is, you could take the string biblioTable elsewhere, allowing you to check for the element first. Try the following (I'm typing this without IDE, so excuse any mistakes etc):
var el;
String exception;
for (int k =1;k<=rowsCount.size();k++){
try
{
el = driver.findElement(By.xpath("//*#id='Label1']/div/table[2]/tbody/tr["+k+"]/td[3]"));
return el;
}
catch (NoSuchElementException)
{
exception = "Whatever you want"
return exception;
}
if(exception == "Whatever you want")
{
Console.Writeline.....
}
else{
String biblioTable = el.getText().trim();
}
}
So what you are doing is:
First you are creating a var and a string which you are going to return in your try/catch. Then in the try/catch, you are first going to try to return the webelement el. If you get a NoSuchElementException, you are going to return the string exception with a value, which you are going to use as a condition to create the string biblioTable. If its filled, it means that the webelement el was not there. So you can do whatever else you want and end the loop. Else, you can create the string biblioTable.
First, I think you have a typo in two places: your xpath is missing the open square bracket "[" after "//*"
Secondly, I think you can accomplish what you want with one declaration:
List<WebELement> biblioTable = driver.findElements(By.xpath("//*[#id='Label1']/div/table[2]/tbody/tr/td[3]"));
Then you can access the text elements via:
for (WebElement text : biblioTable)
String name = text.getText();

Selenium WebDriver if else statement

first time poster, long time user reaping the benefits of all these great questions. But I need your help.
What I'm trying to do below is
Navigate to a page
Find all the particular links
Click on the first link
Check to see if an element is displayed, if it is displayed then navigate back to the previous page and click on the next link of the list. If it is NOT displayed then exit out of the method and continue the test script. This is the part where I'm stuck.
The if statement executes as desired whereby if it finds the element then it navigates back to the previous. But where it fails is when it clicks on the second link of the page. It searches for that element even though that element does not exist in that page and does not exit out of the method even though I've explicitly stated return.
I'm having a brain fart and tried all the possible combinations and permuatations I can think of. If there's anyone out there that can help me I'd greatly appreciate the help.
EDIT
Let me edit to clarify my thoughts. I need my method to exit out of the method once inactive.isDisplayed() returns false. But when it navigates to the second page, it continually tries to find the element then eventually fails with a NoSuchElementException. I know the element doesn't exist, that's why I need it to exit out of the method and perform the next step of the test script. I hope this clarify my situation. It's not really a Selenium WebDriver question as it is a java question.
Thanks
public void checkErrors() {
List<WebElement> videos =driver.findElements(By.cssSelector(".row-
title"));
for (int i = 0; i < videos.size(); i++) {
videos = driver.findElements(By.cssSelector(".row-title"));
videos.get(i).click();
if (inactive().isDisplayed() != false) {
driver.navigate().back();
} else {
return;
}
}
return;
}
EDIT:
private WebElement inactive() {
inactive =
driver.findElement(By.cssSelector("#message>p>strong"));
highlightElement(inactive);
return inactive;
}
You might want to check the presence of the message before checking if it's displayed:
public void checkErrors() {
for(int i = 0; ; i++) {
// handle next link
List<WebElement> videos = driver.findElements(By.cssSelector(".row-title"));
if (i >= videos.size())
return;
// click the next link
WebElement video = videos.get(i);
video.click();
// return if the message is missing or hidden
List<WebElement> messages = driver.findElements(By.cssSelector("#message>p>strong"));
if (messages.size() == 0 || !messages.get(0).isDisplayed())
return;
driver.navigate().back();
}
}
Small recommendation to help you here:
As you're not using the WebElement returned by inactive() after you've checked if it's displayed or not, you might as well move the logic for checking whether its displayed to inactive() and return the value of isDisplayed(). For example:
private boolean inactive() {
try {
inactive = driver.findElement(By.cssSelector("#message>p>strong"));
highlightElement(inactive);
return inactive.isDisplayed(); // Element is present in the DOM but may not visible due to CSS etc.
} catch (NoSuchElementException e) {
return false; // Element is not present in the DOM, therefore can't be visible.
}
}

make wait function do something on and on if it fails

I am very beginner with Selenium and Java to write tests.
I know that I can use the code below to try to click on a web element twice (or as many time as I want):
for(int i=0;i<2;i++){
try{
wait.until(wait.until(ExpectedConditions.visibilityOfElementLocated
(By.xpath("//button[text()='bla bla ..']"))).click();
break;
}catch(Exception e){ }
}
but i was wondering if there is anything like passing a veriable to the wait function to make it do it ith times itself, something like:
wait.until(wait.until(ExpectedConditions.visibilityOfElementLocated
(By.xpath("//button[text()='bla bla ..']"),2)).click();
For example in here 2 may mean that try to do it two times if it fails, do we have such a thing?
Take a look at FluentWait, I think this will cover your use case specifying appropriate timeout and polling interval.
https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/ui/FluentWait.html
If you can't find something in the set of ExpectedConditions that does what you are wanting you can always write your own.
The WebDriverWait.until method can be passed either a com.google.common.base.Function or com.google.common.base.Predicate. If you create your own Function implementation then it's good to note that any non-null value will end the wait condition. For Predicate the apply method simply needs to return true.
Armed with that I do believe there's very little you can't do with this API. The feature you're asking about probably does not exist out of the box, but you have full capability to create it.
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Function.html
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Predicate.html
Best of Luck.
Untested Snippet
final By locator = By.xpath("");
Predicate<WebDriver> loopTest = new Predicate<WebDriver>(){
#Override
public boolean apply(WebDriver t) {
int tryCount = 0;
WebElement element = null;
while (tryCount < 2) {
tryCount++;
try {
element = ExpectedConditions.visibilityOfElementLocated(locator).apply(t);
//If we get this far then the element resolved. Break loop.
break;
} catch (org.openqa.selenium.TimeoutException timeout) {
//FIXME LOG IT
}
}
return element != null;
}
};
WebDriverWait wait;
wait.until(loopTest);

Selenium exception handling, problems

I have problems with Java Selenium. I use it to automate testing web page, which structure is very complicated - a lot of elements are loaded dynamically, there is a lot of unnecessary elements in html pages. It's very difficult to make my tests reliable. Sometimes page can't load or I try to click on the button which doesn't exist yet (in similiar method of course).
So, I wrote Util class with methods like this one:
public static void findAndSendKeys(String vToSet, By vLocator) {
log.info("findAndSendKeys " + vLocator.toString());
int attempts = 0;
while (attempts < ATTEMPTS) {
WebElement element = null;
try {
element = webDriverWait.until(ExpectedConditions.presenceOfElementLocated(vLocator));
element.sendKeys(vToSet);
break;
} catch (TimeoutException e) {
log.error("timeOut exception " + e.getMessage());
} catch (StaleElementReferenceException e) {
log.error("StaleElementReference exception " + e.getMessage());
} catch (UnhandledAlertException e) {
log.error("UnhandledAlert exception " + e.getMessage());
Alert alert = driver.switchTo().alert();
alert.accept();
}
attempts++;
}
}
I know it looks terrible, I didn't refactor it yet, but method usually works fine for most cases - on the second or third loop input field is filled.
Firstly I was using only sendKeys with exception handling, but I noticed that although input field exists, StaleElementReferenceException is thrown, so I put while() in this static method and try to sendKeys again.
Sometimes my webPage shows Alert that is just validation and after catching exception and ignoring alert I can continue work.
I wonder.. It could be easier if there would exist method similiar to "Pause 1000" method in Selenium IDE. Sometimes my web page works fast and good, sometimes page loading process is very long and I have to wait.
There is also problems with while() loop. I don't know what to do if while loop ends and nothing is send - for example loaded page/container is blank, dynamic loading fails, so there is no chance to find our input field
Automate testing process for this web page causes me a headache. Please, be placable, I don't have technical support and I am on my own.
In my opinion, you are trying to overengineer the whole thing. I think you are trying to write a single function that will handle all cases and I don't think that is a good approach. Each case is potentially different and you will need to understand each case and handle it appropriately.
A few tips.
Don't loop a wait.until(). Instead of
for (int i = 0; i < 5; i++)
{
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(...);
}
do
WebDriverWait wait = new WebDriverWait(driver, 50);
wait.until(...);
The end result is the same (max 50s wait) but the 2nd code is less confusing. Read up on waits to better understand them. The default behavior for a wait is to wait up to the max time and poll the condition every 500ms.
If you are getting StaleElementReferenceException, you are missing something. I don't mean that to sound critical... it happens. You need to understand how the HTML of the page is changing because you are looking for an element that is disappearing and then you try to scrape it. You will need to change either the element you are looking for or when you are looking for it.
Don't use Thread.sleep(). In general it's a bad practice because it's hard coded. Depending on the environment, it may be too long or not long enough of a wait. Instead prefer the wait.until() mechanism you are already using. The hard part, at times, is finding what to wait for.
If you are trying to scrape the page after the items are sorted, then you need to determine what changes after sorting occurs. Maybe there's an arrow on the column header that appears to indicate a sort direction (ascending vs descending)? One thing you could do is to grab one of the TD elements, wait for it to go stale (while the elements are being sorted), and then regrab all elements?
Maybe something like this
WebDriverWait wait = new WebDriverWait(driver, 10);
// grab the first TD as a stale reference
WebElement td = driver.findElement(By.tagName("td"));
// wait for it to go stale
wait.until(ExpectedConditions.stalenessOf(td));
// now grab them all and do something with them
List<WebElement> tds = driver.findElements(By.tagName("td"));
Without a link to the page or a lot more explanation of what is happening on the page and some relevant HTML, I'm not sure what else we can provide.
This code works for me:
jsPageLoadWait ="
try {
if (document.readyState !== 'complete') {
return false; // Page not loaded yet
}
if (window.jQuery) {
if (window.jQuery.active) {
return false;
} else if (window.jQuery.ajax && window.jQuery.ajax.active) {
return false;
} else if ($(':animated').length != 0) {
return false;
}
}
if (window.angular) {
if (!window.qa) {
// Used to track the render cycle finish after loading is complete
window.qa = {
doneRendering: false
};
}
// Get the angular injector for this app (change element if necessary)
var injector = window.angular.element('body').injector();
// Store providers to use for these checks
var $rootScope = injector.get('$rootScope');
var $http = injector.get('$http');
var $timeout = injector.get('$timeout');
// Check if digest
if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {
window.qa.doneRendering = false;
return false; // Angular digesting or loading data
}
if (!window.qa.doneRendering) {
// Set timeout to mark angular rendering as finished
$timeout(function() {
window.qa.doneRendering = true;
}, 0);
return false;
}
}
return true;
} catch (ex) {
return false;
}"
public static Boolean WaitLoad(this ISearchContext context, UInt32 timeoutInMilliseconds = 10000, UInt32 millisecondPolling = 1000)
{
Boolean waitReadyStateComplete;
var wait = new DefaultWait<ISearchContext>(context);
wait.Timeout = TimeSpan.FromMilliseconds(timeoutInMilliseconds);
wait.PollingInterval = TimeSpan.FromMilliseconds(millisecondPolling);
wait.IgnoreExceptionTypes(typeof(NoSuchElementException), typeof(StaleElementReferenceException));
waitReadyStateComplete = wait.Until<Boolean>(ctx =>
{
if ((Boolean)((IJavaScriptExecutor)context).ExecuteScript(jsPageLoadWait))
return true;
else
return false;
});
return waitReadyStateComplete;
}

Selenium 2.0 WebDriver: Element is no longer attached to the DOM error using Java

I'm using the PageObject/PageFactory design pattern for my UI automation. Using Selenium 2.0 WebDriver, JAVA, I randomly get the error: org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM, when I attempt logic like this:
#FindBy(how = HOW.ID, using = "item")
private List<WebElement> items
private void getItemThroughName(String name) {
wait(items);
for(int i = 0; i < items.size(); i++) {
try {
Thread.sleep(0500);
} catch (InterruptedException e) { }
this.wait(items);
if(items.get(i).getText().contains(name)) {
System.out.println("Found");
break;
}
}
}
The error randomly happens at the if statement line, as you can see I've tried a couple things to avoid this, like sleeping a small amount of time, or waiting for the element again, neither works 100% of the time
First if you really have multiple elements on the by with the ID of "item" you should log a bug or talk to the developers on the site to fix that. An ID is meant to be unique.
As comments on the question already implied you should use an ExplicitWait in this case:
private void getItemThroughName(String name) {
new WebDriverWait(driver, 30)
.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("id('item')[.='" + name + "']")
));
// A timeout exception will be thrown otherwise
System.out.println("Found");
}

Categories