Java Selenium stale element reference, using AjaxLocatorFactory - java

I have a super-class Component, inside this class I store common ways to access elements attached to the component.
In the Component constructor, I initElements with the pagefactory
PageFactory.initElements(new AjaxElementLocatorFactory(root,10), this);
As I understand it, this will automatically keep element reference fresh, as long as the elements are annotated by the #FindBy annotation.
I have a specialized Component called SearchResultRow which extends Component.
In this component, I have all the WebElements related to a row in the search result.
What I want to do is to get the first search result and click on it. This is my code for that.
public void clickOnFirstResult() {
WebElement firstUser = wait.until(ExpectedConditions.elementToBeClickable(searchResultRoot.findElement(By.cssSelector("tbody > tr:nth-child(1)"))));
new SearchResultRow(firstUser).clickOn(SearchModel.NAME);
}
Here I wait for the element to become clickable, because it's a dynamic element that is not covered by the #FindBy annotation.
SearchModel.NAME refers to a WebElement annotated by #FindBy in the SearchResultRow component. Yet this method sometimes 10-15 % gives the error
stale element reference: element is not attached to the page document

Root cause
Stale element reference refers to web elements that were obtained before a page refresh but attempted to be used (i.e. calling click() method after said refresh.)
Solution
Wait.... Which are the best ways to wait for page reloads? One way is to use Implicit Waits. One very common way to implicitly wait some amount of time:
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
This is how it is done in Selenium Java, but there should be an equivalent method for whichever version of Selenium you are using. The question now is, what does this do? This is the way to tell the web driver to wait the given amount of time BEFORE attempting to obtain a web element. When the implicit wait time is set, it will remain set for the duration of your test session. Therefore, this is typically done at the very beginning before any tests begin to execute. More specifically, you should do it immediately after getting the web driver instance.
That said, this should not be enough. For example, what if the component become stale after initial load of a page? For that, you should use ExpectedConditions class and set an explicit wait for some condition to occur:
WebDriverWait wait = new WebDriverWait(driver, 10); // timeout of 10 seconds and polling the default value of 500 ms, or...
WebDriverWait wait = new WebDriverWait(driver, 10, 100); // timeout of 10 seconds and polling every 100 ms
Then use wait object to set your expected condition and obtain the required web element
WebElement element = wait.until(...);
You will need to pass the appropriate ExpectedConditions to the above method. Common ones to be used to avoid staleness:
WebElement element = wait.until(ExpectedConditions.elementSelectionStateToBe(...));
WebElement element = wait.until(ExpectedConditions.refreshed(...));
Lastly, you will check to see what the state of the element is before interacting with it. For example:
WebElement newElement = wait.until(ExpectedConditions.elementToBeClickable(element));
newElement.click();
In this case, element and newElement are the same object so using just wait.until(ExpectedConditions.elementToBeClickable(element)); should be enough before calling element.click(). By this point, you have established that the element is not stale and it is visible and enabled; and therefore, safe to be clicked (for instance).

It's caused by dynamic DOM.
wait.until(ExpectedConditions.elementToBeClickable(searchResultRoot.findElement(By.cssSelector("tbody > tr:nth-child(1)")))); ends when the element is found and clickable. However this occurs during the process of DOM building, so when you trying to click on this element it may disappear / change so the element you are referencing to is no more exists.
The simplest way to work with dynamic DOM pages is each time the DOM / page is reloaded / rebuilt is to give some enough delay time and only after that to deal with it's elements.
You already asked similar question and I already answered you there.

Related

Selenium : Element is found when using Thread.sleep(), but not Implicit/Explicit waits

