Saturday 16 June 2012

How to Use JQuery Instead of XPath Locators in Selenium Testing Framework


If you have ever worked with Selenium, you may find XPath a convenient alternative to walking through document trees. In case you are using Firefox, XPath remains an opportune and fast way to find specific elements without walking the DOM manually. Unfortunately, it works tremendously slowly in IE 6 and IE 7. Recently, we faced this problem, and now we’re happy to share our elegant solution with you.
To solve the speed problem in IE, we tried to replace XPath selectors with CSS ones. It gave us a 10x speed up in IE. Impressive, but by using CSS, we lost a very useful feature that XPath offers. In Xpath, we can use the ‘..’ expression to select the parent of the current node. This feature is necessary when you need to click a button next to the element having a particular name in the list of elements. See the sample below.
<table class="style">
    <tr class="somestyle">
        <td>SomeName1td>
        <td>
            <input type="submit" value="Edit" class="btn" />
        td>
    tr>
    <tr class="gvAlternatingRowH35">
        <td>SomeName2td>
        <td>
            <input type="submit" value="Edit" class="btn" />
        td>
    tr>
table> 
All the buttons have the same attributes.  If you want to click the “Edit” button next to SomeName2, you can easily complete this task in selenium by using XPath and its parent feature:
selenium.click("//td[text()='SomeName2']/../td/input[@value='Edit']")
As we mentioned above, this won’t be that easy to do with CSS because it doesn’t have the appropriate feature. So, we used theJQuery parent() function to have the ability to select the parent of the current node. It works really fast in all browsers, since JQueryuses CSS. 
We added  Jquery’s library to the Selenium server (see steps 1 and 2 below) and executed JQuery script directly from Selenium. To execute JQuery script, you can use selenium.getEval(someJS) if you need the return value, or selenium.runScript(someJS) if you don’t.
But that was not enough for us. We wanted to select the parent element by using the easy syntaxes of CSS locators. To use them, we had to add a new custom locator to Selenium. Below, find the three easy steps that we took to add a custom locator.
1. Add Jquery’s library to Selenium server.
Add JQuery’s library (‘jquery.min.js’) to your selenium-server.jar (‘selenium-server.jar\core\scripts\’ folder). To do that, open ‘selenium-server.jar’ by using one of the archives — for example, 7-Zip — and drag-and-drop ‘jquery.min.js’ in the mentioned folder.
2. Add script reference to JQuery library.
Add script reference to ‘jquery.min.js’ to the ‘’ section of the RemoteRunner.html (‘selenium-server.jar\core\’).
<head>
...
<script language="JavaScript" type="text/javascript"src="scripts/jquery.min.js" />
...
<\head> 
3. Add new JQuery locator to Selenium core.
You can add a new JQuery locator to the Selenium core in the runSeleniumTest() function of the selenium-remoterunner.js file. All you need to do is to add a call of selenium.doAddLocationStrategy() method before Selenium property initialization of the window object. See the sample below for more details.
function runSeleniumTest() {
    ...
    selenium.doAddLocationStrategy("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 selectors = loc.split('<');
var found = $(inDocument);

for (var i = 0; i < selectors.length; i++) {
    if (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;
}
    ");
    ...
The first parameter of the selenium.doAddLocationStrategy method (“JQuery”) is the new Selenium locator’s name. The second one (“var loc = locator…”) is the new custom Selenium locator. If you want to, you can write your own implementation for the locator. To call the JQuery parent() function, we used the '<' custom symbol . 
Finally, let’s take a look at how you can use the new JQuery locators, instead of the XPath ones.
XPath locators:
•    xpath=//a[contains(@href,'#id1')]
•    xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td
•    xpath=//input[@name='name2' and @value='yes']
JQuery locators:
•    jquery= a[href*='#id1']
•    jquery= table[class='stylee'] th:contains('theHeaderText')•    jquery= input[name='name2'][value='yes']
We hope you’ll find our experience useful to you. Also, we’d be happy to answer your questions if you have any!


Reference