Bring the auto Sync magic of Protractor to Selenium with Java

Its quite sometime, I have beProtractoren working with Protractor, an awesome framework for e2e automated testing of AngularJS applications. There are some facts about Protractor which make it stand out in the crowd , when looking for an automated testing solution for AngularJS applications. Among those remarkable features , one is automatic synchronisation.

What it means is, we need not to write additional wait statements in our code for page loading or any XHR request to complete. There is no need of Implicit or Explicit waits at all. Just write the code without bothering about loaders or any such page loading indicators.

How Protractor does it ?

For every command we write , for example click, enter something to text field etc , Protractor calls one function which is responsible to make sure that all the angular calls are finished/done in the browser before performing that command/user event. That method is – waitForAngular. We can call this method explicitly using the ‘browser’ object.

‘waitForAngular’ method holds the JS snippet , which gets executed with the browser and confirms that everything is finished loading.

What is inside ‘waitForAngular’ function?

Well, here is the snippet:

/**
 * Wait until Angular has finished rendering and has
 * no outstanding $http calls before continuing.
 *
 * Asynchronous.
 *
 * @param {string} rootSelector The selector housing an ng-app
 * @param {function(string)} callback callback. If a failure occurs, it will
 *     be passed as a parameter.
 */
functions.waitForAngular = function (rootSelector, callback) {
    var el = document.querySelector(rootSelector);

    try {
        if (!window.angular) {
            throw new Error('angular could not be found on the window');
        }
        if (angular.getTestability) {
            angular.getTestability(el).whenStable(callback);
        } else {
            if (!angular.element(el).injector()) {
                throw new Error('root element (' + rootSelector + ') has no injector.' +
                    ' this may mean it is not inside ng-app.');
            }
            angular.element(el).injector().get('$browser').
                notifyWhenNoOutstandingRequests(callback);
        }
    } catch (err) {
        callback(err.message);
    }
};

This is a Javascript snippet which gets executed against the application under test in the browser. As the application under test already holds references/links to the Angular library , so all the dependencies of the objects mentioned in the above method are resolved from the application dependencies.

How to achieve it using Selenium Java bindings?

As the concept behind the auto synchronisation is to execute the above JS against the application under test on browser, so we would be needing JavascriptExecutor for the same. So we are going to execute the same JS snippet (inside waitForAngular function) using JavascriptExecutor.

Here is the code snippet in Java :

/**
* Waits for the Angular calls to finish with browser
* This method is helpful for auto synchronization
*
* @return Pass/Fail
* @author Abhishek Swain
*/
public static String waitForAngular(){
    try{
        driver.manage().timeouts().setScriptTimeout(60, TimeUnit.SECONDS);
        driver.executeAsyncScript("var callback=arguments[arguments.length-1];"+" if (!window.angular) { throw new Error('angular could not be found on the window');}if (angular.getTestability) {angular.getTestability(angular.element(document.body)).whenStable(callback); } else { if (!angular.element(angular.element(document.body)).injector()) { throw new Error('root element (' + 'body' + ') has no injector.' +' this may mean it is not inside ng-app.'); }}"+"angular.element(document.body).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);");
         return"Pass:";
       } catch(Throwable e){
    System.err.println(e.getMessage());
    return"Fail:"+e.getMessage();
    }
}
The behaviour of this method is same that of Protractor. If we execute test against a non-angular app or any exception arrises during synchronisation , this method is going to return the exception message.
Few things to be noted:
1. The default timeout for wait until synchronisation is set to 60 seconds, if required it can be modified , or parametrised.

2. Root element is set to ‘document.body’, if required this can also be modified/parameterised , incase root element of the application is other than <body>, which means that AngularJS has been used in other sections/tags , but not body.

This could be helpful for people using Selenium but not Protractor to test applications built with AngularJS.

Happy Testing  🙂

 

  • Suresh Vemuri

    Thanks for your article and time!
    I searched but could not find any resources.

    Can you please let me know how to call Protractor scirpt into Selenium related code.

  • radhika viswanath

    Hi ,
    I need to add test step in protractor reports. How can i achieve this?