ElementReferenceException: stale element reference: element is not attached to the page document - java

I have a test for Selenide:
#Test
public void Test(){
open("https://market.yandex.ru/catalog--smartfony/54726/list?hid=91491&glfilter=7893318%3A153043&glfilter=4940921%3A13475069&onstock=1&local-offers-first=0");
new AfterPhonesCategory()
.collectResults();
}
I have a methods which get the product names and return the selenide elements, when the product runs out, we go to the next page and also get the elements again until they run out:
public class AfterPhonesCategory {
public List<String> collectResultsFromPage() {
List<SelenideElement> resultsNameWebElement = $$x("//article//h3[#data-zone-name = 'title']//span");
Assertions.assertTrue(resultsNameWebElement.stream().anyMatch(x->x.getText().contains("Apple")),
"the snippet does not contain the name of the apple");
return resultsNameWebElement.stream() //line 34
.map(SelenideElement::getText)
.collect(Collectors.toList());
}
private boolean initNextPageButton() {
List<SelenideElement> goNextPageButton = $$x("//a[contains(#class, '_3OFYT')]");
if(goNextPageButton.size() > 0){
goNextPageButton.get(0).click();
return true;
}
else
return false;
}
private List<String> findResults;
public AfterPhonesCategory collectResults() {
findResults = collectResultsFromPage();
while (initNextPageButton()) { //line 52
findResults.addAll(collectResultsFromPage());
}
return this;
}
but when executing the code I get an error:
Caused by: org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
at pages.AfterPhonesCategory.collectResultsFromPage(AfterPhonesCategory.java:34)
at pages.AfterPhonesCategory.collectResults(AfterPhonesCategory.java:52)
What am I doing wrong?

StaleElementReferenceException will be occurs only when your element is not present(vanished or not constructed till) in your website. please add wait conditions to check the visiblity of the element.
By optionXpath = By.xpath("your xpath fp goes here !");
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.elementToBeClickable(optionXpath));
wait.until(ExpectedConditions.visibilityOfElementLocated(optionXpath));
driver.findElement(optionXpath).click();
In case of not constructed :
you need to add wait conditions
In case of vanished :
yours actions on element is wrong. add screenshot just before your failure code and
recheck your code.

Related

If Selenium driver does not find Element ( If - else if) move on, yet it throws a can't find element error

Long story short - I have a button that doesn't have ID's and has a compound class( So selenium hates it / cant find it). So I use the XPath selector for it that works great
driver.findElement(By.xpath("//input[#value='Continue to Payment']")).click()
But the button changes depending on the language being used.
So at the moment, I have
if (driver.findElement(By.xpath("//input[#value='Continue to Payment']")).isDisplayed()){
driver.findElement(By.xpath("//input[#value='Continue to Payment']")).click();
}
else if (driver.findElement(By.xpath("//input[#value='Paiement']")).isDisplayed()){
driver.findElement(By.xpath("//input[#value='Paiement']")).click();
}
else if ( same thing as above but for another language)
But when Selenium errors out after going through the first if statement with:
no such element: Unable to locate element:{"method":"xpath","selector":"//a[contains(text(),'Checkout')]"}
I know the element is not there.. so I dont want it to do anything & move on to the next if else statement.
What am I missing here?
try {
if (driver.findElement(By.xpath("//input[#value='Continue to Payment']")).isDisplayed()){
driver.findElement(By.xpath("//input[#value='Continue to Payment']")).click();
}
else if (driver.findElement(By.xpath("//input[#value='Paiement']")).isDisplayed()){
driver.findElement(By.xpath("//input[#value='Paiement']")).click();
}
else
System.out.println("Button not found");
} catch(NoSuchElementException | StaleElementReferenceException e) {
System.out.println("Impossible to click the pop-up. Reason: " + e.toString());
}
Try above solution, Hopefully it will work for you. In your example wrong code has been written for else if (driver.findElement(By.xpath("//input[#value='Paiement']")).isDisplayed).
You can use separate short methods to achieve expected results and log errors.
public WebElement getElement(WebDriver driver, String XPATH, int timeoutInSeconds){
WebElement elem = null;
try{
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
elem = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(XPATH));
} catch (Exception e){
// log or print error.
}
return elem;
}
You can then call it like
WebElement e = getElement(driver, "//input[#value='Continue to Payment']", 10);
if (e != null) {
e.click();
} else {
e = getElement(driver, "//input[#value='Paiement']", 5);
if (e != null) {
e.click();
} /// and so on....
}
This way you can adjust the wait time for each element and also not fall into errors if any one element is missing because of language.

