WebDriverWait. Fast check element exist or not exist - java

I got a problem and I don't know how to solve it right. Situation: user enters login and password, and then he could be at one of two pages.
Question is: how to check correctly in which page we are?
1. I want to use WebDriverWait, so my implicitlyWait = 0 ms,
2. I use Page Object pattern, pages were initialized with AjaxElementLocatorFactory
a. So, if I'll do method for check some element like this:
#FindBy(id = "pushOutMessage")
private WebElement messageText;
public boolean pageIsPresent() {
return messageText.isDisplayed();
}
It will be not right because if page is wrong, then WebDriver will wait N seconds for this element. So it makes my simple tests slow, very-very slow.
b. If I will check element with "findElement" - my implicity waits is 0 ms, so if element was slowly-loaded pageIsPresent returns false, even if page is right.
I hope there is some other way to do this. Need your help!

There are multiple ways you can do that. but the easiest way will be to check the elemenet counts
I would rather do
#FindBy(id = "pushOutMessage")
private List<WebElement> elements;
public int pageIsPresent() {
return elements.size();
}
And, somewhere test the pageIsPresent() is 0 or greater. if greater than 0 we know the page element was returned
And, since you are using pageobject pattern and Java I would recommand you to create an overloading to the baseClass which will check for a selector everytime you instantiate a new pageobject. I have a git repo here with TestNG. that might help

After the user enters or clicks submit or login button you can do something like this:
public Object clickLogin() {
loginElement.click();
try{
new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementedLocatedBy(By.id("pushOutMessage")));
return PageFactory.initElements(driver, FirstPage.class);
} catch (TimeoutException te) {
return PageFactory.initElements(driver, SecondPage.class);
}
}

Related

Amazon login and logout scenario using selenium webdriver

