33

I am writing some Java Webdriver code to automate my application. How can I correctly check whether the page has been loaded or not? The application has some Ajax calls, too.

I have declared an implicit wait for WebDriver.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Raghubansh
  • 331
  • 1
  • 3
  • 6

10 Answers10

48

Selenium does it for you. Or at least it tries its best. Sometimes it falls short, and you must help it a little bit. The usual solution is Implicit Wait which solves most of the problems.

If you really know what you're doing, and why you're doing it, you could try to write a generic method which would check whether the page is completely loaded. However, it can't be done for every web and for every situation.


Related question: Selenium WebDriver : Wait for complex page with JavaScript(JS) to load, see my answer there.

Shorter version: You'll never be sure.

The "normal" load is easy - document.readyState. This one is implemented by Selenium, of course. The problematic thing are asynchronous requests, AJAX, because you can never tell whether it's done for good or not. Most of today's webpages have scripts that run forever and poll the server all the time.

The various things you could do are under the link above. Or, like 95% of other people, use Implicit Wait implicity and Explicit Wait + ExpectedConditions where needed.

E.g. after a click, some element on the page should become visible and you need to wait for it:

WebDriverWait wait = new WebDriverWait(driver, 10);  // you can reuse this one

WebElement elem = driver.findElement(By.id("myInvisibleElement"));
elem.click();
wait.until(ExpectedConditions.visibilityOf(elem));
Community
  • 1
  • 1
Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
  • Thanks Slanec, i can add explicit wait too but i am not sure about the points it will take more load time.yes you are write most of the applicarion are keep pulling the information form server. so well in that case how can i implement DOM. – Raghubansh Jun 12 '12 at 20:35
  • 1
    @Raghubansh I have no idea what you just said :). However, I have edited the answer a little to be more clear. If you have a concrete problem to solve, start a question about it, post your effort and we'll see what we can do about it :). – Petr Janeček Jun 12 '12 at 22:29
  • 1
    Thanks! Just one note, your sample code says `elen` at one point instead of `elem`. – Dawn Drescher Sep 26 '14 at 12:41
  • 1
    Although this is an old question, I should note that I am under the impression that checking of `document.readyState` is not happening by default when changing focus to an iframe. – LetsPlayYahtzee Oct 04 '16 at 14:14
  • @LetsPlayYahtzee That might actually be true. I honestly don't know anymore, I haven't been working with WebDriver for more than 3 years now. I'm very sure that it checks for it after a click and/or get. – Petr Janeček Oct 04 '16 at 15:04
  • The problem is that sometimes it returns ready state of previous page if the click is processed slowly. So you are testing previous page. I need to check first that the loading started and then that the loading finished. – Tomas Kubes Nov 15 '16 at 16:00
  • @qub1n I recall doing that by waiting for a new element from the new page to appear. – Petr Janeček Nov 15 '16 at 16:30
  • I want to wait until my page completely loads and I can find a specific iframe. What would be the Expected Condition in this case – user2335580 Jul 09 '17 at 03:02
  • @user2335580 Wait for an element in the specific iframe. Any element that you're interested in. – Petr Janeček Jul 09 '17 at 07:56
  • Cant I search if a particular iframe is loaded? Say my element ABC is within iframe XYZ, when my page loads how can I check if my iframe XYZ is loaded? Some sample code will be helpful. Thanks – user2335580 Jul 09 '17 at 15:21
6

Simple ready2use snippet, working perfectly for me

