Selenium Java Chrome: Stale exception - java

Here is a code fragment:
System.setProperty(Constants.WEBDRIVER_CHROME_DRIVER_PROP, Constants.WEBDRIVER_CHROME_DRIVER_PATH);
m_chromeWebdriver = new ChromeDriver();
m_chromeWebdriver.get("mysite.org");
WebElement arrowElement = m_chromeWebdriver.findElement(By.cssSelector(_ARROW_NEXT_DAY));
arrowElement.click();
WebElement elmMainTable = m_chromeWebdriver.findElement(By.className("table-main"));
List<WebElement> allRows = elmMainTable.findElements(By.tagName("tr"));
for (WebElement row : allRows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
for (WebElement cell : cells) {
System.out.println(cell.getText());
}
}
m_chromeWebdriver.quit();
At the last line I get an
"stale element reference: element is not attached to the page
document"
exception.
Why and how can I solve that?
I use Chromdriver 2.2.9.

Well, as this is probably not the perfect solution - at least it worked for me...
I put all the relevant code into a method like this:
private static void handleTable() {
for (int i = 1; i < 5; i++) {
try {
WebElement elmMainTable = m_chromeWebdriver.findElement(By.className("table-main"));
List<WebElement> allRows = elmMainTable.findElements(By.cssSelector(".table-main tr"));
for (WebElement row : allRows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
for (WebElement cell : cells) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
} catch (StaleElementReferenceException e) {
//e.printStackTrace();
handleTable();
}
return;
}
}
And it worked! You can change the value 5 to which ever you'd like of course.

I learned the main reason for Selenium stale exception is element changed in DOM. In case, the stale exception are caused by those web elements you are not handling, you can try below try-catch and continue method in JAVA.
//get webelement list of button on the OU tab
List<WebElement> ouList =
Logon.wDriver.findElements(By.tagName(PagePropertise.tagButton));
// it should be 6
System.out.println("List Size is: "+ouList.size());
for (int i=0; i<ouList.size(); i++ ) {
//get each button element to verify with input Org name
WebElement ouName= ouList.get(i);
String ouLabel = null;
try {
ouLabel = ouName.getText();
} catch (StaleElementReferenceException e) {
//handle in exception catch, just skip invalid element and continue for to handle rest of loop
System.out.println("WebElement "+i+" = text "+ ouLabel);
continue;
}
System.out.println("WebElement "+i+" = text "+ ouLabel);
//webelement processing
...
//for some reason some of webelements in the OUlist were changed for above processing code.
}

Related

Issue in clicking on every element and performing action

I have a webtable which has multiple rows. I want to create a loop such that it traverses all the rows of this table and click on column 1 elements of first row, then check inside that an Edit button exists or not. Then come back and click on next element and check the edit button. Then come back and repeat till all rows are traversed.
But in current code implemented by me, it is just traversing the first row and then exiting. Could someone help me with same.
viewDiscussionScope(driver, scope);
WebElement paginationLabel = WaitUtils.waitForElement(driver, By.cssSelector(".v-csslayout-cvr-c-pagination__header"));
if(paginationLabel.isDisplayed())
{
WebElement table = WaitUtils.waitForElement(driver, By.cssSelector("table.eds-o-table.cvr-c-table--list tbody"));
List<WebElement> rows = table.findElements(By.cssSelector("tr.eds-o-table__row"));
for(WebElement row: rows)
{
List<WebElement> tableCols = row.findElements(By.cssSelector("td.eds-o-table__cell:nth-of-type(1)"));
for(WebElement col : tableCols)
{
col.findElement(By.cssSelector(".v-label-eds-c-text--bold")).click();
WebElement messageField = WaitUtils.waitForElement(driver, By.cssSelector(".eds-o-media__body-eds-o-media__body--top .v-label-eds-u-flexitext.v-label-undef-w:nth-of-type(1)"));
String messageText = messageField.getText();
boolean editLabel = (driver.findElement(By.cssSelector(".eds-c-button-set-eds-c-button-set--align-right .v-button-eds-s-is-first")).getText()).equals("Edit");
if(!(editLabel))
{
LOG.info(messageText+" is not editable by the logged in user");
}
else
{
LOG.info(messageText+" is editable by the logged in user");
}
break;
}
break;
}
}
}
Remove break statement from your parent for loop!
for(WebElement row: rows)
{
List<WebElement> tableCols = row.findElements(By.cssSelector("td.eds-o-table__cell:nth-of-type(1)"));
for(WebElement col : tableCols)
{
// your code
}
// REMOVE => break;
}
}
You have 2 break that need to be removed and you don't need to loop through the td if you want to click only first td
if (paginationLabel.isDisplayed())
{
WebElement table = WaitUtils.waitForElement(driver, By.cssSelector("table.eds-o-table.cvr-c-table--list tbody"));
List<WebElement> rows = table.findElements(By.cssSelector("tr.eds-o-table__row"));
for (WebElement row : rows)
{
// get first td
WebElement tableCol = row.findElement(By.cssSelector("td.eds-o-table__cell:nth-of-type(1)"));
tableCol.findElement(By.cssSelector(".v-label-eds-c-text--bold")).click();
WebElement messageField = WaitUtils.waitForElement(driver, By.cssSelector(".eds-o-media__body-eds-o-media__body--top .v-label-eds-u-flexitext.v-label-undef-w:nth-of-type(1)"));
String messageText = messageField.getText();
boolean editLabel = (driver.findElement(By.cssSelector(".eds-c-button-set-eds-c-button-set--align-right .v-button-eds-s-is-first")).getText()).equals("Edit");
if (!(editLabel))
{
LOG.info(messageText + " is not editable by the logged in user");
}
else
{
LOG.info(messageText + " is editable by the logged in user");
}
}
}
first you need to find where is edit button located in table you can find its xpath, does of all edit buttons having same xpath if yes then your problem will be solved in you just need to find by findElements() method and store it into List and to get value of edit buttons or to check if is available or not you can call .get() method which will get all respected buttons of same xpath and by specifying index as .get(0),.get(1) or pass it in loop you get his value by .getText() method and check or perform actions on that
If you could provide the DOM of the table you trying to work on, i can help you better.
If i understood correctly your problem here is the solution, also, i strongly advice to use methods for small tasks, which will help you in the long run. Also, the selectors needs a better wraping, also the explicit wait needs a wrapper with his own methods.
try this:
if (paginationLabel.isDisplayed())
{
WebElement table = WaitUtils.waitForElement(driver, By.cssSelector("table.eds-o-table.cvr-c-table--list tbody"));
List<WebElement> rows = table.findElements(By.cssSelector("tr.eds-o-table__row"));
for (int i=1; i<=rows.size; i++)
{
// get first td
clickColumn(int i);
getMessageText(int i);
boolean editLabel = (driver.findElement(By.cssSelector(".eds-c-button-set-eds-c-button-set--align-right .v-button-eds-s-is-first")).getText()).equals("Edit");
if (!(editLabel))
{
LOG.info(messageText + " is not editable by the logged in user");
}
else
{
LOG.info(messageText + " is editable by the logged in user");
}
}
}
private clickColumn(int position) {
WebElement tableCol = row.findElement(By.cssSelector("td.eds-o-table__cell:nth-of-type(" + position + ")"));
tableCol.findElement(By.cssSelector(".v-label-eds-c-text--bold")).click();
}
private getMessageText(int position) {
WebElement messageField = WaitUtils.waitForElement(driver, By.cssSelector(".eds-o-media__body-eds-o-media__body--top .v-label-eds-u-flexitext.v-label-undef-w:nth-of-type(" + position + ")"));
String messageText = messageField.getText();
}

