(StaleElementException:Selenium) How do I handle this? - java

This is my first time first day working on selenium and I have no hands on experience on Web Technologies in depth either.
Working around, I have been facing StaleElementException while i try to access a particular object on the DOM.
Following Method handles all the task:
private void extract(WebDriver driver) {
try {
List<WebElement> rows = driver.findElements(By.xpath("//*[#id='gvSearchResults']/tbody/tr"));
for (WebElement row : rows) {
WebElement columns = row.findElement(By.xpath("./td[1]/a"));
if (assertAndVerifyElement(By.xpath("//*[#id='gvSearchResults']/tbody/tr/td[1]/a"))) {
columns.click();
}
List<WebElement> elements = driver
.findElements(By.xpath("//*[#id='ctl00_MainContent_pnlDetailsInd']/table/tbody/tr"));
for (WebElement element : elements) {
WebElement values = element.findElement(By.xpath("./td[1]"));
System.out.print(values.getText() + " ");
WebElement values2 = element.findElement(By.xpath("./td[2]"));
System.out.println(values2.getText());
}
if(assertAndVerifyElement(By.xpath("//*[#id='ctl00_MainContent_btnBack']")))
driver.findElement(By.xpath("//*[#id='ctl00_MainContent_btnBack']")).click();
}
} catch (Exception e) {
e.printStackTrace();
}
}
The Assertion logic goes here:
public boolean assertAndVerifyElement(By element) throws InterruptedException {
boolean isPresent = false;
for (int i = 0; i < 5; i++) {
try {
if (driver.findElement(element) != null) {
isPresent = true;
break;
}
} catch (Exception e) {
// System.out.println(e.getLocalizedMessage());
Thread.sleep(1000);
}
}
Assert.assertTrue("\"" + element + "\" is not present.", isPresent);
return isPresent;
}
I have tried few solutions asking me to use wait until expected conditions, but none of them worked.
Also, It would be appreciated if you point out any bad design practices I might be using in the above sample.

I can give you the idea to overcome staleness.
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.findElement(by.xpath(xpath)).click(); //This works
Another example:
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 StaleElementException occurs when the webelement in question is changed on the dom and the initial reference to that webelement is lost.
You can search for the webelement again
try this
try:
element = self.find_element_by_class('')
element.click()
except StaleElementReferenceException:
element = self.find_element_by_class('')
element.click()

Related

Is there A function that exists that can check if an element is a shadow root element?

So I have attempted this already with some code I got online. I think it may have something to do with the previous implementation.
The idea of this app is to dynamically scrape any website, doing the hard work first by literally scraping every element on the page and indexing important information like buttons and there relative xpath
I am having trouble detecting whether or not the element that I am currently iterating through is a shadow root element or not.
public void getListOfElements(List<WebElement> e, String previous) {
if(e.size() == 0) {
//exit
return;
}
for(WebElement elem: e) {
//checking whether or not the tag that is a shadow root is there
if(elem.getAttribute("id").equals("wmHostPrimary")) {
System.out.println("I FOUND THE WMHOSTPRIMARY");
}
//above works and prints, so why does the below not work
WebElement potShadowRoot = getShadowRoot(webDriver, elem);
if(potShadowRoot != null) {
//the below never runs I am assuming that potShadowRoot is always null
System.out.println("Shadow root element found\n\n");
getListOfElements(potShadowRoot.findElements(By.xpath(".//*")), elem.getTagName());
} else {
List<WebElement> webElems = webDriver.findElements(By.xpath(previous + "/" + elem.getTagName() + "/*"));
getListOfElements(webElems, previous + "/" + elem.getTagName());
}
}
}
the below function always returns null even though there is a shadow root being found from above checking I have verified
private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) {
JavascriptExecutor js = (JavascriptExecutor) driver;
try {
return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
} catch(Exception e) {
return null;
}
}
when I call the function I am calling the children of the top layer of the HTML tag.
getListOfElements(webDriver.findElements(By.xpath("/html/*")), "html");
I think that is all you would need for a minimal example. I have WebDriver autowired and this is a springboot app.
if you need more information please let me know appreciate the help.
the HTML may help as well here is a rough design:
<html>
<body><div id="wmHostPrimary">#shadow-root (open)</div></body>
...othertags
</html>
it may also help to know that the following code works:
String str = "return document.querySelector('#wmHostPrimary').shadowRoot.querySelector('body')";
WebElement element = (WebElement) js.executeScript(str);