static void waitForPageLoad(WebDriver wdriver) {
    WebDriverWait wait = new WebDriverWait(wdriver, 60);

    Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver input) {
            return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
        }

    };
    wait.until(pageLoaded);
}
René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • Most cases here waits for a know element. But sometimes you want to protect behavior on pages that you don´t know. in my case a javascript slow loading is causing trouble. so this one for me is the best choice over all when you are accessing pages you don´t know. The method is indeed strong. So i just checked for document.readyState on local wait – LeoPucciBr May 13 '17 at 18:45
  • What are your observations, Leo? – Narendra Chandratre May 15 '17 at 10:50
  • There are some pages that adds the links on the elements after loading them (ajax). So when the page starts loading, the element appears without the correct link, and the driver will drive incorrectly. So to me the best option is to wait for document ready state before hitting some link public void EsperaPagina(IWebDriver driver) { WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20.0)); wait.Until(delegate (IWebDriver d){return (Boolean)((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"); });} – LeoPucciBr May 20 '17 at 23:15
  • 1
    this answer just duplicates selenium's internal functionality – Corey Goldberg Jul 14 '19 at 01:48
4

I know this post is old. But after gathering all code from above I made a nice method (solution) to handle ajax running and regular pages. The code is made for C# only (since Selenium is definitely a best fit for C# Visual Studio after a year of messing around).

The method is used as an extension method, which means to put it simple; that you can add more functionality (methods) in this case, to the object IWebDriver. Important is that you have to define: 'this' in the parameters to make use of it.

The timeout variable is the amount of seconds for the webdriver to wait, if the page is not responding. Using 'Selenium' and 'Selenium.Support.UI' namespaces it is possible to execute a piece of javascript that returns a boolean, whether the document is ready (complete) and if jQuery is loaded. If the page does not have jQuery then the method will throw an exception. This exception is 'catched' by error handling. In the catch state the document will only be checked for it's ready state, without checking for jQuery.

public static void WaitUntilDocumentIsReady(this IWebDriver driver, int timeoutInSeconds) {
    var javaScriptExecutor = driver as IJavaScriptExecutor;
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));

    try {
        Func<IWebDriver, bool> readyCondition = webDriver => (bool)javaScriptExecutor.ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)");
        wait.Until(readyCondition);
    } catch(InvalidOperationException) {
        wait.Until(wd => javaScriptExecutor.ExecuteScript("return document.readyState").ToString() == "complete");
    }
}
Bedirhan
  • 89
  • 1
  • 5
  • Thank you for posting here even though it is an old thread. I was having crazy issues firing off Selenium tests on an EC2 and the jquery part fixed my problem. THANK YOU! – J-Roel Feb 15 '18 at 18:48
3

You can set a JavaScript variable in your WepPage that gets set once it's been loaded. You could put it anywhere, but if you're using jQuery, $(document).onReady isn't a bad place to start. If not, then you can put it in a <script> tag at the bottom of the page.

The advantage of this method as opposed to checking for element visibility is that you know the exact state of the page after the wait statement executes.

MyWebPage.html

... All my page content ...
<script> window.TestReady = true; </script></body></html>

And in your test (C# example):

// The timespan determines how long to wait for any 'condition' to return a value
// If it is exceeded an exception is thrown.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5.0));

// Set the 'condition' as an anonymous function returning a boolean
wait.Until<Boolean>(delegate(IWebDriver d)
{
    // Check if our global variable is initialized by running a little JS
    return (Boolean)((IJavaScriptExecutor)d).ExecuteScript("return typeof(window.TestReady) !== 'undefined' && window.TestReady === true");
});
Community
  • 1
  • 1
jmathew
  • 1,522
  • 17
  • 29
  • 1
    That would still have a race condition. You're on page A (with the token variable already set), you click on a link to page B and immediately after tell it to wait for the variable to be set (supposedly on page B), but if the request takes a couple of seconds to start, it'll find the existing page A variable and happily proceed. – Edson Medina Mar 04 '16 at 17:10
  • Ah at the time I was working with a single page application. So that issue never presented itself. – jmathew Mar 04 '16 at 19:25
1

Recently, when I was dealing with an AJAX application/RIA, I was having the same issue! And I used implicit wait, with a time of around 90 seconds. It waits, till the element is available...So, what we can do to make sure, that page gets loaded completely is,

add a boolean statement, checking for whether the condition(a particular part of the element), is present and assign it to a variable, check for the condition and only when it is true, " do the necessary actions!"...In this way, I figured out, both waits could be used...

Ex:

@Before

{ implicit wait statement}

@Test

{

boolean tr1=Driver.findElement(By.xpath("xx")).isEnabled/isDisplayed;

if (tr1==true && ____)//as many conditions, to make sure, the page is loaded

{

//do the necessary set of actions...
driver.findElement(By.xpath("yy")).click();

}

}

Hope this helps!! It is in implementation stage, for me too...

Manikandan
  • 417
  • 4
  • 8
  • 18
0

Here is how I would fix it, using a code snippet to give you a basic idea:

public class IFrame1 extends LoadableComponent<IFrame1> {

    private RemoteWebDriver driver;

    @FindBy(id = "iFrame1TextFieldTestInputControlID" ) public WebElement iFrame1TextFieldInput;
    @FindBy(id = "iFrame1TextFieldTestProcessButtonID" ) public WebElement copyButton;

    public IFrame1( RemoteWebDriver drv ) {
        super();
        this.driver = drv;
        this.driver.switchTo().defaultContent();
        waitTimer(1, 1000);
        this.driver.switchTo().frame("BodyFrame1");
        LOGGER.info("IFrame1 constructor...");
    }

    @Override
    protected void isLoaded() throws Error {        
        LOGGER.info("IFrame1.isLoaded()...");
        PageFactory.initElements( driver, this );
        try {
            assertTrue( "Page visible title is not yet available.", 
                    driver.findElementByCssSelector("body form#webDriverUnitiFrame1TestFormID h1")
                    .getText().equals("iFrame1 Test") );
        } catch ( NoSuchElementException e) {
            LOGGER.info("No such element." );
            assertTrue("No such element.", false);
        }
    }

    /**
     * Method: load
     * Overidden method from the LoadableComponent class.
     * @return  void
     * @throws  null
     */
    @Override
    protected void load() {
        LOGGER.info("IFrame1.load()...");
        Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
                .withTimeout(30, TimeUnit.SECONDS)
                .pollingEvery(5, TimeUnit.SECONDS)
                .ignoring( NoSuchElementException.class ) 
                .ignoring( StaleElementReferenceException.class ) ;
        wait.until( ExpectedConditions.presenceOfElementLocated( 
                By.cssSelector("body form#webDriverUnitiFrame1TestFormID h1") ) );
    }
....
djangofan
  • 28,471
  • 61
  • 196
  • 289
0

Below is some code from my BasePageObject class for waits:

public void waitForPageLoadAndTitleContains(int timeout, String pageTitle) {
    WebDriverWait wait = new WebDriverWait(driver, timeout, 1000);
    wait.until(ExpectedConditions.titleContains(pageTitle));
}

public void waitForElementPresence(By locator, int seconds) {
    WebDriverWait wait = new WebDriverWait(driver, seconds);
    wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}

public void jsWaitForPageToLoad(int timeOutInSeconds) {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    String jsCommand = "return document.readyState";

    // Validate readyState before doing any waits
    if (js.executeScript(jsCommand).toString().equals("complete")) {
        return;
    }

    for (int i = 0; i < timeOutInSeconds; i++) {
        TimeManager.waitInSeconds(3);
        if (js.executeScript(jsCommand).toString().equals("complete")) {
            break;
        }
    }
}

   /**
     * Looks for a visible OR invisible element via the provided locator for up
     * to maxWaitTime. Returns as soon as the element is found.
     *
     * @param byLocator
     * @param maxWaitTime - In seconds
     * @return
     *
     */
    public WebElement findElementThatIsPresent(final By byLocator, int maxWaitTime) {
        if (driver == null) {
            nullDriverNullPointerExeption();
        }
        FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(maxWaitTime, java.util.concurrent.TimeUnit.SECONDS)
                .pollingEvery(200, java.util.concurrent.TimeUnit.MILLISECONDS);

        try {
            return wait.until((WebDriver webDriver) -> {
                List<WebElement> elems = driver.findElements(byLocator);
                if (elems.size() > 0) {
                    return elems.get(0);
                } else {
                    return null;
                }
            });
        } catch (Exception e) {
            return null;
        }
    }

Supporting methods:

     /**
     * Gets locator.
     *
     * @param fieldName
     * @return
     */
    public By getBy(String fieldName) {
        try {
            return new Annotations(this.getClass().getDeclaredField(fieldName)).buildBy();
        } catch (NoSuchFieldException e) {
            return null;
        }
    }
Mykola
  • 352
  • 1
  • 9
0

You can get the HTML of the website with driver.getPageSource(). If the html does not change in a given interval of time this means that the page is done loading. One or two seconds should be enough. If you want to speed things up you can just compare the lenght of the two htmls. If their lenght is equal the htmls should be equal and that means the page is fully loaded. The JavaScript solution did not work for me.

0

The solution is using Implicit Wait which will solve your problems of page load.

WebDriverWait waitForElement = new WebDriverWait(driver, 15);
WebElement element = driver.findElement(By.id("myElement"));
element.click(); 
wait.until(ExpectedConditions.visibilityOf(element));
Kamran
  • 669
  • 6
  • 9
-4

You can take a screenshot and save the rendered page in a location and you can check the screenshot if the page loaded completely without broken images