I am new to selenium.
I am trying to automate amazon login and log out.
I am facing couple of problems.
1.In login page the button "continue" appears after username when I try to login for first time.
But later when I try to login for second time , its does not appear.
How to handle this.
Here is the code I have written so far:
public void logindetails()
{
Datafile d=new Datafile("C:\\Users\\kirruPC\\selenium divers\\Data.xlsx",0);
String uname= d.username(0, 0);
WebElement u=driver.findElement(useid);
u.sendKeys(uname);
u.click();
if(driver.findElement(By.xpath(".//*[#id='continue']")).isDisplayed()==true)
{
driver.findElement(By.id("continue")).click();
String psw=d.pass(0,1);
driver.findElement(password).sendKeys(psw);
}
else
{
String psw=d.pass(0,1);
driver.findElement(password).sendKeys(psw);
}
}
Unable to locate sign Out element.
Below is the code I have written to move to sign out link and click:
public void logout() throws Exception
{
Actions a= new Actions(driver);
WebElement ele=driver.findElement(By.xpath(".//*[#id='nav-link-accountList']"));
a.moveToElement(ele).build().perform();
driver.findElement(By.xpath(".//*[#id='nav-al-your-account']"));
Thread.sleep(3000);
driver.findElement(By.xpath(".//*[#id='nav-al-your-account']/a[22]")).click();
}
Please help me
Thanks in advance
First of all, there is a high change that the element you are searching for might not exist at all in the page, so first let's check if the element exist (if the element does not exist, an exception is thrown, so let's add a handle for that as well). For all that, create a function like so:
public bool ElementExists(By locator)
{
try
{
driver.findElement(locator);
//If no exception is thrown here, element exists, so return true
return true;
}
catch (NoSuchElementException ex)
{
return false;
{
}
Now that we have a function that can safely check if an element exists without getting an exception, you can use it to determine if you will run the code handling the element.
Function isDisplayed() already returns a bool, so checking equality with true is not necessary.
if(ElementExists(By.xpath(...)).isDisplayed())
{
if(driver.findElement(By.xpath(".//*[#id='continue']")).isDisplayed())
{
driver.findElement(By.id("continue")).click();
}
}
//The code below will run either way, so move it out of the if statement
String psw=d.pass(0,1);
driver.findElement(password).sendKeys(psw);
As for the second part of your question, your code can be simplified by just, searching for the element and then clicking it like so:
driver.findElement(By.xpath(".//*[#id='nav-al-your-account']/a[22]")).click();
If you will, double-check the xpath or "catch" the element by an ID.

Wait for element - WebDriver - PageObject pattern

As long as I use PageObject pattern I wondered where should I wait for element on dynamic pages. Assuming we have test method and pageObject class. Should I do something like (in test method):
Click on button
Wait for element to be displayed
Verify the element (contains eg. method isElementDisplayed())
Or maybe there is other good practice to wait for the element? Maybe we should wait for element in method isElementDisplayed which is in PageObject.class?
You should wait for elements in your page object class, not in test class, because your elements should be defined in page object class, test class should know nothing of any elements, selectors or similar. Tests, IMHO, should contain only chains of method calls that describe the test flow, all the interaction with the website and underlying DOM should take place in Page Object class.
So an overly verbose method to wait for some element to appear could be something like:
private final By yourElement = By.id("id");
#Override
public void isLoaded() throws Error {
new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(1, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class)
.until(new Function<WebDriver, Boolean>() {
#NotNull
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = driver.findElement(yourElement);
return element != null && element.isDisplayed();
}
});
}
In plain words, the function if polling the DOM for 60 secs (every 1 second) to see, if the element exists in DOM and it is visible (means has height and witdh greater than 1px). If the element exists (and is displayed), the function returns the found element and stops the polling (although isLoaded() method does not return the element in this particular case).
It makes sense to ignore NoSuchElementException which can be thrown by findElement method in case the element is not found, and StaleElementException, which indicates that a reference to an element is now "stale" - the element no longer appears on the DOM of the page. This usually means, that something (most commonly JS) has modified the DOM and the reference is no longer valid, hence the WebDriver needs to look it up again.
Of course shorter code would also to the trick, something like:
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(someWebElement));
The documentation is actually pretty good on this.
EDIT: answer to the comment:
OK, understood. But what if element is present after clicking on some
button etc.?
Lets say you have a scenario, where you have a button and after clicking that button a textbox appears and you want to interact with it.
public class PageObject extends LoadableComponent<PageObject>{
public PageObject() throws Exception {
driver = getWebDriver();
PageFactory.initElements(driver, this);
isLoaded();
}
private WebDriver driver = null;
#FindBy(id = "yourButton")
private WebElement button;
#FindBy(id = "textBoxThatAppears")
private WebElement txtBox;
#Override
public void isLoaded() throws Error {
// Initial loading, called when creating the page object to make sure that the page is loaded to a state where it is ready to interact with us, in our case it means that button is present in DOM and visible.
waitForVisibility(button);
}
private void waitForVisibility(WebElement element) throws Error{
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(element));
}
public void clickButton(){
button.click();
}
public void interactWithTextbox(String text){
// Wait for txtBox to be visible, then send text
waitForVisibility(txtBox);
txtBox.sendKeys(text);
// EDIT 27.04.14:
// Actually you should not do the assertion here or anywhere in
// the pageObject, because when reusing the method in some other test, you might
// not want to assert, you might wonder that why wouldn't you assert some
// specific condition every time, but I would throw that question right back
// to you and ask: What is the point of checking the exact same thing over and
// over again. There are 2 things, firstly the assertion takes resources (and
// that can become important when test suite grows, secondly your tests can
// simply start failing at the same point when one little condition is not as
// it should be. Also, having the asserts in the test, makes the test more
// readable and understandable for others.
// end edit 27.04.14
// Next line is no longer recommended by this answer.
// assert that something happened that you expected.
}
}
And now your test class:
public void TestClass {
#Test
public void testClickButtonAndInteractWithTextbox(){
// Initiate the page object
Pageobject po = new PageObject();
po.clickButtonAndWaitForTextbox();
po.interactWithTextbox("blabla");
// edit 27.04.14
assertSomethingGoodHappened();
}
}
Another efficient concept of test page(Since selenium 1) from one of the selenium testing-frameworks - ISFW can be utilized over here. It has lazy loaded element, custom component feature and auto wait (not implicit wait that reduce performance), inbuilt wait methods with element and other features that are very useful for ajax bases application.
It provide following building blocks for developing test case:
Test Page
Component
Test step
In addition Reporting is also descriptive.

Selenium scan page for error elements while waiting