Nesting dropdowns using for loop is giving stale element reference error

I want to know how to nest dropdowns using selenium webdriver using java,i.e., I have 2 dropdowns and can these dropdowns be nested one after the other?
After looping 2 times for a dropdown it is showing stale element reference error
I have written the following code:
Select drpdwns6 = new Select(driver.findElement(By.xpath("//*[#id=\"MainContent_ddlBillable\"]")));
List <WebElement> sels6 = drpdwns6.getOptions();
sels6.size();
for(int s6=0;s6<sels6.size();s6++) {
drpdwns6.selectByIndex(s6);
System.out.println("selected value"+s6);
Select drpdwns7 = new Select(driver.findElement(By.xpath("//*[#id=\"MainContent_ddlofflinestatus\"]")));
List <WebElement> sels7 = drpdwns7.getOptions();
sels7.size();
for(int s7=0;s7<sels7.size();s7++) {
drpdwns7.selectByIndex(s7);
System.out.println("selected value"+s7);
}
}
My guess is selecting the option from the dropdown refreshes the DOM, so the exception is thrown. You need to relocate the dropdown in each itreation
Select drpdwns6 = new Select(driver.findElement(By.id("MainContent_ddlBillable")));
int drpdwns6Size = drpdwns6.getOptions().size();
for(int s6 = 0 ; s6 < drpdwns6Size ; s6++) {
drpdwns6 = new Select(driver.findElement(By.id("MainContent_ddlBillable")));
drpdwns6.selectByIndex(s6);
System.out.println("selected value"+s6);
Select drpdwns7 = new Select(driver.findElement(By.id("MainContent_ddlofflinestatus")));
int drpdwns7Size = drpdwns7.getOptions().size();
for(int s7 = 0 ; drpdwns7Size ; s7++) {
drpdwns7 = new Select(driver.findElement(By.id("MainContent_ddlofflinestatus")));
drpdwns7.selectByIndex(s7);
System.out.println("selected value"+s7);
}
}
As a side note, if you have an id use By.id it instead of By.xpath
You get the Stale element exception whenever the element present in the DOM is deleted or removed or is not available.
The above answer (ie) relocating the element once the DOM is refreshed or you could use Webdriver wait, If an element is not attached to DOM then you could try using ‘try-catch block’ within ‘for loop’ like below
driver.manage().timeouts().implicitlywait(30,TimeUnit.SECONDS);
try{
Select drpdwns6 = new
Select(driver.findElementByXpath("//[#id=\"MainContent_ddlBillable\"]")));
List <WebElement> sels6AllOptions = drpdwns6.getOptions();
int count1=sels6AllOptions.size();
for(int s6=0;s6<count1;s6++)
{
drpdwns6.selectByIndex(s6);
}
}
catch(StaleElementException e1){
System.out.println("selected value"+s6);
}
try{
Select drpdwns7 = new Select(driver.findElement(By.xpath("//*[#id=\"MainContent_ddlofflinestatus\"]")));
List <WebElement> sels7AllOptions = drpdwns7.getOptions();
int count2=sels7AllOptions.size();
for(int s7=0;s7<count2;s7++) {
drpdwns7.selectByIndex(s7);
catch(StaleElementException e2){
System.out.println("selected value"+s7);
}
}

How to make control on Foreach loop while executing it with Selenium driver?

I try to retrieve text via foreach loop,as according to page wise. Flow is : It prints text of single row and as soon as it completes, it goes to second page and start again to retrieve text. Problem is, it retrieves data of first page multiple times like sometimes 2 or 3 or 4 times, How to control it for single time execution ?
if (driver.findElement(By.xpath("//button[#ng-click='currentPage=currentPage+1']")).isEnabled()) {
int ilength = driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']")).size();
Outer: for (int i1 = ilength; i1 > 0;) {
List<WebElement> findData = driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']"));
for (WebElement webElement : findData) {
String printGroupName = webElement.getAttribute("value").toString();
System.out.println(printGroupName);
ilength--;
}
if (driver.findElement(By.xpath("//button[#ng-click='currentPage=currentPage+1']")).isEnabled()) {
action.moveToElement(driver.findElement(By.xpath("//button[#ng-click='currentPage=currentPage+1']"))).click().perform();
page.pagecallingUtility();
ilength = driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']")).size();
} else {
break Outer;
}
}
} else {
List<WebElement> findAllGroupName = driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']"));
for (WebElement webElement : findAllGroupName) {
String printGroupName = webElement.getAttribute("value").toString();
System.out.println(printGroupName);
}
}
Console Data, on which it retrieve information
Your loop can be simplified as below.
boolean newPageOpened = true;
while (newPageOpened) {
List<WebElement> findData = driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']"));
for (WebElement webElement : findData) {
if (webElement.isDisplayed()) {
String printGroupName = webElement.getAttribute("value").toString();
System.out.println(printGroupName);
}
}
WebElement nextButton = driver.findElement(By.xpath("//button[#ng-click='currentPage=currentPage+1']"));
if (nextButton.isEnabled()) {
action.moveToElement(nextButton).click().perform();
page.pagecallingUtility();
} else {
newPageOpened = false;
}
}
As for the contents of the fist page printing again and again, I suspect when you open the second page the contents of the first page are simply hidden in the page. So when you use driver.findElements(By.xpath("//input[#ng-attr-id='{{item.attr}}']")) the hidden first page elements are also found. The simple solution is to check if the element is displayed before printing it.

Stale Object Reference while Navigation using Selenium

I have been trying a simple program that navigates and fetches data from the new page, comes back in history and open other page and fetch data and so on until all the links have been visited and data is fetched.
After getting results on the below site, i am trying to loop through all the links i get in the first column and open those links one by one and extract text from each of these page. But the below program only visits first link and gives StaleElementReferenceException, I have tried using Actions but it didn't work and I am not aware about JavascriptExecutor. I also tried solutions posted on other SO questions, one of which was mine over here. I would like to have the mistake corrected in the below code and a working code.
public class Selenium {
private final static String CHROME_DRIVER = "C:\\Selenium\\chromedriver\\chromedriver.exe";
private static WebDriver driver = null;
private static WebDriverWait wait = null;
private void setConnection() {
try {
System.setProperty("webdriver.chrome.driver", CHROME_DRIVER);
driver = ChromeDriver.class.newInstance();
wait = new WebDriverWait(driver, 5);
driver.get("https://sanctionssearch.ofac.treas.gov");
this.search();
} catch (Exception e) {
e.printStackTrace();
}
}
private void search() {
try {
driver.findElement(By.id("ctl00_MainContent_txtLastName")).sendKeys("Dawood");
driver.findElement(By.id("ctl00_MainContent_btnSearch")).click();
this.extractText();
} catch (Exception e) {
e.printStackTrace();
}
}
private void extractText() {
try {
List<WebElement> rows = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr"));
List<WebElement> links = null;
for (int i = 1; i <= rows.size(); i++) {
links = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr/td[1]/a"));
for (int j = 0; j < links.size(); j++) {
System.out.println(links.get(j).getText() + ", ");
links.get(j).click();
System.out.println("Afte click");
driver.findElement(By.id("ctl00_MainContent_btnBack")).click();
this.search();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] ar) {
Selenium object = new Selenium();
object.setConnection();
}
}
Generally we will be getting the Stale Exception if the element attributes or something is changed after initiating the webelement. For example, in some cases if user tries to click on the same element on the same page but after page refresh, gets staleelement exception.
To overcome this, we can create the fresh webelement in case if the page is changed or refreshed. Below code can give you some idea.
Example:
webElement element = driver.findElement(by.xpath("//*[#id='StackOverflow']"));
element.click();
//page is refreshed
element.click();//This will obviously throw stale exception
To overcome this, we can store the xpath in some string and use it create a fresh webelement as we go.
String xpath = "//*[#id='StackOverflow']";
driver.findElement(by.xpath(xpath)).click();
//page has been refreshed. Now create a new element and work on it
driver.fineElement(by.xpath(xpath)).click(); //This works
In this case, we are collecting a group of webelements and iterating to get the text. But it seems there is some changes in the webelement after collecting the webelements and gettext throws staleness. We can use a loop and create the element on the go and get text.
for(int i = 0; i<5; i++)
{
String value = driver.findElement(by.xpath("//.....["+i+"]")).getText);
System.out.println(value);
}
Hope this helps you. Thanks.
The reason you get StaleElementReference Exception, is normally because you stored element(s) into some variable, however after that you did some action and page has changed (due to some ajax response) and so your stored element has become stale.
The best solution is not to store element in any variable in such case.
This should work.
links = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr/td[1]/a"));
for (int j = 0; j < links.size(); j++) {
System.out.println(links.get(j).getText() + ", ");
driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr/td[1]/a")).get(j).click();
System.out.println("Afte click");
driver.findElement(By.id("ctl00_MainContent_btnBack")).click();
this.search();
}
Please check this code
private void extractText() {
try {
List<WebElement> rows = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr"));
List<WebElement> links = null;
System.out.println(rows.size());
for (int i = 0; i < rows.size(); i++) {
links = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr/td[1]/a"));
WebElement ele= links.get(0);
System.out.println(ele.getText() + ", ");
ele.click();
System.out.println("After click");
driver.findElement(By.id("ctl00_MainContent_btnBack")).click();
}
} catch (Exception e) {
e.printStackTrace();
}
}

How to fetch all links and click those links one by one using Selenium WebDriver

I am using Selenium WebDriver with java.
I am fetching all links from webpage and trying to click each link one by one. I am getting below error:
error org.openqa.selenium.StaleElementReferenceException: Element not found in the cache - perhaps the page has changed since it was looked up
Command duration or timeout: 30.01 seconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.25.0', revision: '17482', time: '2012-07-18 21:09:54'
and here is my code :
public void getLinks()throws Exception{
try {
List<WebElement> links = driver.findElements(By.tagName("a"));
int linkcount = links.size();
System.out.println(links.size());
for (WebElement myElement : links){
String link = myElement.getText();
System.out.println(link);
System.out.println(myElement);
if (link !=""){
myElement.click();
Thread.sleep(2000);
System.out.println("third");
}
//Thread.sleep(5000);
}
}catch (Exception e){
System.out.println("error "+e);
}
}
actually, it's displaying in output
[[FirefoxDriver: firefox on XP (ce0da229-f77b-4fb8-b017-df517845fa78)] -> tag name: a]
as link, I want to eliminate these form result.
There is no such a good idea to have following scenario :
for (WebElement element : webDriver.findElements(locator.getBy())){
element.click();
}
Why? Because there is no guarantee that the element.click(); will have no effect on other found elements, so the DOM may be changed, so hence the StaleElementReferenceException.
It is better to use the following scenario :
int numberOfElementsFound = getNumberOfElementsFound(locator);
for (int pos = 0; pos < numberOfElementsFound; pos++) {
getElementWithIndex(locator, pos).click();
}
This is better because you will always take the WebElement refreshed, even the previous click had some effects on it.
EDIT : Example added
public int getNumberOfElementsFound(By by) {
return webDriver.findElements(by).size();
}
public WebElement getElementWithIndex(By by, int pos) {
return webDriver.findElements(by).get(pos);
}
Hope to be enough.
Credit goes to "loan".
I am also getting "stale exception" so I used 'loan' answer and works perfectly. Just if anyone need to know how to click on each link from results page try this (java)
clickAllHyperLinksByTagName("h3"); where "h3" tag contains hyperlink
public static void clickAllHyperLinksByTagName(String tagName){
int numberOfElementsFound = getNumberOfElementsFound(By.tagName(tagName));
System.out.println(numberOfElementsFound);
for (int pos = 0; pos < numberOfElementsFound; pos++) {
getElementWithIndex(By.tagName(tagName), pos).click();
driver.navigate().back();
}
}
public static int getNumberOfElementsFound(By by) {
return driver.findElements(by).size();
}
public static WebElement getElementWithIndex(By by, int pos) {
return driver.findElements(by).get(pos);
}
WebDriver _driver = new InternetExplorerDriver();
_driver.navigate().to("http://www.google.co.in/");
List <WebElement> alllinks = _driver.findElements(By.tagName("a"));
for(int i=0;i<alllinks.size();i++)
System.out.println(alllinks.get(i).getText());
for(int i=0;i<alllinks.size();i++){
alllinks.get(i).click();
_driver.navigate().back();
}
If you're OK using WebDriver.get() instead of WebElement.click() to test the links, an alternate approach is to save the href value of each found WebElement in a separate list. This way you avoid the StaleElementReferenceException because you're not trying to reuse subsequent WebElements after navigating away with the first WebElement.click().
Basic example:
List<String> hrefs = new ArrayList<String>();
List<WebElement> anchors = driver.findElements(By.tagName("a"));
for ( WebElement anchor : anchors ) {
hrefs.add(anchor.getAttribute("href"));
}
for ( String href : hrefs ) {
driver.get(href);
}
//extract the link texts of each link element
for (WebElement elements : linkElements) {
linkTexts[i] = elements.getText();
i++;
}
//test each link
for (String t : linkTexts) {
driver.findElement(By.linkText(t)).click();
if (driver.getTitle().equals(notWorkingUrlTitle )) {
System.out.println("\"" + t + "\""
+ " is not working.");
} else {
System.out.println("\"" + t + "\""
+ " is working.");
}
driver.navigate().back();
}
driver.quit();
}
For complete Explanation Read This POST
List <WebElement> links = driver.findElements(By.tagName("a"));
int linkCount=links.size();
System.out.println("Total number of page on the webpage:"+ linkCount);
String[] texts=new String[linkCount];
int t=0;
for (WebElement text:links){
texts[t]=text.getText();//extract text from link and put in Array
//System.out.println(texts[t]);
t++;
}
for (String clicks:texts) {
driver.findElement(By.linkText(clicks)).click();
if (driver.getTitle().equals("notWorkingUrlTitle" )) {
System.out.println("\"" + t + "\""
+ " is not working.");
} else {
System.out.println("\"" + t + "\""
+ " is working.");
}
driver.navigate().back();
}
driver.quit();

Categories