21

Some of the sites I deal with have heavy ajax requests. I plan to wait for Ajax request completion before clicking for asserting for element. Currently I use

try {
    if (driver instanceof JavascriptExecutor) {
        JavascriptExecutor jsDriver = (JavascriptExecutor)driver;

        for (int i = 0; i< timeoutInSeconds; i++) 
        {
            Object numberOfAjaxConnections = jsDriver.executeScript("return jQuery.active");
            // return should be a number
            if (numberOfAjaxConnections instanceof Long) {
                Long n = (Long)numberOfAjaxConnections;
                System.out.println("Number of active jquery ajax calls: " + n);
                if (n.longValue() == 0L)  break;
            }
            Thread.sleep(1000);
        }
    }
    else {
       System.out.println("Web driver: " + driver + " cannot execute javascript");
    }
}
catch (InterruptedException e) {
    System.out.println(e);
}

But it works well for Ajax requests but not for any similar requests with variants of jQuery libraries.

Note:

document.readyState == 'complete'

It doesn't work for Ajax requests or any other similar alternatives.

Neither tests are written by me or belong to single webapp. So I can't edit the webapp.

itwasntme
  • 1,442
  • 4
  • 21
  • 28
raju
  • 4,788
  • 15
  • 64
  • 119
  • Do you mean you in other sites ajax calls are made without jquery? What's the purpose of injecting jquery? – Purus Jun 19 '14 at 04:55
  • Question is why you need that – Purus Jun 19 '14 at 05:31
  • If asynchronous requests are sent using other libraries like vanilla, does jquery.active still work? If not then I am asking wrong question. – raju Jun 19 '14 at 08:09
  • If your application does not use jquery, you don't have any point of injecting it to the application. – Purus Jun 19 '14 at 08:11
  • https://github.com/sebuilder/se-builder/issues/188 gives little more context. – raju Jun 19 '14 at 08:17
  • As I mentioned, unless the sites uses jquery, there is no point using jquery. Try to get something using plain javascript instead of jquery way – Purus Jun 19 '14 at 08:26
  • Can you check my answer posted below? – Purus Jun 19 '14 at 08:29
  • Good to know. Check and let me know. – Purus Jun 19 '14 at 08:38
  • 2
    Have you checked this SO question : [How to check if HTTP requests are open in browser?](http://stackoverflow.com/questions/9267451/how-to-check-if-http-requests-are-open-in-browser) – LeGEC Jun 23 '14 at 12:12

4 Answers4

13

I found the answer and it worked for few Ajax and non-ajax sites I checked. After this patch I no longer need to do implicit waits even for ajax heavy pages, LeGac pointed out the following code in one of his comments to the question.

public static void checkPendingRequests(FirefoxDriver driver) {
    int timeoutInSeconds = 5;
    try {
        if (driver instanceof JavascriptExecutor) {
            JavascriptExecutor jsDriver = (JavascriptExecutor)driver;

            for (int i = 0; i< timeoutInSeconds; i++) 
            {
                Object numberOfAjaxConnections = jsDriver.executeScript("return window.openHTTPs");
                // return should be a number
                if (numberOfAjaxConnections instanceof Long) {
                    Long n = (Long)numberOfAjaxConnections;
                    System.out.println("Number of active calls: " + n);
                    if (n.longValue() == 0L)  break;
                } else{
                    // If it's not a number, the page might have been freshly loaded indicating the monkey
                    // patch is replaced or we haven't yet done the patch.
                    monkeyPatchXMLHttpRequest(driver);
                }
                Thread.sleep(1000);
            }
        }
        else {
           System.out.println("Web driver: " + driver + " cannot execute javascript");
        }
    }
    catch (InterruptedException e) {
        System.out.println(e);
    }    
}



public static void monkeyPatchXMLHttpRequest(FirefoxDriver driver) {
    try {
        if (driver instanceof JavascriptExecutor) {
            JavascriptExecutor jsDriver = (JavascriptExecutor)driver;
            Object numberOfAjaxConnections = jsDriver.executeScript("return window.openHTTPs");
            if (numberOfAjaxConnections instanceof Long) {
                return;
            }
            String script = "  (function() {" +
                "var oldOpen = XMLHttpRequest.prototype.open;" +
                "window.openHTTPs = 0;" +
                "XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {" +
                "window.openHTTPs++;" +
                "this.addEventListener('readystatechange', function() {" +
                "if(this.readyState == 4) {" +
                "window.openHTTPs--;" +
                "}" +
                "}, false);" +
                "oldOpen.call(this, method, url, async, user, pass);" +
                "}" +
                "})();";
            jsDriver.executeScript(script);
        }
        else {
           System.out.println("Web driver: " + driver + " cannot execute javascript");
        }
    }
    catch (Exception e) {
        System.out.println(e);
    }
}

After every step you would need to call

checkPendingRequests(driver);
Ben McCann
  • 18,548
  • 25
  • 83
  • 101
raju
  • 4,788
  • 15
  • 64
  • 119
  • 6
    Are you sure that it's javascript? It looks like java – Tebe Nov 03 '15 at 13:07
  • in the angular app, this script is giving below warning in browser console and my service calls are failing too. [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. – Karthi Aug 31 '17 at 23:55
  • This is racy. You will get times when the page has already fired off ajax before your script runs. The best way to do this is with a browser extension. But that doesn't work for Chrome Headless mode. – Nicholas DiPiazza Mar 19 '18 at 14:10
2

This doesn't work? http://api.jquery.com/ajaxstop/

$(document).ajaxStop(function() {
    // Do stuff here...    
});
Rick Burns
  • 1,538
  • 16
  • 20
1

If you are using JSONP requests, you need to enable the active handling:

jQuery.ajaxPrefilter(function( options ) {
    options.global = true;
});

I think that the use of active is correct, but possibly the way you have used might return false in the instanceof conditions.

Optionally, see another way to wait for jQuery ajax calls using active in Selenium tests:

browser.wait_for_condition("selenium.browserbot.getCurrentWindow().jQuery.active === 0;", '30000')
Community
  • 1
  • 1
falsarella
  • 12,217
  • 9
  • 69
  • 115
-3

Based on our discussion over the comments, this might work for you.

With prototype.js:

var ACTIVE_REQUESTS = 0; // GLOBAL

ACTIVE_REQUESTS++
new Ajax.Request('/your/url', {
  onSuccess: function(response) {
    ACTIVE_REQUESTS--;
    // Handle the response content...
  }
}));

console.log("there are " + ACTIVE_REQUESTS + " open AJAX requests pending");

With plain script:

interValRef = 0;

interValRef = setInterval("checkState();",100)

function checkState(){
    if(document.readyState == 'complete'){
        clearInterval(interValRef);
        myFunc();
    }
}

Source: Check Pending AJAX requests or HTTP GET/POST request

Community
  • 1
  • 1
Purus
  • 5,701
  • 9
  • 50
  • 89
  • Doesnt work for me. I have checked with a pending ajax request. document.readyState is always 'complete'. checkout this http://tinypic.com/r/mr4qis/8. even with pending ajax requests, document.readyState is 'complete' – raju Jun 19 '14 at 08:44