Alternative of isDisplayed() in Selenium java

public void privateCohortCreation() {
if(webElements.newCohortElm.isDisplayed()) {
SeleniumUtils.click(getDriver(),webElements.createCohortSelectionFromMenu);
webElements.cohortname.sendKeys("private_cohort_test");
SeleniumUtils.click(getDriver(),webElements.createCohortButton);
}
else {
doApply();
}
}
I want that if the element is displayed then perform the task else call doApply() method. But this is giving an exception
"no such element: Unable to locate element: {"method":"xpath","selector":"/html/body/app-root/div/app-container/app-indv301/app-global-filters/div/ul/li[3]/app-cohort/div/div/app-status/div"} (Session info: chrome=70.0.3538.77)"
You can use findElements() to check whether the element is on the webpage.
findElements() - returns empty list if there is no element with given locator
findElement() - throws NoSuchElementException if element is not on the page
Try below code:
List<WebElement> elements = driver.findElements(By.locator);
if(!elements.isEmpty()) {
if(elements.get(0).isDisplayed()) {
elements.get(0).click();
}
else {
// element not visible
}
}else{
// here mention code if element not present
}
Recommendation : Use relative xpath instead of absolute xpath. or try CSS selector instead.
Try using try catch instead of if else.
try {
if (webElements.newCohortElm.isDisplayed()) {
doApply();
}
}
catch (Exception e){
SeleniumUtils.click(getDriver(), webElements.createCohortSelectionFromMenu);
webElements.cohortname.sendKeys("private_cohort_test");
SeleniumUtils.click(getDriver(), webElements.createCohortButton);
}

Selenium and Java: How do I get all of the text after a WebElement