I read about how beneficial in using Implicit/Explicit waits, and decided to abandon Thread.sleep() in my code. However, the result is opposite. Using Thread.sleep(), I can actually locate the element, while Implicit/Explicit waits result in the ElementClickInterceptedException: element click intercepted: [duplicate] error for the same element.
The element I am trying to simulate a click on is an "I AGREE" button located in a dialog box. The following is the DOM for the button which I am trying to click.
And the following is the code to simulate the click.
Using Implicit Wait
driver.manage().timeouts().implicitlyWait(120, TimeUnit.SECONDS);
driver.findElement(By.xpath("/html/body/div[1]/div/div/div[4]/a")).click();
Result : ElementClickInterceptedException: element click intercepted: [duplicate] error
Using Explicit Wait
WebDriverWait wait = new WebDriverWait(driver, 60);
WebElement I_Agree;
I_Agree = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/html/body/div[1]/div/div/div[4]/a")));
I_Agree.click();
Result : ElementClickInterceptedException: element click intercepted: [duplicate] error
Using Thread.sleep()
Thread.sleep(20000);
driver.findElement(By.xpath("/html/body/div[1]/div/div/div[4]/a")).click();
Result : Element is found and click can be executed.
Hope to have advice on any mistakes I may have done.
implicitlyWait waits for the element existence.
So in case of
driver.findElement(By.xpath("/html/body/div[1]/div/div/div[4]/a")).click();
Selenium will try clicking that element immediately after the goal element appears.
In many cases the element can still be not clickable so utilizing the implicitlyWait may often cause exceptions.
Using ExpectedConditions.visibilityOfElementLocated will mostly work better, but still there are several situations this will not work.
In some cases the goal element can be out of the visible screen. In this case you will have to scroll to the goal element in order to bring it into the visible screen and then click it properly.
In some other cases during the page rendering Selenium will indicate element as visible and clickable while actually some other element may be overlapping on the goal element so clicking on it will cause the wrong element accepting the click. However if you give some more wait the page rendering process will complete and now you can click the goal element properly.
This is seems to be your case.
In most cases the overlapping element will be some kind of spinner element.
If so you should add the ExpectedConditions.invisibilityOfElementLocated condition to wait until the spinner / other overlapping element is disappeared.
To give more precise answer we need to see the specific site you are working on and the actual test flow you are doing.
You can see more about the ElementClickInterceptedException here, here, here and at more resources.
UPD
From your comments I see you did not actually understand. Both implicitly and explicitly waits are pooling the DOM to find element matching the passed condition. In both cases at the moment the condition is met Selenium will immediately stop waiting and polling, it will return an element (or Boolean true in some cases) and continue to the next command.
Implicitly wait is waiting for element presence. So in many cases the element itself is still not finally rendered and may still not be clickable or visible but it is already present, so in your case
driver.findElement(By.xpath("/html/body/div[1]/div/div/div[4]/a"))
may return the element that is still not ready to be clicked on.
This is why it is preferred to use explicit waits like ExpectedConditions.visibilityOfElementLocated since in this case Selenium will wait polling the DOM until it finds the desired element to be visible, this is much more mature state of the element on the page and it can normally be clicked on this point.
However, sometimes it can be some spinner etc. on that element. This is some external problem. So in this case we need to add ExpectedConditions.invisibilityOfElementLocated to wait until that spinner is disappeared.
There are more cases explicit wait will not be enough to be applied on the element itself. Sometimes you will have to scroll the desired element into the view, sometimes you will even need to use JavaScript click etc.

How to wait HTML element appear globally with Selenide?

We have many e2e test written by Selenide.
To avoid failing test in test server, I would like selenide to wait for html element appearing.
I know some scripts like wait.until(...) for this. But, I don't want to fix all test code.
Is there any global switch or setting for Selenide ? ( in detail, I hope the option making cssselector waiting )
My question is resolved by this post
Implicit Wait is the way to configure the WebDriver instance to poll the HTML DOM (DOM Tree)
for a configured amount of time when it tries to find an element
or find a group/collection of elements if they are not immediately available.
As per the current W3C specification the default time is configured to 0.
We can configure the time for the Implicit Wait any where within our script/program and can reconfigure it as per our necessity.
Once we set Implicit Wait it will be valid for the lifetime of the WebDriver instance.
I think implicit wait time is almost the global switch I expected.
Before performing any action, selenide provides methods to look up for Conditions.
$("By Locator").shouldBe(Condition."Desired Condition").
I might be late for this question. But for anyone who still needs help in selenide wait, my answer might still be helpful.
For selenium if you have wait method like:
element = (new WebDriverWait(driver, <timeOutForElement>))
.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(<cssSelector>)));
You can perform the same in Selenide with element = $(<cssSelector>).should(exist);
For
element = (new WebDriverWait(driver, <timeOutForElement>))
.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(<cssSelector>)));
We just go with element = $(<cssSelector>).shouldBe(visible);
Reference Page Here.

How to create wait in selenium that waits for a web element to finish refreshing

