Recently about JavaScript

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')")



I would be interested in hearing feedback from anyone who has a chance to use this technique!

The most popular entry I've written at Near Infinity has been the JavaScript Particle Engine. It had limitations because I used transparent images and only made one color -- black. I recreated the fire demo for a talk -- Advanced Web Graphics with Canvas -- that I gave at the Rich Web Experience. I'll post the slides, sample code, and some more demos in upcoming entries.

I was reading a blog entry at Web Reflection that outlined some obscure solutions to common JavaScript patterns.

I thought that entry was interesting, but I'm not sure I'd use them because of code readability and maintenance. It did get me thinking of some other ways to obscure simple tasks.

The web community has been buzzing about the new Ajax server, Jaxer, from Aptana. If you haven't heard see John's, or Dion's Ajaxian posts about it.

Now, overall, I am really excited about the future in this project. The problem I had is all their examples use synchronous XMLHttpRequests. We already know why this is unfriendly to users.

I spoke at the Reston, VA No Fluff Just Stuff conference again this past weekend. The talk was on JavaScript security covering topics including:

  • Cross-site scripting (XSS)
  • Cross-site-request forgery (CSRF)
  • JSON Hi-jacking
  • JavaScript portscanning
  • JavaScript and CSS History "Go Fish"