Testing AngularJS with Selenium Testing AngularJS with Selenium selenium selenium

Testing AngularJS with Selenium


This will wait for page loads / jquery.ajax (if present) and $http calls, and any accompanying digest/render cycle, throw it in a utility function and wait away.

/* C# Example var pageLoadWait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(timeout));            pageLoadWait.Until<bool>(                (driver) =>                {                    return (bool)JS.ExecuteScript(@"*/try {  if (document.readyState !== 'complete') {    return false; // Page not loaded yet  }  if (window.jQuery) {    if (window.jQuery.active) {      return false;    } else if (window.jQuery.ajax && window.jQuery.ajax.active) {      return false;    }  }  if (window.angular) {    if (!window.qa) {      // Used to track the render cycle finish after loading is complete      window.qa = {        doneRendering: false      };    }    // Get the angular injector for this app (change element if necessary)    var injector = window.angular.element('body').injector();    // Store providers to use for these checks    var $rootScope = injector.get('$rootScope');    var $http = injector.get('$http');    var $timeout = injector.get('$timeout');    // Check if digest    if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {      window.qa.doneRendering = false;      return false; // Angular digesting or loading data    }    if (!window.qa.doneRendering) {      // Set timeout to mark angular rendering as finished      $timeout(function() {        window.qa.doneRendering = true;      }, 0);      return false;    }  }  return true;} catch (ex) {  return false;}/*");});*/


Create a new class that lets you figure out whether your website using AngularJS has finished making AJAX calls, as follows:

import org.openqa.selenium.JavascriptExecutor;import org.openqa.selenium.WebDriver;import org.openqa.selenium.support.ui.ExpectedCondition;public class AdditionalConditions {    public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {        return new ExpectedCondition<Boolean>() {            @Override            public Boolean apply(WebDriver driver) {                return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)").toString());            }        };    }}

You can use it anywhere in the your code by using the following code:

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);wait.until(AdditionalConditions.angularHasFinishedProcessing()));


We have had a similar issue where our in house framework is being used to test multiple sites, some of these are using JQuery and some are using AngularJS (and 1 even has a mixture!). Our framework is written in C# so it was important that any JScript being executed was done in minimal chunks (for debugging purposes). It actually took a lot of the above answers and mashed them together (so credit where credit is due @npjohns). Below is an explanation of what we did:

The following returns a true / false if the HTML DOM has loaded:

        public bool DomHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)    {        var hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");        while (hasThePageLoaded == null || ((string)hasThePageLoaded != "complete" && timeout > 0))        {            Thread.Sleep(100);            timeout--;            hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");            if (timeout != 0) continue;            Console.WriteLine("The page has not loaded successfully in the time provided.");            return false;        }        return true;    }

Then we check whether JQuery is being used:

public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)    {        var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");        return (bool)isTheSiteUsingJQuery;    }

If JQuery is being used we then check that it's loaded:

public bool JqueryHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)        {                var hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");                while (hasTheJQueryLoaded == null || (!(bool) hasTheJQueryLoaded && timeout > 0))                {                    Thread.Sleep(100);                timeout--;                    hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");                    if (timeout != 0) continue;                    Console.WriteLine(                        "JQuery is being used by the site but has failed to successfully load.");                    return false;                }                return (bool) hasTheJQueryLoaded;        }

We then do the same for AngularJS:

    public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)    {        string UsingAngular = @"if (window.angular){        return true;        }";                    var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);        return (bool) isTheSiteUsingAngular;    }

If it is being used then we check that it has loaded:

public bool AngularHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)        {    string HasAngularLoaded =        @"return (window.angular !== undefined) && (angular.element(document.body).injector() !== undefined) && (angular.element(document.body).injector().get('$http').pendingRequests.length === 0)";                var hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);                while (hasTheAngularLoaded == null || (!(bool)hasTheAngularLoaded && timeout > 0))                {                    Thread.Sleep(100);                    timeout--;                    hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);                    if (timeout != 0) continue;                    Console.WriteLine(                        "Angular is being used by the site but has failed to successfully load.");                    return false;                }                return (bool)hasTheAngularLoaded;        }

After we check that the DOM has successfully loaded, you can then use these bool values to do custom waits:

    var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));    var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));