How to wait for jQuery Ajax requests to complete f

2020-05-19 08:39发布

问题:

I'm writing WatiN tests to test an Ajax web application and have come across a timing issue with Ajax requests.

After an Ajax request is triggered by an action on the page, I'd like WatiN to wait until the request is complete before validating that the page was updated correctly.

I have a feeling that the solution will involve eval-ing JavaScript to register handlers for $.ajaxStart and $.ajaxComplete to track whether requests are in progress. I'll dig into that shortly, but wanted to see if anybody else has already solved this. Seems like it would be a common problem with Ajax testing.

回答1:

I've created a few WatiN Browser extension methods to solve this problem, but am still interested in other solutions.

The InjectAjaxMonitor method creates a javascript global variable that attaches to the ajaxStart and ajaxComplete events to track the number of requests in progress.

Whenever you need to wait for AJAX requests to complete before moving on, you can then call browserInstance.WaitForAjaxRequest();.


public static class BrowserExtensions
{
    public static void WaitForAjaxRequest( this Browser browser )
    {
        int timeWaitedInMilliseconds = 0;
        var maxWaitTimeInMilliseconds = Settings.WaitForCompleteTimeOut*1000;

        while ( browser.IsAjaxRequestInProgress()
                && timeWaitedInMilliseconds < maxWaitTimeInMilliseconds )
        {
            Thread.Sleep( Settings.SleepTime );
            timeWaitedInMilliseconds += Settings.SleepTime;
        }
    }

    public static bool IsAjaxRequestInProgress( this Browser browser )
    {
        var evalResult = browser.Eval( "watinAjaxMonitor.isRequestInProgress()" );
        return evalResult == "true";
    }

    public static void InjectAjaxMonitor( this Browser browser )
    {
        const string monitorScript =
            @"function AjaxMonitor(){"
            + "var ajaxRequestCount = 0;"

            + "$(document).ajaxSend(function(){"
            + "    ajaxRequestCount++;"
            + "});"

            + "$(document).ajaxComplete(function(){"
            + "    ajaxRequestCount--;"
            + "});"

            + "this.isRequestInProgress = function(){"
            + "    return (ajaxRequestCount > 0);"
            + "};"
            + "}"

            + "var watinAjaxMonitor = new AjaxMonitor();";

        browser.Eval( monitorScript );
    }
}


回答2:

This solution doesn't work very well because .ajaxStart is called only for the first Ajax request, while .ajaxComplete is called each time an ajax request is finished. if you run a this simple code in your console :

$.ajax({url:"/"}); $.ajax({url:"/"})

and add some logging in the .ajaxStart and .ajaxComplete handler methods, you can see that .ajaxStart handler will be called only once and .ajaxComplete handler twice. So ajaxRequestCount will become negative and all your design is screwed.

I suggest that you use .ajaxSend instead of .ajaxStart if you want to keep your design.

Another solution would be to use .ajaxStop instead of .ajaxComplete, but by doing so, you don't need the ajaxRequestCount, you only need a boolean that say if there are ajax requests running behind the scene.

Very useful information can be found : http://api.jquery.com/category/ajax/global-ajax-event-handlers/

Hope this helps.



回答3:

I just ran into this issue myself while working on some tests using WatiN. I found that in version 1.1.0.4000 of WatiN (released on May 2nd 2007 (latest version being 2.0 RC2 from December 20th 2009)), it is claimed that better support for handling Ajax in tests were added:

To better support testing of AJAX enabled websites, this release adds some more options to your toolbox.

A new method is added that will wait until some attribute has a certain value. This might be handy in situations where you need to wait until a value of an element gets updated.

Example:

// Wait until some textfield is enabled
textfield.WaitUntil("disable", false.ToSting, 10);
// Wait until some textfield is visible and enabled
textfield.WaitUntil(new Attribute("visibile", new BoolComparer(true)) && new Attribute("disabled", new BoolComparer(false)));

See the link to the release notes for more information.

I haven't looked into it in detail yet, so I cannot tell in which cases it might be useful or not. But thought it could be worth mentioning in case anybody else comes across this question.