Sunday, April 12, 2009

How to use jQuery to create custom Selenium locate strategy

In Tellurium, we start to support jQuery selector because XPath in IE is way too slow. At the current stage, we use a custom Selenium locate strategy to achieve this goal.

The first thing is to let Selenium load up jQuery library. You can put jquery.js into Selenium core TestRunner.html or RemoteTestRunner.html and repackage selenium-server.jar, or dump all jquery.js code into user-extensions.js.

Before run your selenium tests, you should first register your jquery locate strategy. Our Groovy code looks as follows,

//use Get to return the DOM reference.
//need to check if it is an attribute locator in the format of locator@attr
// alert("locator: " + locator + " loc: " + loc + " attr: " + attr);
sel.addLocationStrategy("jquery", '''
var loc = locator;
var attr = null;
var isattr = false;
var inx = locator.lastIndexOf('@');
if(inx != -1){
loc = locator.substring(0, inx);
attr = locator.substring(inx + 1);
isattr = true;
}
var found = $(inDocument).find(loc);
if(found.length == 1 ){
if(isattr){
return found[0].getAttributeNode(attr);
}else{
return found[0];
}
}else if(found.length > 1){
if(isattr){
return found.get().getAttributeNode(attr);
}else{
return found.get();
}
}else{
return null;
}''')

You may be aware that Selenium provides the following api to register a custom locate strategy:

addLocationStrategy ( strategyName,functionDefinition )

Selenium will pass three arguments to your function:

* locator: the string the user passed in
* inWindow: the currently selected window
* inDocument: the currently selected document

The function must return null if the element can't be found.

Since we have jquery loaded up in selenium-core, we can use $(inDocument).find(jquery statement) to find the elements we want.


There are two concerns here, first, jQuery find() will return jQuery
objects, which are a wrap of DOM references, Selenium only takes DOM
elements. Thus, you should use get(0) (i.e., [0]) or get() to return
DOM references to Selenium.

Second, Selenium provides attribute locator for xpath by append @attr
to the xpath, you may like to use the same format for jQuery selector.
As a result, in our locate strategy, we check if there are @attr at the
end of the locator, if it includes @attr, we need to call getAttributeNode
to return the attribute node to selenium. Notice that you cannot pass
the attribute value itself to Selenium because Selenium only takes the
attribute node from the locate strategy. The rest of the code is straightforward.

5 comments:

  1. Can we use jquery selector for all browsers: IE, Firefox, Chrome, Safari??

    ReplyDelete
  2. to include jquery one can also run

    selenium.addScript(readFile(new URL(new URL(selenium.getLocation()), "/some/your/location/jquery.js")), "");

    and then use

    String text = selenium.getEval("jQuery('html anything', window.document).text().trim()");

    ReplyDelete
  3. Hi! thanks for your Blog :D I like it.

    I would like to know if you already used jQuery with the Selenium remote control? and in this case wich ones where your steps.

    I have tried adding the jquery.js in core/scripts and modifiying the TestRunner.html, RemoteTestRunner.html, and the user-extensions.js file but i did not get it. When I run my test the Selenium Control Remote starts but it does not execute any command.

    Any idea?

    Thanks for your help

    ReplyDelete
  4. I tried this out and it was working GREAT... until we tried running our selenium scripts using a similar location strategy vs IE7. Now we get an error that reads: "ERROR: Error executing strategy function jquery: 'jQuery' is null or not an object."

    This to me reads like it's something failing WITHIN the location strategy code, but it ONLY fails when the test is run vs IE7. Any ideas here?

    Here's the location strategy we're using (it's not the one you describe, but another I found that supports the jQuery .parents call)
    var loc = locator;
    var attr = null;
    var isattr = false;
    var inx = locator.lastIndexOf('@');
    if (inx != -1){
    loc = locator.substring(0, inx);
    attr = locator.substring(inx + 1);
    isattr = true; }
    var selectors = loc.split('<');
    var found = $(inDocument);
    for (var i = 0; i 0) {
    found = $(found.parents()[0]); }
    if (jQuery.trim(selectors[i]) != '')
    found = found.find(selectors[i]);
    }
    if (found.length > 0) {
    if (isattr) {
    return found[0].getAttributeNode(attr);
    }
    else {
    return found[0];
    }
    }
    else {
    return null;
    }

    Coudl it be the use of jQuery. instead of $()? Or could it be something where when running in IE7 I've got the definition in the wrong file? I've got the jquery definition added to both TestRunner and RemoteRunner.html in the \core\ folder.

    Thanks for any ideas!
    JDB

    ReplyDelete
  5. hi Martin;
    I am beginner in Selenium.
    I use Selenium 2.0 with Firefox WebDriver and I have an error with the LOCATOR_DETECTION. there is the generated Error:

    "Exception in thread "main" com.thoughtworks.selenium.SeleniumException: LOCATOR_DETECTION_FAILED not found"

    I try it your solution, but it does not work because Eclipse can't know the readFile method method.
    Code:
    " readFile(new URL(new URL(selenium.getLocation()), "/some/your/location/jquery.js"))"
    Can you give more details about your solution or other advice

    Thx :)

    ReplyDelete