Selenium Webdriver wait for JavaScript JQuery and Angular

Hi all, during the last two weeks I was dealing with the best solution to wait for both JQuery, Angular and JavaScript (JS) in my Selenium (Java) test codes and finally, I found a significantly stable solution without silly sleep() statements to share with you.

In this period, I searched maybe more than 20 articles and sites to find best and unique solutions and in this article, I combined all solutions in only one method. This is “WaitJQueryAngular()“. When you need to wait for an asynchronous wait, you can use the below methods as your project type.

Then, my solution upgraded by Colm Murphy and he added Angular 5 synchronization method as well. Thanks to him. I updated code snippet on 18 Feb 2019.

I know that you wonder to know what it does? First, it waits JQuery is Ready? and then waits Angular is Ready? and if JQuery or Angular is ready? Then it checks JS is ready? If all of them are ready then the code will continue to run the next statements. Also, I added a control point that checks JQuery and Angular are defined or not. It is important that if the any of them is not defined in the page, code skips asynchronous wait statements. If this control does not exist, we will get a JS error.

You can just add below methods in your code and use “WaitJQueryAngular()” for an asynchronous wait in your test automation codes.

  • Add below JSWaiter utility class into your project.
  • Then, send WebDriver object to JSWaiter Class in a convenient place in your project by adding this line: “JSWaiter.setDriver(driver);”
  • Finally, you can use any of the methods below in your test code:
    • JSWaiter.waitJQueryAngular(); –> Both waits JQuery, Angular, JS
    • waitUntilAngularReady(); –> It only waits Angular and JS
    • waitUntilJQueryReady(); –> It only waits JQuery and JS
    • waitUntilAngular5Ready(); –> Waits for Angular 5 projects

Note: I added waitUntilJSReady(); call both in waitUntilJQueryReady() and waitUntilAngularReady() methods. In this way, if your test site only comprises of Angular or JQuery, you can just use the relevant method to wait asynchronous operations.

Note: You should combine this asynchronous wait solution with the synchronous explicit wait statements to get highest stability in your tests. If you face with a problem, please let me know.

Github Link of a Sample Project: https://github.com/swtestacademy/JSWaiter (It is not updated. Below code snippet is the latest version. You should update JSWaiter class when u clone this project.)

JQuery, Angular, and JavaScript Waiter

public class JSWaiter {

    private static WebDriver jsWaitDriver;
    private static WebDriverWait jsWait;
    private static JavascriptExecutor jsExec;

    //Get the driver 
    public static void setDriver (WebDriver driver) {
        jsWaitDriver = driver;
        jsWait = new WebDriverWait(jsWaitDriver, 10);
        jsExec = (JavascriptExecutor) jsWaitDriver;
    }

   private void ajaxComplete() {
        jsExec.executeScript("var callback = arguments[arguments.length - 1];"
            + "var xhr = new XMLHttpRequest();" + "xhr.open('GET', '/Ajax_call', true);"
            + "xhr.onreadystatechange = function() {" + "  if (xhr.readyState == 4) {"
            + "    callback(xhr.responseText);" + "  }" + "};" + "xhr.send();");
    }

    private void waitForJQueryLoad() {
        try {
            ExpectedCondition<Boolean> jQueryLoad = driver -> ((Long) ((JavascriptExecutor) this.driver)
                .executeScript("return jQuery.active") == 0);

            boolean jqueryReady = (Boolean) jsExec.executeScript("return jQuery.active==0");

            if (!jqueryReady) {
                jsWait.until(jQueryLoad);
            }
        } catch (WebDriverException ignored) {
        }
    }

    private void waitForAngularLoad() {
        String angularReadyScript = "return angular.element(document).injector().get('$http').pendingRequests.length === 0";
        angularLoads(angularReadyScript);
    }

    private void waitUntilJSReady() {
        try {
            ExpectedCondition<Boolean> jsLoad = driver -> ((JavascriptExecutor) this.driver)
                .executeScript("return document.readyState").toString().equals("complete");

            boolean jsReady = jsExec.executeScript("return document.readyState").toString().equals("complete");

            if (!jsReady) {
                jsWait.until(jsLoad);
            }
        } catch (WebDriverException ignored) {
        }
    }

    private void waitUntilJQueryReady() {
        Boolean jQueryDefined = (Boolean) jsExec.executeScript("return typeof jQuery != 'undefined'");
        if (jQueryDefined) {
            poll(20);

            waitForJQueryLoad();

            poll(20);
        }
    }