I'm fairly new to Selenium and I'm writing a test for a web app using it. In doing this, I'm using assertions to make sure the web app is working correctly. For a few of these assertions, I'm asserting on a web element that has a numeric value in which the expected number is known. The problem is when a change is made that changes this numeric value the change happens gradually based on how fast the internet connection is. Up to this point have resorted to using sleep's to wait for the element to finish refreshing before I use assertions but I would like to make it so this wait is no longer than the time it takes for the element to stop refreshing and thus not have to include sleep's that are either too short or too long.
You should try this:
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(<specific locator of element>));
Sleep is not a good option because you wait always expected the amount of time.
In the approach presented above you always wait for the visibility of a specific element. When an element is visible your test steps will go forward. There is no extra wait time which you got with implicit sleep
Avoid using sleep and replace it with an implicit wait or use expected condition if applicable. below is c# code for it
int time =10; // set maximum time required for operation
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(time));
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.XPath(""))); //replace "" with your desired xpath
The above code will wait for max 10 seconds for an element to be visible. but if it appears earlier then it will jump to next process so you need not have to wait for a specific time. Also, there are other expected conditions available like ElementExists, ElementToBeClickable etc. I will leave it to you to explore the appropriate option for yourself
if you wan t to use implicit wait specifically use below code
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(50));

how to check page load if i have more than 50 pages

I am new to selenium webdriver (java coding). I have been given a website and asked to write script to check if all the pages load properly.I was not sure how to check a page load. One of my friend suggested that when a page load, i should look for last element which gets loaded and use a wait method on that element to check until its visible.
My question is I have more than 50 pages to check. For each page should i use a wait method and check visibility, becoz it takes a lot of time . Is there any other way?
You can solve the issue by following the steps given below (for all 50 pages, one at a time):
Identify an element which is displayed at last in the browser rendering.
Add visibility_of_element_located, to check whether the element is displayed on the web page or not.
Wait for visibility of an element:
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "myDynamicElement")) # here, you can use any available locator of your choice, like, By.XPATH, BY.CLASS_NAME etc.
This waits up to 10 seconds before throwing a TimeoutException or if it finds that the element is displayed, will return it in 0 - 10 seconds. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.
So, you are not actually waiting for 10 seconds to check whether the element is displayed. Once the element is displayed on the page, the method will return immediately within 10 seconds.
Note: 10 seconds is configurable.
You need to look in visual testing direction, and pure selenium not designed for that.
You may try two addons for Selenium:
1) Applitools (https://applitools.com/), cloud based, you could try it for free,
examples is here https://eyes.applitools.com/app/Web/Deploy/Tutorial/tutorial.html
2) Or you could use Sikuli(http://www.sikuli.org/), free, non cloud.

"Implicit wait" waiting for element to clickable, displayed or visible

There have been questions on the difference between implicit and explicit wait in Selenium WebDriver.
What is difference between Implicit wait Vs. Explicit wait in selenium webdriver?
When to use explicit wait vs implicit wait in Selenium Webdriver?
On SeleniumHq also:
Implicit Waits
An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if
they are not immediately available. The default setting is 0. Once
set, the implicit wait is set for the life of the WebDriver object
instance.
But not clear what kind of wait is this, I mean would it wait till isDispalyed, isVisible or isClickable
I believe the implicit wait does not care about any of those properties (isDisplayed, isVisible or isClickable). It just waits the time you set up and checks regularly whether the element you try to select is available in your DOM. If it is not found it will lauch a timeout error.
I believe narko is right and here's some code that I think proves it.
By hiddenLocator = By.id("csi");
FirefoxDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get("http://www.google.com");
WebElement hiddenEle = driver.findElement(hiddenLocator);
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.presenceOfElementLocated(hiddenLocator));
System.out.println("done");
I went to google.com and found an element that was hidden
<textarea name="csi" id="csi" style="display:none"></textarea>
I set the implicit wait to 30s, set up a WebDriverWait for 30s, and then waited for the element to be present. From the presenceOfElementLocated() source
An expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible.
If implicit wait was waiting for anything other than just the presence of the element in the DOM, it would have waited for 30s... but it finished as soon as the page was loaded in the browser.
I did some research to better understand what these different functions are doing and how they are truly different. Here's what I found...
WebElement has three methods related to this question: isDisplayed(), isEnabled(), and isSelected(). From the docs...
isDisplayed() Is this element displayed or not? This method avoids the
problem of having to parse an element's "style" attribute.
isEnabled() Is the element currently enabled or not? This will
generally return true for everything but disabled input elements.
isSelected() Determine whether or not this element is selected or not.
ExpectedConditions also comes into play here with several methods. I'll just look briefly at elementToBeClickable(). From the docs...
elementToBeClickable An expectation for checking an element is visible
and enabled such that you can click it.
If you look at the source, the description is accurate. You can look at the source for the other methods, etc. if you want more info but I think this is enough to answer your question.

Categories