Fast(er) CSS Selector Based Element Lookups in WatiN via jQuery

We’re using SpecFlow and WatiN to automate acceptance-level tests for a new ASP.NET MVC 2 web application. As we write more and more high-level system tests, we find ourselves wishing we had the ability to search our DOM using CSS selectors and other cool jQuery tricks. Eventually it dawned on us: why not actually use jQuery in the browser to find elements for us? It turns out to be pretty easy… and snappy, too.

Watin as a tool is pretty accessible – it’s easy to add to your project, has a nicely fleshed-out API for controlling and exploring web pages, and dovetails nicely with SpecFlow.

Something we missed keenly from our previous experiences with tools like Celerity, Webrat, etc. is the ability to use sophisticated CSS selectors to locate DOM elements. Out of the box, Watin supports neither CSS selector- nor XPath-based searching. Watin has its own methods and constraints for selecting nodes, and some of them are pretty nifty, but CSS selectors provide a distinct advantage for us: we’re already thinking about the semantic structure of our interface in terms of CSS selectors for both styling AND interactivity via jQuery. We already know how to address our elements… it’s a bummer to always be translating that into method chains and constraint lambdas.

Idea: use browser.eval() to execute jQuery in the browser, find the target element, return the id and then use WatiN to get an Element:


var link = browser.FindViaJQuery(“.search_results.businesses .result a:contains(‘Applebees’)”).Click();

FindViaJQuery() uses browser.eval() to invoke a Javascript function already loaded in the page called WatinSearchHelper.getElementId, passing along the CSS selector string. If the element is found in the page, its id is returned from the browser back to our test code, which then uses browser.Element(Find.ById(id)) to get an Element instance.

For elements that don’t actually have an id attribute, we generate one and attach it to the targeted node so we can still use a direct id-based lookup from Watin back in our C# code.

Javascript source code for the search helper function:


;WatinSearchHelper = function ($) {
    var earTagId = 1;

    var getElementId = function (cssSelector) {
        var resultId = "_no_element_";
        var el = $(cssSelector);
        if (el.length > 0) {
            var firstEl = el[0];
            if (firstEl.id == "") {
                // Give this element a contrived id so we can find it directly from the WatiN side:
                firstEl.id = "_watin_search_" + earTagId++;
            }
            resultId = firstEl.id;
        }
        return resultId;
    };

    return { getElementId: getElementId };
} (jQuery);

Extension method to enact CSS selector-based search via jQuery in the client:

If you can’t – or won’t – include watin_search_helper.js in your application, you could simply use browser.eval() to inject that blob of code dynamically into your page at test-time.

After-note: We did indeed find and use WatiNCssSelectorExtensions, and it was good for a while, but in certain circumstances it seemed to cause extreme slowdowns in our tests, but more importantly, the CSS selector support within Fizzler (the underlying dom search library) is not as advanced as jQuery’s.

Conversation
  • saxx says:

    Great idea.

    I ran into the same problems with Fizzler, but I never even thought of using JQuery to find the element from within Watin. Thanks!

  • David Crosby David Crosby says:

    Cool news! Thanks Jeroen!

  • Jeroen van Menen says:

    Hi,

    Just for the record, after reading this post and others who wrote about CSS selector support for WatiN, this feature is added to WatiN 2.0 Final. Here is an example:

    var link = browser.Link(Find.BySelector(“.search_results.businesses .result a:contains(‘Applebees’)”);

    WatiN will inject Sizzle (the engine used by jQuery) to make this work.

    Thanks for showing your usage/solution. It helped shaping the current implementation in WatiN.

    Jeroen
    Lead dev WatiN

  • Comments are closed.