jQuery selectors in Robot Framework Selenium Library
Few months ago I selected a tool to test web interfaces: Robot Framework with Selenium Library. It allows the QA team to script test scenarios. Selenium provides a large set of keywords to drive a web-based user interface but more complicated interfaces developed using Java Script libraries like YUI and jQuery and AJAX can be harder to test. Recently I wrote a keyword to wait for YUI table to load using Java Script expression with jQuery selectors. I thought it would be great if I could use jQuery selectors to locate page elements, just like I use XPath at the moment. After doing some research I found it is possible and decided to give it a try.
I found Tellurium Automated Testing Framework that uses Selenium and jQuery selectors and a documentation page that describes how jQuery selector works.
The first thing to do is to make sure that Selenium loads jQuery. I checked out selenium library (download will also work) and unpacked the selenium-server.jar file.
cd src/SeleniumLibrary/lib mkdir selenium-server cd selenium-server jar xvf ../selenium-server.jar
Then I downloaded jQuery and placed it in core/scripts directory – it’s a single Java Script file. I edited TestRunner.html andRemoteRunner.html and added a line to load jQuery just before user-extensions.js.
... <script language="JavaScript" type="text/javascript" src="xpath/javascript-xpath-0.1.11.js"></script> <script language="JavaScript" type="text/javascript" src="scripts/jquery-1.3.2.js"></script> <script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script> ...
Then I added the following function definition to core/scripts/user-extensions.js file (RIDE replaces > and < with HTML encoded versions so those need to be converted again).
Selenium.prototype.locateElementByJQuerySelector = function(locator, inDocument, inWindow) { var loc = locator.replace(/>/g, '>'); loc = loc.replace(/</g, '<'); var found = $(inDocument).find(loc); if (found.length == 1 ) { return found[0]; } else if(found.length > 1) { return found.get(); } else { return null; } }
Next, I repackaged the jar file with ‘jar uf’ command only updating the files that I’ve changed.
jar uvf ../selenium-server.jar \ core/TestRunner.html \ core/RemoteRunner.html \ core/scripts/jquery-1.3.2.js \ core/scripts/user-extensions.js
At this point, the Selenium Library must be modified to expose “Add Location Strategy” keyword. It’s currently implemented but not visible to the user. I also added a possibility to start a different selenium-server.jar than the one distributed with the Library so I can keep my custom jar file with my test cases. I created an issue 59 and provided the patch. I hope those changes will get into the next release of the library. To simply enable the keyword, add the following method to SeleniumLibrary class in src/SeleniumLibrary/__init__.py
def add_location_strategy(self, strategyName, functionDefinition): """Ads custom location strategy. 'strategyName' is the name of the strategy; a prefix used when addressing an element. 'functionDefinition' is the java script that will be called. It must return a DOM reference, an array with DOM references, or null. Together with the modified selenium-server.jar it can provide a new method of locating elements on the page. For example, a JQuery strategy can be added to locate elements given JQuery selector syntax. Examples: | Add Location Strategy | jquery | return Selenium.prototype.locateElementByJQuerySelector(locator, inDocument, inWindow); | | Page Should Contain Element | jquery=div.#data-table | | """ self._selenium.add_location_strategy(strategyName, functionDefinition)
Before using jQuery locators in test cases they have to be registered with Selenium. I do that in the suite setup after I open the browser window.
| Add Location Strategy | jquery | return Selenium.prototype.locateElementByJQuerySelector(locator, inDocument, inWindow); |
I can now check for existence of a table in a page (table in a div with id data-table).
| Page Should Contain Element | jquery=div.#data-table > table |