    public void waitUntilAngularReady() {
        try {
            Boolean angularUnDefined = (Boolean) jsExec.executeScript("return window.angular === undefined");
            if (!angularUnDefined) {
                Boolean angularInjectorUnDefined = (Boolean) jsExec.executeScript("return angular.element(document).injector() === undefined");
                if (!angularInjectorUnDefined) {
                    poll(20);

                    waitForAngularLoad();

                    poll(20);
                }
            }
        } catch (WebDriverException ignored) {
        }
    }

    public void waitUntilAngular5Ready() {
        try {
            Object angular5Check = jsExec.executeScript("return getAllAngularRootElements()[0].attributes['ng-version']");
            if (angular5Check != null) {
                Boolean angularPageLoaded = (Boolean) jsExec.executeScript("return window.getAllAngularTestabilities().findIndex(x=>!x.isStable()) === -1");
                if (!angularPageLoaded) {
                    poll(20);

                    waitForAngular5Load();

                    poll(20);
                }
            }
        } catch (WebDriverException ignored) {
        }
    }

    private void waitForAngular5Load() {
        String angularReadyScript = "return window.getAllAngularTestabilities().findIndex(x=>!x.isStable()) === -1";
        angularLoads(angularReadyScript);
    }

    private void angularLoads(String angularReadyScript) {
        try {
            ExpectedCondition<Boolean> angularLoad = driver -> Boolean.valueOf(((JavascriptExecutor) driver)
                .executeScript(angularReadyScript).toString());

            boolean angularReady = Boolean.valueOf(jsExec.executeScript(angularReadyScript).toString());

            if (!angularReady) {
                jsWait.until(angularLoad);
            }
        } catch (WebDriverException ignored) {
        }
    }

    public void waitAllRequest() {
        waitUntilJSReady();
        ajaxComplete();
        waitUntilJQueryReady();
        waitUntilAngularReady();
        waitUntilAngular5Ready();
    }

    /**
     * Method to make sure a specific element has loaded on the page
     *
     * @param by
     * @param expected
     */
    public void waitForElementAreComplete(By by, int expected) {
        ExpectedCondition<Boolean> angularLoad = driver -> {
            int loadingElements = this.driver.findElements(by).size();
            return loadingElements >= expected;
        };
        jsWait.until(angularLoad);
    }

    /**
     * Waits for the elements animation to be completed
     * @param css
     */
    public void waitForAnimationToComplete(String css) {
        ExpectedCondition<Boolean> angularLoad = driver -> {
            int loadingElements = this.driver.findElements(By.cssSelector(css)).size();
            return loadingElements == 0;
        };
        jsWait.until(angularLoad);
    }