I am coding a program in Java using WebDriver and am having a little bit of trouble getting the text after the select webElement.
The HTML code for the part of the website that I want is as follows:
<select name="language" id="langSelect" style="width:100px;">
<option value="1" >Français</option>
</select>
</div>
<div id="content">
<div id="Pagination"></div>
<div id="mid">
</div>
</div>
The textbox class codes for a search bar and a drop down bar of languages
My Java code is currently able to open chrome using the chrome driver and is able to type into the search bar. I am however not able to get the text that results from the entry.
Image
In the image here, I entered "avoir" into the search bar, and I want all of the text inside the boxes after which do not seem to have any id's or names to be used inside the xpath.
Can someone please help me in finding how to get and save the text from those fields after the dropdown language menu?
Thank you in advance!
The code I have so far:
//import statements not shown
public class WebScraper {
public WebScraper() {
}
public WebDriver driver = new ChromeDriver();
public void openTestSite() {
driver.navigate().to(the URL for the website);
}
public void enter(String word) {
WebElement query_editbox =
driver.findElement(By.id("query"));
query_editbox.sendKeys(word);
query_editbox.sendKeys(Keys.RETURN);
}
public void getText() {
//List<WebElement> searchResults =
driver.findElements(By.xpath("//div[#id='mid']/div"));
// Writer writer = new BufferedWriter(new
OutputStreamWriter(new FileOutputStream("status.txt"),
"utf-8"));
//int[] index = {0};
WebElement result=driver.findElement(By.id("mid"));
System.out.println(result.getText());
}
public static void main(String[] args) throws IOException {
System.setProperty("webdriver.chrome.driver", "chromedriver");
System.out.println("Hello");
WebScraper webSrcaper = new WebScraper();
webSrcapper.openTestSite();
webSrcapper.enter("avoir");
webSrcapper.getText();
System.out.println("Hello");
}
}
I have specified three approaches to extract the text from the result box. Please check all the approaches and use the required approach.
If you want to extract all the text, then you can find the element of the result box and then you can get the Text from that.
WebElement result=driver.findElement(By.id("mid"));
System.out.println(result.getText());
If you want to extract the Text based on the Section by section, then you can go with the below approach,
List<WebElement> sectionList=driver.findElements(By.xpath("//div[#id='mid']/div"));
int i=0;
for(WebElement element:sectionList){
System.out.println("Section "+i+":"+element.getText());
i++;
}
If you want to extract the text from specific section, then you can do with the below approach
List<WebElement> sectionList=driver.findElements(By.xpath("//div[#id='mid']/div"));
int i=0;
//Inorder to get the Section 3 Content
int section=2;
for(WebElement element:sectionList){
if(section==i){
System.out.println("Section "+i+":"+element.getText());
}
i++;
}
Edit: To address followup question
I would suggest to use some explicit wait after doing some action which resulting in some element rendering. In your code, after doing some modification, I am getting the result as expected.
In openTestSite method, I have just added the explicit wait to ensure the page load after loading the URL
In enter method, actually you are getting the autocomplete suggestion after entering the query value .So, we need to just select the value from the autocomplete.
In getText method, Search result is taking more time.So, we need to add some explicit wait using any one of the dynamically loading element locator.
Code:
openTestSite Method:
public void openTestSite() {
//driver.navigate().to(the URL for the website);
driver.get("https://wonef.fr/try/");
driver.manage().window().maximize();
//Explicit wait is added after the Page load
WebDriverWait wait=new WebDriverWait(driver,20);
wait.until(ExpectedConditions.titleContains("WoNeF"));
}
enter Method:
public void enter(String word) {
WebElement query_editbox =
driver.findElement(By.id("query"));
query_editbox.sendKeys(word);
//AutoComplete is happening even after sending the Enter Key.
// So, Value needs to be selected from the autocomplete
WebDriverWait wait=new WebDriverWait(driver,20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#class='autocomplete']/div")));
List<WebElement> matchedList=driver.findElements(By.xpath("//div[#class='autocomplete']/div"));
System.out.println(matchedList.size());
for(WebElement element : matchedList){
if(element.getText().equalsIgnoreCase(word)){
element.click();
}
}
//query_editbox.sendKeys(Keys.RETURN);
}
getText Method:
public void getText() {
WebDriverWait wait=new WebDriverWait(driver,20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#id='mid']/div")));
WebElement result=driver.findElement(By.id("mid"));
System.out.println(result.getText());
}
I have tested with the above modified code and it is working fine.
In order to inspect the relevant results for your query, common strategy would be to load a list of search results:
List<WebElement> searchResults = driver.findElements(By.xpath("//div[#id='mid']/div"));
Now you can use stream to iterate over the list and extract your relevant text, by getting the text from child elements of each result:
int[] index = {0};
searchResults.stream().forEach(result -> {
System.out.println("Printing query result of index: " + index[0]);
result.findElements(By.xpath(".//*")).stream().forEach(webElement -> {
try {
System.out.println(webElement.getText());
} catch (Exception e) {
// Do nothing
}
});
index[0]++;
});
And you would get the output:

wait.until(ExpectedConditions.visibilityOf(element)); causes a WebDriverException: Failed to decode response from marionette

List<WebElement> itemList = getWebDriver().findElements(By.className("className"));
WebElement target = null;
for(WebElement e: itemList){
if(e.getAttribute("textContent").equals("Stuff")){
target = e;
}
}
wait.until(ExpectedConditions.visibilityOf(target));
The wait line at the bottom is throwing an exception, and I would like to know why. This works:
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("selector")));
But passing in a specific element instead of a By. path does not.

I am trying to select a value which is in the 2nd index from dropdown and compare it with the value fetched from excel

List <WebElement> elementCount = dropdown.getOptions();
System.out.println(elementCount.size());
for(int i=0;i<elementCount.size();i++)
{
System.out.println("Value of i is: "+i);
WebDriverWait wait = new WebDriverWait(dri10,500+(elementCount.size()*500));
WebElement element1 = wait.until(ExpectedConditions.elementToBeClickable(By.id("Select Role")));
dropdown.selectByIndex(i); //when the value of i becomes 2 an exception is displayed
Thread.sleep(100+(elementCount.size()*400));
String approle=dri10.findElement(By.id("Select Role")).getText();
String assigrole=s1.getCell(3,16).getContents(); //Fetching data from excel
if(approle.equals(assigrole))
{
dri10.findElement(By.id("Submenu")).click();
}
}
Exception Details: org.openqa.selenium.StaleElementReferenceException: Element not found in the cache - perhaps the page has changed since it was looked up
Command duration or timeout: 146 milliseconds
This exception is thrown when there are js updates on your element.
To avoid this exception, I usually surround my code with :
try{
//some code
}catch (StaleElementReferenceException e){
//retry it
}
I created some functions to avoid it. You can retry multiple time if the app is calling js multiple time also.

Categories

Resources