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.
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 );
}
}
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.
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.