Home > .Net, jQuery, Uncategorized, WatiN > Synchronizing WatiN and Ajax with jQuery

Synchronizing WatiN and Ajax with jQuery

WatiN (pronounced “what in”) is an open source .Net project that allows the programmatic automation of browsers (IE/Firefox/Chrome).  My company uses WatiN within Visual Studio unit tests to automate the testing of our web application.  It works interactively with the browser window: clicking, entering text, and navigating just like a user.   With the unit tests, assertions can be made about the state of the application through various user scenarios.  The project is under active development on sourceforge.  In short, it’s a wonderful tool and isn’t hard to use.

We maintain hundreds of test cases against our web application.  We chain team builds so that when the app build finishes building successfully, it kicks off another build which ships and runs mstest against the WatiN solution.  There’s a dedicated build agent running in interactive mode which runs the tests.  I’ll cover this setup in a future post.

WatiN abstracts the browser/DOM/javascript engine into its own API.  It provides a consistent abstraction for several major browsers.  At a high level, it works by instantiating the browser under test and holding a native windows handle to the browser window.  Then unmanaged calls into User32.dll are used to perform various actions on the browser.

There’s a good amount of synchronization that is done natively by WatiN.  For example, when you click a link that goes to a new page, the click is simulated and then the WatiN code waits for the browser to be ready when the next page is loaded.  In most cases this works perfectly.  Here’s an example from the WatiN homepage:

[Test]
public void SearchForWatiNOnGoogle()
{
  using (var browser = new IE("http://www.google.com"))
  {
    browser.TextField(Find.ByName("q")).TypeText("WatiN");
    browser.Button(Find.ByName("btnG")).Click();

    Assert.IsTrue(browser.ContainsText("WatiN"));
  }
}

Between the .Click() method and the Assert, WatiN waits for the DOM to complete loading.  See the DomContainer.WaitForComplete() method in WatiN to understand how this works.

However, modern web applications change their pages with Ajax in addition to full page loads.  This can been a hairy problem for every browser automation library, including WaitN.  WatiN doesn’t know which page interaction will trigger Ajax behavior, and the page may be different after the interaction.  Another synchronization mechanism in needed.

If you are using jQuery .ajax, you’re in luck.

jQuery has global Ajax event handlers ajaxStart and ajaxStop which wrap all ajax activity.  Any time Ajax requests start, ajaxStart is called; and after all ajax requests have finished, ajaxStop is called.  We can write a piece of code in a central place (common js or masterpage) to keep track of whether the page is currently in the middle of an ajax request.

// used to syncronize the WaitN tests
var isRunningAjax = false;
$(document).ajaxStop(function() { isRunningAjax = false; });
$(document).ajaxStart(function() { isRunningAjax = true; });

Now we’ll give WatiN  the ability to poll the page for the isRunningAjax variable, using its ability to run any javascript with the Browser.Eval method.  We do this with a simple extension method on Browser, which is written in the same spirit as WatiN’s native WaitForComplete():


internal static void WaitForAjaxComplete(this DomContainer browser)  {            
  WaitForAjaxComplete(browser, 150);        
}

internal static void WaitForAjaxComplete(this DomContainer browser, int pollingIntervalInMs) {
  while (true)
  {                
    Thread.Sleep(pollingIntervalInMs);                                    
    if(browser.Eval("typeof(isRunningAjax)") == "undefined" || browser.Eval("isRunningAjax") == "false") { 
      break;
    }
  }        
}

Normally, a while(true) would be a no-no in production code, but in this case since the mstest runner has a timeout (default 30s) for the test to run, the worst that can happen is 30 seconds of waiting followed by a failed test. Also, notice that typeof(isRunningAjax) makes sure the synchronization mechanism is on the page (in case you forget to put it there, or another js error prevented its evaluation).

Finally, here’s a snippet of WatiN code that takes advantage of our new synchronization for jQuery Ajax calls:

 Browser.TextField(Find.ById(SOME_ID)).TypeText(SOME_SEARCH_KEY);
 Browser.WaitForAjaxComplete();
 Div div = Browser.Div(Find.ByClass("savedResultsBlock"));
 div.Link(Find.ByTitle(SOME_VALUE)).Click();

I’ve been in awe of WatiN for a while now and I’m planning some more posts describing how we have set up a page/control wrapper to WatiN that provides an even higher layer of abstraction so that the test methods can be written against an Api specific to our application.

Categories: .Net, jQuery, Uncategorized, WatiN
  1. March 6, 2011 at 11:45 pm

    Hi,

    nice post – I also use this in my testing code. I inject a javascript monitor on every page (that allows me to do css selector – I came to WatiN before the introduction of css selector – and ajax call wait), just after the dom is ready. It allows to test any kind of page (even if they don’t initially have jQuery).

    So I still have a problem: if the ajax call begins at the page load time, because my javascript monitoring code is not yet injected, I cannot use my .WaitForAjaxComplete() method :/

  2. March 16, 2011 at 5:19 pm

    Thanks for this. I have not had much luck with Watin + AJAX. I tried out your code. All runs fine on my local machine and all my tests pass, but when I try the same thing on my build server, I get a very small number of random test failures – different ones each run! The nature of the failure is clearly, in each case, caused by an Ajax request not completing. Did you experience anything similar?

  3. mickey mouse
    July 25, 2011 at 7:39 pm

    Where should the code go for internal static void WaitForAjaxComplete(this DomContainer browser)?

    Should it go into Watin.Core or elsewhere? Please provide details on code locations!!

    I hate when all the code I find just has the snippets and not actual locations. this doesn’t help

    • July 26, 2011 at 1:38 am

      It’s meant to be an extension method that you can put in any static class in your own assembly. The c# magic makes it appear to be an instance method on any IDomContainer. Good question.

  4. January 23, 2012 at 3:36 pm

    I modified your extension methods a bit to include timeout code (for those frameworks that don’t time out automatically):

    http://blog.thecognizantcoder.com/2012/01/implementing-wait-for-ajax-to-complete.html

  1. March 11, 2011 at 12:06 pm

Leave a comment