Stale Element Exception Java Selenium - java

I am trying to crawl trip advisor reviews. Some reviews have the more link, which displays the entire text of the review when clicked. However, when I imitate the click using Selenium, the DOM changes, which gives me the Stale Element Exception. I am trying to work around it by getting the DOM again, but the error still exists. Where am I going wrong?
List reviews = driver.findElements(By.className("review"));
for (int x = 0; x < reviews.size(); x++) {
WebElement element = driver.findElements(By.className("review")).get(x);
try {
if (element.findElements(By.className("expandLink")).size() > 0)
if (element.findElement(By.className("expandLink")).findElement(By.className("ulBlueLinks")).getText().startsWith("More")) {
element.findElement(By.className("ulBlueLinks")).click();
if (driver.findElements(By.className("ui_overlay")).size() > 0)
driver.findElement(By.className("ui_overlay")).findElement(By.className("ui_close_x")).click();
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
while (driver.findElements(By.className("ui_close_x")).size() > 0)
driver.findElement(By.className("ui_close_x")).click();
element = driver.findElements(By.className("review")).get(x);
String rating = element.findElement(By.className("ui_bubble_rating")).getAttribute("class").split(" ")[1].replaceAll("bubble_", "");
String review = element.findElement(By.className("entry")).getAttribute("innerHTML").replaceAll("<[^>]*>", "").replaceAll("[,\n]", " ");

I faced same thing, this is due to sometimes problems with DOM elements which are not reachable by selenium causing exception.
I come with this solution and it worked for me.
http://darrellgrainger.blogspot.in/2012/06/staleelementexception.html

Have you considered using the #FindBy annotation for declaring WebElements? Using this annotation, each time you make a call to a WebElement the driver searches the DOM for that WebElement.
This methodology means you only need to declare each element once and a reference to the WebElement is stored. It also allows you to make use of the Page Object Pattern, which will give you clean separation between page and test logic.
Here is a write up on it:
https://www.toptal.com/selenium/test-automation-in-selenium-using-page-object-model-and-page-factory

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, ensure that all the checkbox are checked

I'm using Selenium to test action on a html page. The one i'm working on contains multiple list of check box. My selenium script do the following actions :
-Click on the dropdown list to display the check box list
-Click on all the check box
-Click on the dropdown list to close the check box list
-Repeat for the next list
The problem is that somtimes the script goes too fast for the browser and some box are not checked, it happens really often since i make multiple test
To resolve my problem i tried to make a method that check if the
checkbox is checked, if not then i click again but it's not working and it increases the time of my test. Here my code so far :
public void clickClearanceListBox(int numberInList) throws InterruptedException {
int iteration = countTheNumberOfElement("//div[5]/div["+numberInList+"]/div[2]");
for(int i = 1; i <= iteration; i++) {
String xpathBox ="//div["+numberInList+"]/div[2]/div["+i+"]/div/div/label/span/span[2]";//xpath de la checkbox
String xpathInput = "//div["+numberInList+"]/div[2]/div["+i+"]/div/div/label/input";
clickTheDOMbyJs(xpathBox);
while(!checkBoxChecked(xpathInput)) {
Thread.sleep(200);
clickTheDOMbyJs(xpathBox);
}
}
}
Or :
public void clickClearanceListBox(int numberInList) throws InterruptedException {
int iteration = countTheNumberOfElement("//div[5]/div["+numberInList+"]/div[2]");
for(int i = 1; i <= iteration; i++) {
String xpathBox ="//div["+numberInList+"]/div[2]/div["+i+"]/div/div/label/span/span[2]";//xpath de la checkbox
String xpathInput = "//div["+numberInList+"]/div[2]/div["+i+"]/div/div/label/input";
clickTheDOMbyJs(xpathBox);
while(!checkBoxChecked(xpathInput)) {
Thread.sleep(200);
}
}
}
It looks like you need to use WebDriverWait in here. First, wait for the dropdown list to load completely, then wait for the checkbox to be visible.
A word of warning to you though. These xpaths are likely to be very brittle, and your tests will break quite easily. You should consider finding these various elements using ids, classes, or other attributes.
I manage to do it by checking if the attribute "value" of my checkbox has the value "true". While the attribute value is false, it means that the checkbox is still not checked for the browser so i wait... This extremly slow but at least it's working everytime. After the script clicked on the checkbox i do this :
while(!getAnElementAttribute(xpathInput, "value").equals("true")) {
}

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.
}
}

Selenium: StaleElementReferenceException

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

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