StaleElementReference Exception with Webdriver using firefox v57

Using the following code I keep getting a StaleElementReference Exception when running my Webdriver scripts on firefox v57. I have tried all sorts of things but other than a thread.sleep or catching the exception and retrying I cannot get it to work.
public List<String> readCoIData (String column) throws StaleElementReferenceException
{
int colNumber 0;
WebTable searchResuIts = getTable();
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
wait.pollingEvery(250, TimeLinit.MILLISECONDS);
wait.with Timeout (30, TimeLlnit.SECONDS);
wait.ignoring (NoSuchElementException.class) ;
CustomLogger.addInfo(Logger, "Ensure that that the first column row contains text: " column ) ;
int colcount = searchResults.getColumnCount();
CustomLogger. addlnfoLog4jOnly (logger , "colcount= " colcount )
for (int col =1; col <= colcount; col++)
{
CustomLogger.addInfoLogJOnly(Logger, "Get the cell data for col. Use a string as this does not go stale unlike a reference.'
String locator = String.format("pf_table_t2 > tbody:nthchild(1) > tr:nth-child(1) > td:nth-child(%d)", col);
wait.until(ExpectedConditions.presenceOfElementLocated(By. cssSelector (locator))) ;
if (driver.FindElement(By.cssSelector(locator)).getText().contains (column))
{
colNumber = Col;
}
}
CustomLogger.addInfoLogJOnly(Logger, "Assert that there is not 0 columns");
assertThat (column + " not found' , colNumber !=0, is (true));
List<String> colvalues = new ArrayList<String>();
CustomLogger.addInfoLogJOnly(Logger, "Get the object .pf_paging a");
List<WebElement) paging = driver.findElements(By.cssSelector(.pf_paging a));
if (paging.size() !=0)
{
CustomLogger.addlnfoLogJOnly ( logger , "If more than one result page, wait..." );
wait.until(ExpectedConditions.visibilityOf((WebElement driver.findElement(By.cssSelector(".pf_paging_next_active"))));
CustomLogger.addInfoLogJOnly(Logger, "Span with '>>' is found so loop through all pages and get the data");
while (driver.findElements(By.cssSelector(".pf_paging_next_active")). size() == 1)
{
for (int i=2; i<=searchResults.getRowCount(); i++)
{
String locator = String.format("pf_table_t2 > tbody:nthchild(1) > tr:nth-child(%d) > td:nth-child(%d)", I, colNumber); col);
String cell Text = driver.findElement(By.cssSelector(locator)).getText();
colvalues.add(cellText);
}
CustomLogger.addInfoLogJOnLy(Logger, "Click the Next Page Button ' to move onto next page and wait for the page to be visible.'
driver.findElements(By.cssSelector(.pf_paging_next_active a").click();
CustomLogger.addInfoLogJ0nly(Logger, "Get & Wait for table after clicking next");
// Get the element table again as we have clicked 'filter' and the DOM would have changed.
searchResults.getTable();}
public WebTable getTable()
{
CustomLogger.addInfoLog450nLy(Iogger, "Wait and get the element table again as we have previously clicked 'filter' and the DOM would have changed");
new WebDriverWait(driver, 30).until((ExpectedConditions<Boolean> driver -> ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete"));
FluentWait(WebDriver» wait new
wait.pollingEvery(250, TimeLinit.MILLISECONDSS);
wait.with Timeout(30, TimeLlnit.SECONDS);
wait.ignoring (NoSuchEIementException.class) ;
CustomLogger.addInfo(Logger, "Wait until the results table is present..
wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("pf_table_t2"))));
CustomLogger.addInfo(Logger, "Get the results table");
SearchResuItsTable = driver.FindElement(By.cssSelector("pf_table_t2"));
WebTabIe searchResuIts = new WebTable(SearchResultsTabIe);
return searchResuIts;
}
Essentially the page has some filter criteria and a results table with results in blocks of 10 rows, to get each new set you have to click >. i.e. the page changes.
I get StaleElement exception in two places, the first happens getting the text at the locator (then setting colNumber = Col). The 2nd happens adding celltext to the colValues array.
In both places I have only just got the element again. Any help would be appreciated. Thanks in advance.
Note I don't get this with the Chrome browser.
Just to give you a brief (if you are not aware already)
StaleElementReferenceException happens because location of the webelement on the webpage (more preciously on the DOM) has changed due some ajax or similar reason. Coincidentally this happens exactly between you capturing the webelement and performing the action on the webelement.
For Example:
//Getting the webelement
WebElement element = driver.findElement(By.id("id_value"));
//Location of the webelement changes between these two steps.
//Click on the webelement
element.click();
One of the ways to handle the StaleElementReferenceException is to catch the exception at the place where it happens and handle it by re-initializing the webelement.
For example:
try {
WebElement element = driver.findElement(By.id("id_value"));
element.click();
} catch (StaleElementReferenceException e) {
Thread.sleep(3000);
WebElement element = driver.findElement(By.id("id_value"));
element.click();
}
There are also others ways to handle the StaleElementReferenceException. You can refer this link.
Better and cleaner way to handle StaleElementReferenceException is to make method for action that you are trying to perform.
For Example:
If you want click on a webelement.
public static void click(By by) throws Exception {
try {
driver.findElement(by).click();
} catch(StaleElementReferenceException staleElement) {
Thread.sleep(3000);
driver.findElement(by).click();
} catch (Exception e) {
e.printStacktrace();
}
}
Similarly you can create other methods.

Getting "Element is no longer valid" error while executing the below the code

I am new to Selenium. Getting
org.openqa.selenium.StaleElementReferenceException: Element is no longer valid (WARNING: The server did not provide any stacktrace information)
error while running the below the code.
Expectation: while loop should resume once the method "changdrawer" returned true. Please help me if any correction is needed in my code.
public class ManageTaskList {
public void CheckRequestType() throws InterruptedException
{
//Switching the driver to TaskList frame
LaunchBrowser.driver.switchTo().frame("taskList");
boolean dateFlag=false;
String date = "06/12";
WebElement table = LaunchBrowser.driver.findElement(By.id("dataTable"));
List<WebElement> rows = table.findElements(By.tagName("tr"));
Iterator<WebElement> i = rows.iterator();
System.out.println("Table has following content");
while(i.hasNext())
{
WebElement row=i.next();
List<WebElement> columns= row.findElements(By.tagName("td"));
Iterator<WebElement> j = columns.iterator();
while(j.hasNext())
{
WebElement column = j.next();
String ColumnValues=column.getText();
//System.out.println("ColumnValues" + ColumnValues);
if (ColumnValues.contains(date))
{
System.out.println("Date confirmed" +ColumnValues );
dateFlag = true;
}
if (ColumnValues.contentEquals("Issue Change Drawer") && dateFlag==true)
{
System.out.println("Found Change Drawer");
dateFlag=false;
column.click();
ChangeDrawer();
Thread.sleep(100);
}
}
}
public boolean ChangeDrawer()
{
// Issue Change Drawer
LaunchBrowser.driver.findElement(By.xpath(".//*[#id='content']/div[3]/form/table/tbody/tr[2]/td/table/tbody/tr/td[1]/input")).click();
LaunchBrowser.driver.switchTo().defaultContent();
return true;
}
}
If the changeDrawer method (that is, a click on the element) causes the page to refresh, or even the table elements to change, even if you end up having a WebElement that still matches your selector (in this case, rows), you are still holding a reference to the "old" object, which doesn't exist anymore in the page.
You would need to call the findElement/s method again to refresh the WebElement if that is the case.

Confirm the Page is loaded or not

i have list of Hyperlinks in one page, when i click the links , they are redirecting to the new tab. how do i find out whether the page is loaded or not. i have used the do while loop for find out the element is enabled or not. but i am getting "no such element" only. could you please help on this.. Below is the piece of code. i have tried with Explicit wait also . but getting the same issue.
WebElement element7 = driver.findElement(By.id("MenuControlBNY_MenuNavBar_MenuControlBNY_MenuNavBar_p11__Tree_item_2_cell"));
if (element7.isEnabled())
{
element7.click();
System.out.println(" Report is selected");
}
boolean element8 = false;
int count = 0 ;
do
{
element8 = driver.findElement(By.id("working")).isEnabled();
System.out.println("Report is loaded");
count = count+1;
if(count == 1000)
{
break;
}
}while(element8 == true);
I use the following, its fairly self explanatory.
private static WebDriverWait wait = new WebDriverWait(driver, 60);
private static JavascriptExecutor js = (JavascriptExecutor) driver;
public static void waitForPageLoaded() {
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
Boolean res = (js.executeScript("return document.readyState").equals("complete"));
System.out.println("[DEBUG] waitForPageLoaded: " + res);
return res;
}
});
}
EDIT: This will return true when the page is in a ready state ie the page is loaded. So you would call the above method prior to searching for your element.
You can set an implicit wait which will wait for the elements when finding them for a 15 seconds
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
or explicitly:
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("working")));
From the information you have provided, I am assuming you have clicked the link and the page is opened in a new tab and then you are getting nu such elment exception.
In this case you need to switch to the new tab using window handles and use explict wait for element.here is sample code
String parentWindow= driver().getWindowHandle();
element7.click();
Set<String> myset = driver().getWindowHandles();
myset.remove(parentWindow);
driver().switchTo().window((String)myset.toArray()[0]);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(locator)));

handle exception while checking links using selenium webdriver

I am trying to check all the menu urls of a webpage by checking H1 element on the page. So when the loop runs and encounters an exception loop stops. I want the loop to continue even if it encounters a mismatch of css locator. please help
here's my code
List<WebElement> dropdown = Driver.findElementsByXPath("//ul[#class='nav']//ul//li//a");
int dropdown_count = dropdown.size();
System.out.println(dropdown_count);
for (int i=0; i<dropdown_count; i++)
{
String page = dropdown.get(i).getAttribute("href");
System.out.println(page);
Thread.sleep(2000);
Driver1.get(page);
try
{
Driver1.findElement(By.cssSelector("h1")).isDisplayed();
}
catch(NoSuchElementException ne)
{
System.out.println("error page: " + page);
}
{
System.out.println("page heading: " + Driver1.findElement(By.cssSelector("h1")).getText());
}
}
You have two major problems here:
Problem #1 - An unhandled NoSuchElementException.
The only statement in your code that can lead to that, is Driver1.findElement(...).
You are executing this statement inside the try clause, but also outside the try clause.
So obviously, the loop (and probably the entire method) is terminated due to that exception.
Therefore, you need to move the second Driver1.findElement(...) into the try clause.
In fact, in the try clause, you might as well search for the h1 element only once:
try
{
WebElement h1 = Driver1.findElement(By.cssSelector("h1"));
if (h1.isDisplayed())
System.out.println("page heading: " + h1.getText());
}
catch(NoSuchElementException ne)
{
System.out.println("error page: " + page);
}
Problem #2 - An unhandled StaleElementException.
This exception usually occurs when you fetch some WebElement object, and at a later point in execution - after navigating to a different page using get(...), or after switching to a different frame using switchTo().frame(...) - you attempt to use it.
This is what probably happens when you attempt to use dropdown after calling Driver1.get(page).
Here is an optional workaround for this problem:
for (int i=0; true; i++)
{
List<WebElement> dropdown = Driver.findElementsByXPath(...);
int dropdown_count = dropdown.size();
if (dropdown_count >= i)
break;
String page = dropdown.get(i).getAttribute("href");
Driver1.get(page);
...
Driver1.navigate().back();
}
And here is an alternative solution to both problems, that would make your program a lot more efficient. First, iterate all the links and build a list of URLs. Then, navigate to each URL and do whatever you wanna do on each page:
List<WebElement> links = Driver.findElementsByXPath(...);
List<String> pages = new ArrayList<String>();
for (WebElement dropdown : links)
{
String page = dropdown.getAttribute("href");
pages.add(page);
}
for (String page : pages)
{
Driver1.get(page);
try
{
WebElement h1 = Driver1.findElement(By.cssSelector("h1"));
if (h1.isDisplayed())
System.out.println("page heading: " + h1.getText());
}
catch(NoSuchElementException ne)
{
System.out.println("error page: " + page);
}
}

Categories