Watir ( “Web Application Testing in Ruby” – pronounced 'water' ) is a sweet little system used to automate browsers for the purposes of testing your site. Unfortunately, it’s for IE on Windows. Despite being odd in that most Ruby software is Linux or MacOS it’s also frustrating because I don’t use Windows. Luckily the Tubes have granted us two alternatives, SafariWatir and FireWatir. They achieve browser automation in very different ways.

SafariWatir works by using appscript – a Ruby / Applescript bridge whilst FireWatir uses a Firefox extension called JSSH – JavaScript SHell. Starting Firefox with a command line argument ‘-jssh’ enables the extension and starts a server listening on port 9997. Connecting to this port allows Ruby to drive Firefox via Javascript.

It seems to work well, aside from a couple of little annoyances. SafariWatir has some timing problems. For example, sometimes Watir will look for text before the AJAX form has returned data. I worked around by adding sleep(x) between operations. SafariWatir also doesn’t have the element_by_xpath method which makes search for elements a bit of a pain and FireWatir seems to have a version of the contains_text function which doesn’t work like I thought it would.

There’s really two main things I need to do when testing my web application, and that is navigate through the pages, and confirm that pages have the text I’m expecting. Both the Watirs I’m using seem competent at driving the browser, but the interface for searching for elements in the page isn’t as consistent between the two libraries. ( Feel free to send me email telling me I’m wrong btw. I can take it. ) What I need is some library that can take the HTML of a page and provide a consistent interface for searching through it for elements. Something like Hpricot. Since I want to test my site in both Firefox and Safari and both SafariWatir and FireWatir support the _html function, one option seems to be to pass the page contents to Hpricot and use the result to test page contents.

So here’s my first crack at a Watir Script – it’s built using the Ruby Test::Unit module. Seemed to be a reasonable way to go about it. This script is built to test my site Booko, a site which searches for the cheapest place to buy books in Australia. We’ll be searching for ISBNs and text. Obviously the text searches will change over time as different books are introduced, but I can deal with that as it happens.

require 'test/unit'
require 'rubygems'
require 'hpricot'
require 'safariwatir'
require 'firewatir'
include FireWatir

Firstly, include the required libraries and such.

class WairTest 
 @@server_url = 'http://localhost:3000/'
 def new_browser
    #Firefox.new
    Watir::Safari.new
 end

Now, I know there’s probably a smart way to select which class initialiser to call, probably using a lambda or something clever. If you know what it is let me know. ;-)

def test_search_01
 res = browser = ""
 test = {
  :test01 => { :se => 'fp', :q => 'Laziness', :r => "The Lazy Girl's Guide to Losing Weight and Getting Fit" },
  :test02 => { :se => 'fp', :q => '978-0747546290', :r => "Harry Potter and the Prisoner of Azkaban" },
  :test03 => { :se => 'fp', :q => '0321445619', :r => "The Rails Way" },
  :test04 => { :se => 'fp', :q => '031601348X', :r => "Starbucked: A Double Tall Tale of Caffeine, Commerce, and Culture" },
  :test05 => { :se => 'us', :q => 'Laziness', :r => "The Myth of Laziness" },
  :test06 => { :se => 'us', :q => '978-0439136358', :r => "Harry Potter and the Prisoner of Azkaban (Book 3)" },
  :test07 => { :se => 'us', :q => '0321445619', :r => "The Rails Way (Addison-Wesley Professional Ruby Series)" },
  :test08 => { :se => 'us', :q => '031601348X', :r => "Starbucked: A Double Tall Tale of Caffeine, Commerce, and Culture" },
  :test09 => { :se => 'uk', :q => 'Laziness', :r => "The Joy of Laziness: How to Slow Down and Live Longer" },
  :test10 => { :se => 'uk', :q => '978-0747546290', :r => "Harry Potter and the Prisoner of Azkaban (Book 3) Paperback" },
  :test11 => { :se => 'uk', :q => '0321445619', :r => "The Rails Way: Driving Rails into the Enterprise (Addison-Wesley Professional Ruby)" },
  :test12 => { :se => 'uk', :q => '0340960817', :r => "Starbucked: A Double Tall Tale of Caffeine, Commerce and Culture" },
 }

Each of the hashes contains a search engine to use, ( fp is Fishpond, uk is Amazon UK and us is Amazon US ) a query string and the expected result. We’ll be stepping through each of these.

assert_block "Couldn't use Browser" do
 browser = new_browser
 browser.goto(@@server_url)
 # We need to sleep for Safari
 sleep(1)
end

This is an example of an assert_block construct. The block passes if it yields true. This block connects to either Safari or Firefox and heads off to the local development version of my site ( localhost:3000 ).

test.each do |key, value|
   browser.text_field(:name, 'search[query]').set(value[:q])
   browser.select_list(:name, 'search[search_engine_id]').select_value(value[:se])
   browser.button(:value, "Search").click
   # Next we sleep for 6 seconds to allow the page to load. This is dependent on the site being searched.
   sleep(6)
end
doc = Hpricot(browser.html)
   assert_equal( value[:r], (doc/"h3/a")[0].inner_html )
 end
end

Here’s the meat of the testing – for each test we fill in the text_field, set the search engine to the correct value and submit the form. We then wait for 6 seconds for the response ( this varies between the different search engines ) to be retuned via an Ajax call. The results can always be accessed with the same XPath expression which is pretty handy. And that’s that. It’s kind of mesmerising watching your browser fill in forms and submit them.