Ok, I'm not sure there is a ready solution to what my problem is, but essentially the website I am trying to do automated testing on is prone to "System Error"'s. I have a state machine that navigates through various webpages to get a desired result.
The crux of the issue is that selenium is doing a 120 second timeout while waiting for an element on the page but all the page has on it is the big "System Error" instead.
I already have some framework set up that can scan a page for the system errors and throw a CriticalPageException if it finds this. However, I want to be able to scan for this error in the WebDriverWait.
Is there a way to do this in the Java version of Selenium, or am I s.o.l.?
We had a similar issue. What we did was created a custom waitForWebElement method
that polls the site, you could do something similar
public WebElement waitForWebElement(final By by, final boolean isDisplayed) {
// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<SearchContext> wait = new FluentWait<SearchContext>(getSearchElement()).withTimeout(timeout, TimeUnit.SECONDS).pollingEvery(pollSeconds, TimeUnit.SECONDS).ignoring(NoSuchElementException.class);
WebElement webElement = wait.until(new Function<SearchContext, WebElement>() {
public WebElement apply(SearchContext search) {
try{
WebElement element = search.findElement(by);
catch(NoSuchElementException e){
if(CHECK_YOUR_ERROR_PRESENT){
throw CriticalPageException()
}else{
throw e;
}
}
return element;
}
});
return webElement;
}
EDIT : getSearchElement() just returns your search context

How to resolve, Stale element exception? if element is no longer attached to the DOM?

I have a question regarding "Element is no longer attached to the DOM".
I tried different solutions but they are working intermittent. Please suggest a solution that could be permanent.
WebElement getStaleElemById(String id, WebDriver driver) {
try {
return driver.findElement(By.id(id));
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElemById(id, driver);
}
}
WebElement getStaleElemByCss(String css, WebDriver driver) {
try {
return driver.findElement(By.cssSelector(css));
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElemByCss(css, driver);
} catch (NoSuchElementException ele) {
System.out.println("Attempting to recover from NoSuchElementException ...");
return getStaleElemByCss(css, driver);
}
}
Thanks,
Anu
The problem
The problem you are probably facing is that the method returns the right (and valid!) element, but when you're trying to access it a second later, it is stale and throws.
This usually arises when:
You click something that loads a new page asynchronously or at least changes it.
You immediatelly (before the page load could finish) search for an element ... and you find it!
The page finally unloads and the new one loads up.
You try to access your previously found element, but now it's stale, even though the new page contains it, too.
The solutions
There are four ways to solve it I know about:
Use proper waits
Use proper waits after every anticipated page-load when facing asynchronous pages. Insert an explicit wait after the initial click and wait for the new page / new content to load. Only after that you can try to search for the element you want. This should be the first thing you'll do. It will increase the robustness of your tests greatly.
The way you did it
I have been using a variant of your method for two years now (together with the technique above in solution 1) and it absolutely works most of the time and fails only on strange WebDriver bugs. Try to access the found element right after it is found (before returning from the method) via a .isDisplayed() method or something. If it throws, you already know how to search again. If it passes, you have one more (false) assurance.
Use a WebElement that re-finds itself when stale
Write a WebElement decorator that remembers how it was found and re-find it when it's accessed and throws. This obviously forces you to use custom findElement() methods that would return instances of your decorator (or, better yet, a decorated WebDriver that would return your instances from usual findElement() and findElemens() methods). Do it like this:
public class NeverStaleWebElement implements WebElement {
private WebElement element;
private final WebDriver driver;
private final By foundBy;
public NeverStaleWebElement(WebElement element, WebDriver driver, By foundBy) {
this.element = element;
this.driver = driver;
this.foundBy = foundBy;
}
#Override
public void click() {
try {
element.click();
} catch (StaleElementReferenceException e) {
// log exception
// assumes implicit wait, use custom findElement() methods for custom behaviour
element = driver.findElement(foundBy);
// recursion, consider a conditioned loop instead
click();
}
}
// ... similar for other methods, too
}
Note that while I think that the foundBy info should be accessible from the generic WebElements to make this easier, Selenium developers consider it a mistake to try something like this and have chosen not to make this information public. It's arguably a bad practice to re-find on stale elements, because you're re-finding elements implicitly without any mechanism for checking whether it's justified. The re-finding mechanism could potentially find a completely different element and not the same one again. Also, it fails horribly with findElements() when there are many found elements (you either need to disallow re-finding on elements found by findElements(), or remember the how-manyeth your element was from the returned List).
I think it would be useful sometimes, but it's true that nobody would ever use options 1 and 2 which are obviously much better solutions for the robustness of your tests. Use them and only after you're sure you need this, go for it.
Use a task queue (that can rerun past tasks)
Implement your whole workflow in a new way!
Make a central queue of jobs to run. Make this queue remember past jobs.
Implement every needed task ("find an element and click it", "find an element and send keys to it" etc.) via the Command pattern way. When called, add the task to the central queue which will then (either synchronously or asynchronously, doesn't matter) run it.
Annotate every task with #LoadsNewPage, #Reversible etc. as needed.
Most of your tasks will handle their exceptions by themselves, they should be stand-alone.
When the queue would encounter a stale element exception, it would take the last task from the task history and re-run it to try again.
This would obviously take a lot of effort and if not thought through very well, could backfire soon. I used a (lot more complex and powerful) variant of this for resuming failed tests after I manually fixed the page they were on. Under some conditions (for example, on a StaleElementException), a fail would not end the test right away, but would wait (before finally time-outing after 15 seconds), popping up an informative window and giving the user an option to manually refresh the page / click the right button / fix the form / whatever. It would then re-run the failed task or even give a possibility to go some steps back in history (e.g. to the last #LoadsNewPage job).
Final nitpicks
All that said, your original solution could use some polishing. You could combine the two methods into one, more general (or at least make them delegate to this one to reduce code repetition):
WebElement getStaleElem(By by, WebDriver driver) {
try {
return driver.findElement(by);
} catch (StaleElementReferenceException e) {
System.out.println("Attempting to recover from StaleElementReferenceException ...");
return getStaleElem(by, driver);
} catch (NoSuchElementException ele) {
System.out.println("Attempting to recover from NoSuchElementException ...");
return getStaleElem(by, driver);
}
}
With Java 7, even a single multicatch block would be sufficient:
WebElement getStaleElem(By by, WebDriver driver) {
try {
return driver.findElement(by);
} catch (StaleElementReferenceException | NoSuchElementException e) {
System.out.println("Attempting to recover from " + e.getClass().getSimpleName() + "...");
return getStaleElem(by, driver);
}
}
This way, you can greatly reduce the amount of code you need to maintain.
I solve this by 1. keeping the stale element and poll it until it throws an exception, and then 2. wait until the element is visible again.
boolean isStillOnOldPage = true;
while (isStillOnOldPage) {
try {
theElement.getAttribute("whatever");
} catch (StaleElementReferenceException e) {
isStillOnOldPage = false;
}
}
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("theElementId")));
If you are trying to Click on link, that taking you to new page. After that navigating back and clicking on other links. They below code may help you.
public int getNumberOfElementsFound(By by) {
return driver.findElements(by).size();
}
public WebElement getElementWithIndex(By by, int pos) {
return driver.findElements(by).get(pos);
}
/**click on each link */
public void getLinks()throws Exception{
try {
List<WebElement> componentList = driver.findElements(By.tagName("a"));
System.out.println(componentList.size());
for (WebElement component : componentList)
{
//click1();
System.out.println(component.getAttribute("href"));
}
int numberOfElementsFound = getNumberOfElementsFound(By.tagName("a"));
for (int pos = 0; pos < numberOfElementsFound; pos++) {
if (getElementWithIndex(By.tagName("a"), pos).isDisplayed()){
getElementWithIndex(By.tagName("a"), pos).click();
Thread.sleep(200);
driver.navigate().back();
Thread.sleep(200);
}
}
}catch (Exception e){
System.out.println("error in getLinks "+e);
}
}
Solutions to resolve them:
Storing locators to your elements instead of references
driver = webdriver.Firefox();
driver.get("http://www.github.com");
search_input = lambda: driver.find_element_by_name('q');
search_input().send_keys('hello world\n');
time.sleep(5);
search_input().send_keys('hello frank\n') // no stale element exception
Leverage hooks in the JS libraries used
# Using Jquery queue to get animation queue length.
animationQueueIs = """
return $.queue( $("#%s")[0], "fx").length;
""" % element_id
wait_until(lambda: self.driver.execute_script(animationQueueIs)==0)
Moving your actions into JavaScript injection
self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
Proactively wait for the element to go stale
# Wait till the element goes stale, this means the list has updated
wait_until(lambda: is_element_stale(old_link_reference))
This solution, which worked for me
When a Stale Element Exception occurs!!
Stale element exception can happen when the libraries supporting those textboxes/ buttons/ links has changed which means the elements are same but the reference has now changed in the website without affecting the locators. Thus the reference which we stored in our cache including the library reference has now become old or stale because the page has been refreshed with updated libraries.
for(int j=0; j<5;j++)
try {
WebElement elementName=driver.findElement(By.xpath(“somexpath”));
break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying :: ” + e.getMessage());
}
elementName.sendKeys(“xyz”);
For Fitnesse you can use:
|start |Smart Web Driver| selenium.properties|
#Fixture(name = "Smart Web Driver")
public class SmartWebDriver extends SlimWebDriver {
private final static Logger LOG = LoggerFactory.getLogger(SmartWebDriver.class);
/**
* Constructs a new SmartWebDriver.
*/
#Start(name = "Start Smart Web Driver", arguments = {"configuration"}, example = "|start |Smart Web Driver| selenium.properties|")
public SmartWebDriver(String configuration) {
super(configuration);
}
/**
* Waits for an element to become invisible (meaning visible and width and height != 0).
*
* #param locator the locator to use to find the element.
*/
#Command(name = "smartWaitForNotVisible", arguments = {"locator"}, example = "|smartWaitForNotVisible; |//path/to/input (of css=, id=, name=, classname=, link=, partiallink=)|")
public boolean smartWaitForNotVisible(String locator) {
try {
waitForNotVisible(locator);
} catch (StaleElementReferenceException sere) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a StaleElementReferenceException occurred, trying to continue...", locator);
} catch (NoSuchElementException ele) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a NoSuchElementException occurred, trying to continue...", locator);
} catch (AssertionError ae) {
if (ae.getMessage().contains("No element found")) {
LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a AssertionError occurred, trying to continue...", locator);
} else {
throw ae;
}
}
return true;
}
}
https://www.swtestacademy.com/selenium-wait-javascript-angular-ajax/ here is a good article about dynamic waiter strategies.
Your problem is not waiting properly all the ajax, jquery or angular calls.
Then you end up with StaleElementException.
If your approach is to use Try-Catch mechanism, I guess it has a flaw. You shouldn't rely on that structure as you'll never know it's gonna work in the catch clause.
Selenium gives you the opportunity to make javascript calls.
You can execute
"return jQuery.active==0"
return
angular.element(document).injector().get('$http').pendingRequests.length
=== 0"
"return document.readyState"
"return angular.element(document).injector() === undefined"
commands just to check the existence and states of those calls.
You can do that before any findBy operation so you always work with the latest page

