Selenium Mojo : Protoype Bindings in Selenium

Introduction

On my current project we have been involved in converting some of the hundreds of manual tests that are run by our Test Team every release into a suite of automated Selenium RC tests.

During the course of this adventure my crew found several instances where XPath and native JavaScript were not sufficiently expressive to find elements in some of our more complicated interfaces.

Since our web app uses the Prototype/Scriptaculous JavaScript framework we wanted to find a way to make the locating power of Prototype available within Selenium RC.

We developed approaches for both Selenium 0.9.2 and Selenium 1.0.3 (which had better programmatic support for adding JavaScript user extensions to Selenium).


Selenium 0.9.2

Selenium RC provides the capability to add "user extensions" to augment its JavaScript core.

See http://seleniumhq.org/docs/08_user_extensions.html for detailed information on how to set this up.

We wrote the following user-extension.js file:

Selenium.prototype.setupProtoypeJS = function() { id = selenium.browserbot.getCurrentWindow().$; css = selenium.browserbot.getCurrentWindow().$$; }

In our code we have a base class for all our test cases. To this we added our own waitForPage() method:

public void waitForPage() { selenium.waitForPage('60000') proc.doCommand('setupPrototypeJS', []) }

Thus every time the page reloads (which clears the JavaScript context) we call waitForPage() and this command is re-executed. It creates two global variables (id and css) and binds them to Prototype's $ and $$ functions respectively.

Note: The reason we choose id and css instead of $ and $$ was that Groovy considers $ in Strings to be a special character and we would have had to escape it each time it was used.

The Prototype selectors can now be used in Selenium RC like this:

selenium.click("dom=id('foo')") selenium.click("dom=css('.bar')") selenium.click("dom=css('span.foo a.baz')")

Note: You still have to specify the dom locator type so Selenium RC will know to execute your locator string as JavaScript. 


Selenium 1.0.3

In more recent version of Selenium RC the project added the setExtensionJs() method. This allows you to set extension JavaScript programmatically prior to starting the selenium client:

selenium = new DefaultSelenium(...) selenium.setExtensionJs('...') selenium.start()

This made it much easier to implement our Prototype bindings. The only trick was that the JavaScript seems to be executed prior to having access to a page context and is also only executed once. This required us to take a different approach.

We created id and css as global functions instead of variables. This allowed us to defer accessing the current window until the functions were actually invoked.  

selenium.setExtensionJs(''' id = function(value) { return selenium.browserbot.getCurrentWindow().$(value); } css = function(value) { return selenium.browserbot.getCurrentWindow().$$(value); } ''');
Note: The above code snippet is written in Groovy which allows multiline Strings.

The Prototype selectors are used in Selenium RC like before:

selenium.click("dom=id('foo')") selenium.click("dom=css('.bar')") selenium.click("dom=css('span.foo a.baz')")

Thanks for reading! See me on Twitter (@marlhammer) or contact me through email (smouring@nearinfinity.com) if you have any questions or comments!

New Comment