I am trying to learn the PageFactory model. I understood the fact that when we do a initElements, the WebElements are located. Say for example, I click on a webelement and because of which there is a change in one of the other webelements in DOM. Now, obviously I would get a StaleElementReferenceException here. How would I resolve this issue?
Should I find that specific WebElement again knowing the fact that there can be a change in the WebElement's properties in the DOM? or is there an another way to handle this?
StaleElementReferenceException
StaleElementReferenceException extends WebDriverException and indicates that the previous reference of the element is now stale and the element reference is no longer present on the DOM of the page.
Common Reasons
The common reasons behind facing StaleElementReferenceException are as follows:
The element has been deleted entirely.
The element is no longer attached to the DOM.
The webpage on which the element was part of has been refreshed.
The (previous) element has been deleted by a JavaScript or AjaxCall and is replaced by a (new) element with the same ID or other attributes.
Solution : If an (old) element has been replaced with new identical one, the simple strategy would be to use findElement() or findElements to look out for the element again.
Answering your queries
When we do a initElements, the WebElements are located : When you call initElements() method, all the WebElements of that page will get initialized. For example,
LoginPageNew login_page = PageFactory.initElements(driver, LoginPageNew.class);
This line of code will initialize all the static WebElements defined within the scope of the LoginPageNew.class whenever and wherever it is invoked from your Automation Script.
I click on a webelement and because of which there is a change in one of the other webelements in DOM : This is pretty much possible.
As an example, in general invoking click() on a <input> tag wouldn't trigger any change of any of the WebElements on the HTML DOM.
Where as invoking click() on a <button> tag or <a> tag may call a JavaScript or a Ajax which inturn may delete an element or can replace the (previous) element by a (new) element with the same ID or other attributes.
Conclusion
So, if WebDriver throws a StaleElementReferenceException, that implies even though the element still exists, the reference is lost. We should discard the current reference we have and replace it by locating the WebElement once again when it gets attached to the DOM. That means you have to again reinitialize the class through initElements() method which inturn reinitializes all the WebElements defined in that page.
Solution
If a old element has been replaced with new identical one, the simple strategy would be to invoke WebDriverWait inconjunction with ExpectedConditions to look out for the element.
You can find relevant detailed discussions in:
How to add explicit wait in PageFactory in PageObjectModel?
References
Here are the references of this discussion:
Stale Element Reference Exception
Class StaleElementReferenceException
Selenium: How to tell if RemoteWebDriver.findElements(By) can throw StaleElementReferenceException at all?
This is a known problem with the PageFactory implementation.
If you are unlucky enough for the element to become stale in the instant between the element being found, and then the element being clicked upon, you will get this error. Unfortunately the PageFactory code does not try to find the element again if it has become stale and it throws an Exception.
I would classify this as a bug with PageFactory, it should auto re-find the element if it ever becomes stale (unless the #CacheLookup annotation is used).
The suggestion to recall initElements isn't going to fix anything, you only need to init the elements once because that binds a Java proxy class to the element in question. The page factory implementation is supposed to remove the possibility of StaleElementReferenceExceptions (hence why this is a bug)
Stale element exception is thrown in two cases
The element is no longer attached to the DOM.
The element has been deleted entirely.
When this happen you wrap your code in try catch block then you can loop and retry as many times as you need until it succeeds.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}
Related
Trying to use the #FindBy annotation my feeling is there is no perfect way to check the presence of an element.
There are similar discussions here or here, but I see only workarounds and nothing like
#FindBy (id = "abc")
private WebElement TestElement;
if (TestElement.isPresent) {...};
I found solutions with #FindBys, but this way I have to implement an additional #FindBys for every element I would check the presence within DOM? Not really nice.
Every alternative solution with ExpectedConditions.presenceOf... or anything using FindElement needs a locator as parameter instead of a WebElement, correct?
Sure I can build workarounds using e.g. ExpectedConditions.elementToBeClickable(WebElement) within a try/catch and raise an org.junit.jupiter.api.Assertions.fail, but this is not a real DOM check.
To me it seems I'm more flexible with classic By definitions of Page Objects (e.g. private By searchInput = By.xpath("\\input[#class='input_search']");) instead of using #FindBy. Using #FindBy I always deal with the WebElement itself and there is no chance to check the presence before, right?
What are best pratice solutions to check the DOM presence of elements in context of page objects using #FindBy? Or should I stay with By to be safe.
This may not be the best solution but you can use findBy and return a list:
List<WebElement> TestElement = driver.findElements(By.id(...));
and check the size of the list elelement each time (in a while loop) to see if it is not empty.
Selenium: How selenium identifies elements visible or not? Is is possible that it is loaded in DOM but not rendered on UI?
I would like to verify a scenario where element is clickable, I know web drive has method "ElementToBeClickable" however, I would like to know the inner working. Please help me on this.
Also,how to handle a scenario where the element is loaded in the DOM but UI shows loading in progress, how to wait for complete load?
Please let me know, if the UI is not loaded then will selenium directly call the DOM element or if UI element is being loaded then it will fail the execution? I would really appreciate more technical explanation on this.
Selenium can identify the presence or visibility of the elements as soon as they are present or visible in the HTML DOM. From user perspective you can invoke isDisplayed() method on an WebElement to examine if the intended WebElement is displayed or not. As per current implementation Selenium may not be distinguishing between loaded and rendered elements. The ElementToBeClickable method in ExpectedConditions class sets an expectation for checking if an element is visible and enabled so that you can click it.
When the element is loaded in the DOM but UI shows loading in progress you still have to wait for the JavaScript and AJAX Calls to complete loading the page so all the WebElements on the page becomes interactable. At most to wait for complete load you can set the pageLoadStrategy to normal but may still have to induce WebDriverWait for the intended WebElement to become present, visible, interactable or clickable.
Here you can find a detailed discussion on Page load strategy
Of-coarse if the UI is not loaded Selenium may not be able to interact with a few of the DOM elements.
Update
As per your counter question here are the different stages of an WebElement and the respective ExpectedConditions to check the stages :
Presence of an element :
presenceOfElementLocated(By locator)
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.
Visibility of an element :
visibilityOf(WebElement element)
An expectation for checking that an element, known to be present on the DOM of a page, is visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
Element to be Clickable :
elementToBeClickable(By locator)
An expectation for checking an element is visible and enabled such that you can click it.
Note : As per the docs Element is Clickable - it is Displayed and Enabled.
.isDisplayed()--> never returns False why? it always give No Such Element Exception though i am using try catch block.
try {
boolean status_second= Appointment_Booking_page.second_confirmation.isDisplayed();
System.out.println("Current Second Appointment booking status-->" + status_second);
}
catch (NoSuchElementException e) {
}
Please suggest for same.
Is displayed is not giving you false because even before that method is executed ,NoSuchElementException is occured.
isDisplayed is used in cases where element is present in DOM and you need to check whether it is displayed or not in the UI. It is never used to check whether an element is present in the DOM.
Your questions is correct, isDisplayed will display True for the all the element that are present , anything else will definitely go to NoSuchElementException. This is correct only for the element which are displayed in the UI , check with an element that is not displayed example Hidden Elements, or for that matter the element which is displayed only after scrolling, i believe in these scenarios it should display False.
Here is another question regarding same:
https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/1880
It is not True that .isDisplayed() method never returns False.
In your case .isDisplayed() method is invoked through Appointment_Booking_page.second_confirmation which is your possible Strategy to locate an webelement. As the Locator Strategy you have adapted doesn't identifies the intended webelement uniquely so (even before .isDisplayed() is invoked) NoSuchElementException is thrown and your program terminates.
It is worth to mention, even if the webelement is present on the Web Page but not within the Viewport, .isDisplayed() method can still return False.
isDisplayed() is the method used to verify presence of a web element within the webpage. The method is designed to result from a Boolean value with each success and failure. The method returns a “true” value if the specified web element is present on the web page and a “false” value if the web element is not present on the web page
There is a lot of confusion around why isDisplayed method not returns false instead it throws NoSuchElementException ; the problem is with the understanding of when to use isDisplayed method
isDisplayed throws NoSuchElementException because the element isn't present in the DOM; (try inspecting element using your locating strategy for which you are getting the above exception in the browser using firepath or any developer tool in browser and you will see that your locating strategy has found 0 match elements)
When to use isDisplayed?
.isDisplayed() exists to tell you whether the element, which has already been located i.e. your element is present in the DOM(try inspecting element and you should find match element),is visible on the page; i.e. whether its width and height are greater than zero, it
isn't hidden by CSS, etc. If the element is present on the page, but has style="display:none;" then isDisplayed() will return false.
what is meant by above statement is although you have matching element but as the element got hidden by CSS thus you are getting false
Selenium: How selenium identifies elements visible or not? Is is possible that it is loaded in DOM but not rendered on UI?
I would like to verify a scenario where element is clickable, I know web drive has method "ElementToBeClickable" however, I would like to know the inner working. Please help me on this.
Also,how to handle a scenario where the element is loaded in the DOM but UI shows loading in progress, how to wait for complete load?
Please let me know, if the UI is not loaded then will selenium directly call the DOM element or if UI element is being loaded then it will fail the execution? I would really appreciate more technical explanation on this.
Selenium can identify the presence or visibility of the elements as soon as they are present or visible in the HTML DOM. From user perspective you can invoke isDisplayed() method on an WebElement to examine if the intended WebElement is displayed or not. As per current implementation Selenium may not be distinguishing between loaded and rendered elements. The ElementToBeClickable method in ExpectedConditions class sets an expectation for checking if an element is visible and enabled so that you can click it.
When the element is loaded in the DOM but UI shows loading in progress you still have to wait for the JavaScript and AJAX Calls to complete loading the page so all the WebElements on the page becomes interactable. At most to wait for complete load you can set the pageLoadStrategy to normal but may still have to induce WebDriverWait for the intended WebElement to become present, visible, interactable or clickable.
Here you can find a detailed discussion on Page load strategy
Of-coarse if the UI is not loaded Selenium may not be able to interact with a few of the DOM elements.
Update
As per your counter question here are the different stages of an WebElement and the respective ExpectedConditions to check the stages :
Presence of an element :
presenceOfElementLocated(By locator)
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.
Visibility of an element :
visibilityOf(WebElement element)
An expectation for checking that an element, known to be present on the DOM of a page, is visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
Element to be Clickable :
elementToBeClickable(By locator)
An expectation for checking an element is visible and enabled such that you can click it.
Note : As per the docs Element is Clickable - it is Displayed and Enabled.
I am trying to learn the PageFactory model. I understood the fact that when we do a initElements, the WebElements are located. Say for example, I click on a webelement and because of which there is a change in one of the other webelements in DOM. Now, obviously I would get a StaleElementReferenceException here. How would I resolve this issue?
Should I find that specific WebElement again knowing the fact that there can be a change in the WebElement's properties in the DOM? or is there an another way to handle this?
StaleElementReferenceException
StaleElementReferenceException extends WebDriverException and indicates that the previous reference of the element is now stale and the element reference is no longer present on the DOM of the page.
Common Reasons
The common reasons behind facing StaleElementReferenceException are as follows:
The element has been deleted entirely.
The element is no longer attached to the DOM.
The webpage on which the element was part of has been refreshed.
The (previous) element has been deleted by a JavaScript or AjaxCall and is replaced by a (new) element with the same ID or other attributes.
Solution : If an (old) element has been replaced with new identical one, the simple strategy would be to use findElement() or findElements to look out for the element again.
Answering your queries
When we do a initElements, the WebElements are located : When you call initElements() method, all the WebElements of that page will get initialized. For example,
LoginPageNew login_page = PageFactory.initElements(driver, LoginPageNew.class);
This line of code will initialize all the static WebElements defined within the scope of the LoginPageNew.class whenever and wherever it is invoked from your Automation Script.
I click on a webelement and because of which there is a change in one of the other webelements in DOM : This is pretty much possible.
As an example, in general invoking click() on a <input> tag wouldn't trigger any change of any of the WebElements on the HTML DOM.
Where as invoking click() on a <button> tag or <a> tag may call a JavaScript or a Ajax which inturn may delete an element or can replace the (previous) element by a (new) element with the same ID or other attributes.
Conclusion
So, if WebDriver throws a StaleElementReferenceException, that implies even though the element still exists, the reference is lost. We should discard the current reference we have and replace it by locating the WebElement once again when it gets attached to the DOM. That means you have to again reinitialize the class through initElements() method which inturn reinitializes all the WebElements defined in that page.
Solution
If a old element has been replaced with new identical one, the simple strategy would be to invoke WebDriverWait inconjunction with ExpectedConditions to look out for the element.
You can find relevant detailed discussions in:
How to add explicit wait in PageFactory in PageObjectModel?
References
Here are the references of this discussion:
Stale Element Reference Exception
Class StaleElementReferenceException
Selenium: How to tell if RemoteWebDriver.findElements(By) can throw StaleElementReferenceException at all?
This is a known problem with the PageFactory implementation.
If you are unlucky enough for the element to become stale in the instant between the element being found, and then the element being clicked upon, you will get this error. Unfortunately the PageFactory code does not try to find the element again if it has become stale and it throws an Exception.
I would classify this as a bug with PageFactory, it should auto re-find the element if it ever becomes stale (unless the #CacheLookup annotation is used).
The suggestion to recall initElements isn't going to fix anything, you only need to init the elements once because that binds a Java proxy class to the element in question. The page factory implementation is supposed to remove the possibility of StaleElementReferenceExceptions (hence why this is a bug)
Stale element exception is thrown in two cases
The element is no longer attached to the DOM.
The element has been deleted entirely.
When this happen you wrap your code in try catch block then you can loop and retry as many times as you need until it succeeds.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}