Wait for page load in Selenium

How do you make Selenium 2.0 wait for the page to load?
You can also check pageloaded using following code
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Use class WebDriverWait
Also see here
You can expect to show some element. something like in C#:
WebDriver _driver = new WebDriver();
WebDriverWait _wait = new WebDriverWait(_driver, new TimeSpan(0, 1, 0));
_wait.Until(d => d.FindElement(By.Id("Id_Your_UIElement")));
If you set the implicit wait of the driver, then call the findElement method on an element you expect to be on the loaded page, the WebDriver will poll for that element until it finds the element or reaches the time out value.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
source: implicit-waits
In general, with Selenium 2.0 the web driver should only return control to the calling code once it has determined that the page has loaded. If it does not, you can call waitforelemement, which cycles round calling findelement until it is found or times out (time out can be set).
Ruby implementation:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
#driver.execute_script("return document.readyState;") == "complete"
}
All of these solutions are OK for specific cases, but they suffer from at least one of a couple of possible problems:
They are not generic enough -- they want you to know, ahead of time, that some specific condition will be true of the page you are going to (eg some element will be displayed)
They are open to a race condition where you use an element that is actually present on the old page as well as the new page.
Here's my attempt at a generic solution that avoids this problem (in Python):
First, a generic "wait" function (use a WebDriverWait if you like, I find them ugly):
def wait_for(condition_function):
start_time = time.time()
while time.time() < start_time + 3:
if condition_function():
return True
else:
time.sleep(0.1)
raise Exception('Timeout waiting for {}'.format(condition_function.__name__))
Next, the solution relies on the fact that selenium records an (internal) id-number for all elements on a page, including the top-level <html> element. When a page refreshes or loads, it gets a new html element with a new ID.
So, assuming you want to click on a link with text "my link" for example:
old_page = browser.find_element_by_tag_name('html')
browser.find_element_by_link_text('my link').click()
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
For more Pythonic, reusable, generic helper, you can make a context manager:
from contextlib import contextmanager
#contextmanager
def wait_for_page_load(browser):
old_page = browser.find_element_by_tag_name('html')
yield
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
And then you can use it on pretty much any selenium interaction:
with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()
I reckon that's bulletproof! What do you think?
More info in a blog post about it here
You may remove the System.out line. It is added for debug purposes.
WebDriver driver_;
public void waitForPageLoad() {
Wait<WebDriver> wait = new WebDriverWait(driver_, 30);
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
System.out.println("Current Window State : "
+ String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")));
return String
.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"))
.equals("complete");
}
});
}
Here is a Java 8 version of the currently most upvoted answer:
WebDriverWait wait = new WebDriverWait(myDriver, Duration.ofSeconds(15));
wait.until(webDriver -> "complete".equals(((JavascriptExecutor) webDriver)
.executeScript("return document.readyState")));
Where myDriver is a WebDriver object (declared earlier).
Note: Be aware that this method (document.readyState) only checks the DOM.
You can also use the class: ExpectedConditions to explicitly wait for an element to show up on the webpage before you can take any action further actions
You can use the ExpectedConditions class to determine if an element is visible:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
See ExpectedConditions class Javadoc for list of all conditions you are able to check.
Imran's answer rehashed for Java 7:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState"
).equals("complete");
}
});
This seems to be a serious limitation of WebDriver. Obviously waiting for an element will not imply the page being loaded, in particular the DOM can be fully build (onready state) whereby JS is still executing and CSS and images are still loading.
I believe the simplest solution is to set a JS variable upon the onload event after everything is initialized and check and wait for this JS variable in Selenium.
If you want to wait for a specific element to load, you can use the isDisplayed() method on a RenderedWebElement :
// Sleep until the div we want is visible or 5 seconds is over
long end = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < end) {
// Browsers which render content (such as Firefox and IE) return "RenderedWebElements"
RenderedWebElement resultsDiv = (RenderedWebElement) driver.findElement(By.className("gac_m"));
// If results have been returned, the results are displayed in a drop down.
if (resultsDiv.isDisplayed()) {
break;
}
}
(Example from The 5 Minute Getting Started Guide)
Man all these answers require too much code. This should be a simple thing as its pretty common.
Why not just inject some simple Javascript with the webdriver and check.
This is the method I use in my webscraper class. The Javascript is pretty basic even if you don't know it.
def js_get_page_state(self):
"""
Javascript for getting document.readyState
:return: Pages state. See doc link below.
"""
ready_state = self.driver.execute_script('return document.readyState')
if ready_state == 'loading':
self.logger.info("Loading Page...")
elif ready_state == 'interactive':
self.logger.info("Page is interactive")
elif ready_state == 'complete':
self.logger.info("The page is fully loaded!")
return ready_state
More Info in "Document.readyState" of MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
Explicitly wait or conditional wait in this wait until given this condition.
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
This will wait for every web element for 60 seconds.
Use implicitly wait for wait of every element on page till that given time.
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
This will wait for every web element for 60 seconds.
I'm surprised that predicates weren't the first choice as you typically know what element(s) you will next interact with on the page you're waiting to load. My approach has always been to build out predicates/functions like waitForElementByID(String id) and waitForElemetVisibleByClass(String className), etc. and then use and reuse these wherever I need them, be it for a page load or page content change I'm waiting on.
For example,
In my test class:
driverWait.until(textIsPresent("expectedText");
In my test class parent:
protected Predicate<WebDriver> textIsPresent(String text){
final String t = text;
return new Predicate<WebDriver>(){
public boolean apply(WebDriver driver){
return isTextPresent(t);
}
};
}
protected boolean isTextPresent(String text){
return driver.getPageSource().contains(text);
}
Though this seems like a lot, it takes care of checking repeatedly for you
and the interval for how often to check can be set along with the ultimate
wait time before timing out. Also, you will reuse such methods.
In this example, the parent class defined and initiated the WebDriver driver and the WebDriverWait driverWait.
I hope this helps.
Use implicitly wait for wait of every element on page till given time.
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
this wait for every element on page for 30 sec.
Another wait is Explicitly wait or conditional wait in this wait until given condition.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
In id give static element id which is diffidently display on the page, as soon as page is load.
The best way to wait for page loads when using the Java bindings for WebDriver is to use the Page Object design pattern with PageFactory. This allows you to utilize the AjaxElementLocatorFactory which to put it simply acts as a global wait for all of your elements. It has limitations on elements such as drop-boxes or complex javascript transitions but it will drastically reduce the amount of code needed and speed up test times. A good example can be found in this blogpost. Basic understanding of Core Java is assumed.
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
Call below Function in your script , this will wait till page is not loaded using javascript
public static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
NodeJS Solution:
In Nodejs you can get it via promises...
If you write this code, you can be sure that the page is fully loaded when you get to the then...
driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));
If you write this code, you will navigate, and selenium will wait 3 seconds...
driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...
From Selenium Documentation (Nodejs):
this.get( url ) → Thenable<undefined>
Schedules a command to navigate to the given URL.
Returns a promise that will be resolved when the document has finished loading.
You can use the below existing method to set the pageLoadTimeout. In below example if the page is taking more than 20 seconds to load, then it will throw an exception of page reload:
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
/**
* Call this method before an event that will change the page.
*/
private void beforePageLoad() {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("document.mpPageReloaded='notYet';");
}
/**
* Call this method after an event that will change the page.
*
* #see #beforePageLoad
*
* Waits for the previous page to disappear.
*/
private void afterPageLoad() throws Exception {
(new WebDriverWait(driver, 10)).until(new Predicate<WebDriver>() {
#Override
public boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
Object obj = js.executeScript("return document.mpPageReloaded;");
if (obj == null) {
return true;
}
String str = (String) obj;
if (!str.equals("notYet")) {
return true;
}
return false;
}
});
}
You can change from the document to an element, in the case of where only part of a document is being changed.
This technique was inspired by the answer from sincebasic.
SeleniumWaiter:
import com.google.common.base.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
public class SeleniumWaiter {
private WebDriver driver;
public SeleniumWaiter(WebDriver driver) {
this.driver = driver;
}
public WebElement waitForMe(By locatorname, int timeout){
WebDriverWait wait = new WebDriverWait(driver, timeout);
return wait.until(SeleniumWaiter.presenceOfElementLocated(locatorname));
}
public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {
// TODO Auto-generated method stub
return new Function<WebDriver, WebElement>() {
#Override
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
};
}
}
And to you use it:
_waiter = new SeleniumWaiter(_driver);
try {
_waiter.waitForMe(By.xpath("//..."), 10);
}
catch (Exception e) {
// Error
}
You can explicitly wait for an element to show up on the webpage before you can take any action (like element.click()):
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>() {
#Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}
}
);
This is what I used for a similar scenario and it works fine.
My simple way:
long timeOut = 5000;
long end = System.currentTimeMillis() + timeOut;
while (System.currentTimeMillis() < end) {
if (String.valueOf(
((JavascriptExecutor) driver)
.executeScript("return document.readyState"))
.equals("complete")) {
break;
}
}
You can use this snippet of code for the page to load:
IWait wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver,TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Or you can use waiter for any element to be loaded and become visible/clickable on that page, most probably which is going to be load at the end of loading like:
Wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath(xpathOfElement));
var element = GlobalDriver.FindElement(By.XPath(xpathOfElement));
var isSucceededed = element != null;
The best way I've seen is to utilize the stalenessOf ExpectedCondition, to wait for the old page to become stale.
Example:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
It'll wait for ten seconds for the old HTML tag to become stale, and then throw an exception if it doesn't happen.
I use node + selenium-webdriver(which version is 3.5.0 now). what I do for this is:
var webdriver = require('selenium-webdriver'),
driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
return state === 'complete';
}))
You can use wait. there are basically 2 types of wait in selenium
Implicit wait
Explicit wait
- Implicit wait
This is very simple please see syntax below:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- Explicit wait
Explicitly wait or conditional wait in this wait until given condition is occurred.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
You can use other properties like visblityOf(), visblityOfElement()
If someone uses selenide:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
More Conditions can be found here:
http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
In my case , I used the following to know the page load status. In our application loading gif(s) are present and, I listen to them as follows to eliminate unwanted wait time in the script.
public static void processing(){
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
}
Where the xpath locates the gif in the HTML DOM.
After this, You may also implement your action methods Click.
public static void click(WebElement elementToBeClicked){
WebDriverWait wait = new WebDriverWait(driver, 45);
wait.until(ExpectedConditions.visibilityOf(element));
wait.until(ExpectedConditions.elementToBeClickable(element));
wait.ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class); elementToBeClicked.click();
}

Categories

Resources