    private void poll(long milis) {
        try {
            Thread.sleep(milis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Notes:

– For java.net.SocketTimeoutException you can add below line before get() method. It will also wait for Page Load.

driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);

Thanks.

-Onur

66 thoughts on “Selenium Webdriver wait for JavaScript JQuery and Angular”

  1. HI Onur Baskirt,

    this class works with Selenium v2.53.1 but its throwing an error with Selenium 3.4.0

    Can you please help as my framework depends heavily on this class to wait for angular api calls
    Thanks…

    Reply
  2. Best solution ever. Many thanks.

    Also do you have a tutorial to setup an automation framework in real projects (with real world examples)
    preferably Page object Model ?

    Again Many Thanks.
    Ray

    Reply
  3. Will this work on Angular 1.5? Since I’m getting message in console “Angular injector is not defined on this site!”.

    Note: I’ve used method “waitJQueryAngular()”

    Reply
    • Thank you for your feedback. If I have time, I will find a solution for Angular2 and above soon. By the way, did you ever try Procractor or nightwatch.js? They maybe better solutions for angular2 and above.

      Reply
    • Hi Mukesh, I found a small library of WebDriver locators and more for AngularJS (v1.x) and Angular (v2.x +), for Java. They have taken JavaScript from Angular’s Protractor project. While ngWebDriver perfectly compliments the Java version of WebDriver, it has to pass JavaScript up to the browser to interoperate with Angular, and the Protractor project has done the hard work (including testing) to make that solid. You can use ngWebDriver today with the regular Java Selenium2/WebDriver libraries. Like Protractor, ngWebDriver works with Angular versions greater than 1.0.6/1.1.4, and is compatible with Angular 2 applications. Note that for Angular 2 apps, the binding and model locators are not supported. they recommend using by.css.

      new NgWebDriver(driver).waitForAngularRequestsToFinish();

      Do this if WebDriver can possibly run ahead of Angular’s ability finish it’s MVC stuff in your application.

      Library link: https://github.com/paul-hammant/ngWebDriver

      Reply
  4. Hi Onur. I would like to convert this code into Python, I have some problems. For example with angularLoad and angularReady, you basically execute the same script. Can I use only one angularReady and check if it is ready. Because in python there is no such cast ExpectedCondition like in Java.
    it would be really great if you by any chance have an example in Python. Thank you

    Reply
  5. Hi,For Mobile Automation, which type of wait you would recommend to use? I’m currently working on mobile automation and using “wait.until.elementvisibility…”
    Thanks!

    Reply
  6. Hi, I am using angular materiel menus(md-menu), there i have implemented your JSWaiter , still I am getting some TimeoutExceptions randomly.

    Reply
  7. Thank you for your fast reply. So I am free to use a modified version of this code in my own application.
    I had a similiar approach but it was as extensive and robust as yours. So far I haven’t had any problems but I keep your suggestion in mind.
    Thanks again.

    Reply
  8. Hello, I am using method waitForAngularLoad() fro above code. It is throwing error – ‘ unknown error: angular is not defined’.

    My application is build with AngularJS5.

    Can anyone help please..

    Reply
  9. Hello Sir,
    We are working on Angular 1.3 project. I am trying to automate using selenium webdriver and Java and new to this. Problem here is that we are caching everything. So in DOM it always resides.we are hiding using ng-hide etc. Core developers who did have left organization there and no one will be changing it.The main problem is that , till we get response (REST api service calls are called ) all elements are displayed. Once a response is received (status 200- if we try to see in network tab of the chrome browser) based on the outcome the elements are hidden or displayed.
    For example as soon as I enter a product code-> GET method with request url is sent. It takes time to get the response. Till that time all other elements like location/etc are displayed. So I can’t wait for the elements to be displayed nor I can force to sleep using thread.sleep as I don’t know how long will it take to get response and will slow down the script.I can’t even use implicit wait because the data entered may be invalid which i will come to know after getting response. If services returns message as invalid product then all the elements like location/code etc will be hidden and a pop up message is displayed.
    So I am kind of stuck and I stumbled on your site . I would like to know more about the code

    boolean angularReady = Boolean.valueOf(jsExec.executeScript(angularReadyScript).toString());
    Does it check and wait for pending request response? What I need is that till I get response(200) my script should be paused. Once I get the response based on the test data my DOM will change and thereafter it will be easy for me to manipulate by checking on the visibility of the elements
    Kindly help me on this matter
    Thanks in advance.

    Reply
  10. Hi Onur,
    This solution here looks really helpful for my current project. the only problem is that i am a C# , .Net person not able to convert the code to C# completly. Do you think you can help me here??

    Regards,Joe

    Reply
  11. This solution here looks really helpful for my current project. the only problem is that i am a python , .Net person not able to convert the code to python completly. Do you think you can help me here??

    Reply
  12. This is really helpful and working as expected for Java, Can you please convert and make repo for Selenium c# ? This is many of user looking for. As this is well-structured for Java, we need same for c#. I have referred SO link which you have provided, But can’t go through it.

    Reply
  13. Hi Onur, If we want to understand method defined in JSWaiter class, where to read basics to understand how it is implemented.

    Hope you understand my question

    Reply
  14. Thanks for the great wait class. I needed to make a couple adjustments to make it work.
    I needed to change all the places that said: this.driver to be jsWaitDriver. IntelliJ didn’t think this.driver existed.

    Also I couldn’t call the methods that were not static. So I changed all the methods to static.
    I noticed both of these things were fixed in initial release on Github.
    The angular5 method is awesome. I do this to wait for all types of elements.
    JSWaiter.waitAllRequest();

    Reply
  15. Selam Onur,

    Bizim projemiz VS Code üzerinde Node JS ile oluşturulmuş framework, bir uyguşlamada Ajaxın dolmasını beklemek için waitforJqueryLoad kodunu NodeJS e covert edemedim nasıl bir kod hazırlamalıyım yardımcı olursan çok sevinirim.

    Teşekkürler.

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.