How to quit Firefox using FireWatir on a Mac

There seems to be a bug in the way that FireWatir on the mac quits firefox… it doesn’t! When you try, you end up with a dialog box saying:

Close Firefox
A copy of Firefox is already open. Only one copy of Firefox can be open at a time.

Here it is…

close_firefox

These dialog boxes pile up on top of each other during a test run; a few minutes into the run it looks like the dialog box is surrounded by a big black band!

To get around the problem and kill firefox reliably, you can use the following method. It simply calls the killall command line app and tells it to kill the firefox process. Here’s the method:


def kill_mac_firefox_browser
  `killall -9 firefox-bin`
end

Brutal but effective.

BTW, here’s how to kill a process by name using ruby on windows; the example used is how to kill firefox too!

How to know if you have a secure session in Watir

Sometimes you’re on a secure page, sometimes you’re not. How to know? There’s a very simple method you can write to find out, based on the browser’s current url. Here’s the method:


def is_session_secure?
  @browser.url =~ /^https/ ? true : false
end

It’s worth noting that the method highlights how much you can get done with very little code using ruby and watir. Here’s what it does: first, get the url out of the browser. Then, see if the url matches a simple regex. Since this operation is done in a ternary operation, a simple true/false will result. And, since ruby returns what the final line in a method resolves to, the true/false result of the match will be returned. Another nail in the coffin of big and bloated UI automation tools!

Here’s an example of the code in use…


require "rubygems"
require "firewatir"
require "test/unit"

include FireWatir

class GoogleSecurePageTests < Test::Unit::TestCase
  def setup
    @browser = Firefox.start("http://www.google.com")
  end

  def test_google_https
    assert(!is_session_secure?)
    @browser.link(:text, "Sign in").click
    assert(is_session_secure?)
  end

  def is_session_secure?
    @browser.url =~ /^https/ ? true : false
  end
end

How to get only direct child objects in Watir

When you come across methods in watir that return child objects for the first time (eg: the ‘lis‘ in @browser.ul(:id,’main’).lis ) you’d think they’d return only the direct child objects. Frustratingly, they don’t. They’ll also return nested objects of the same type. Here’s some example html:



<ul id='main'>
  <li></li>
  <li> </li>
  <li>
  <ul>
    <li></li>
  </ul>
  </li>
</ul>
  

If you call @browser.ul(:id, 'main').lis.length (to get the number of child li objects), you’d get the answer: 4. Intuitively, you’d expect to get the answer 3. Well, I would, and everyone else I’ve mentioned this to agrees [grumble grumble]. Now the example above is trivial – in the real world, nesting ul‘s in li‘s is common and can get quite complex. It gets even more complex when what you really want is to be able to type the following:


@browser.ul(:id,'main').lis[4].links[7]

…but instead you end up writing convoluted, complex, fragile and unmaintainable code to do what you’d think the above code should do – get the 7th link that is a direct child of the 4th li element that is a direct child of the ul.

But, there is a solution. Perversely, the solution is part of the watir/firewatir code. If you’re prepared to use xpath to identify your objects (my personal preference is to always use xpath), there’s a great method called elements_by_xpath which will do exactly what you want. Again, using the example above, you can get 3 as the answer (number of direct child li’s of the ul) with the following code:


@browser.elements_by_xpath("//ul[@id='main']/li").length

Even better, in doing the following:


my_lis = []
my_lis = @browser.elements_by_xpath("//ul[@id='main']/li")

…the my_lis variable will contain an array of elements; each element being of the right type – Li‘s in this instance. This works with links, table rows, table cells… everything! You can go back to writing clean, maintainable tests!

Note, this works in watir and firewatir. I haven’t checked safariwatir…

How to check for errors on every page using Watir

Testing a web app in an unstable environment is a pain. Many tests will fail for environmental reasons and filtering out those results can take a while. To make this sort of thing less painful, Watir and its derivatives (eg: firewatir) provide the ability to run any number of methods each time a page is loaded. They call these methods “checkers”… because… they check stuff. Here’s how to use them:

1) Write your checker
Checkers are Ruby Procs – essentially, nameless methods stored in variables. Here’s an example:


check_for_bad_things = Proc.new do
  puts "Server Error!" if @browser.text.include?("server error")
end

2) Add the checker to your instance of the browser


@browser = Watir::Browser.new
@browser.add_checker(check_for_bad_things)

3) Run your test. Every time a page is loaded, the check_for_bad_things proc is executed; and instead of seeing obscure failures when the test environment dies, you’ll see a message saying “Server Error!” making it easy to spot tests that failed for environmental/non-regression reasons.

You can make the checkers more useful by making them throw exceptions instead of just printing a line to the console. This way, they’ll cause tests to end with error instead of failed, making it even easier to sort out real failed tests from fails due to environment instability.

Fix for Firewatir visible? method

The visible? method in the ruby Firewatir 1.6.2 gem isn’t great. After some hunting around, I ended up here and found a working monkeypatch. Here’s the code:


class Element
  def visible?
    assert_exists
    jssh_command = "var val = 'true'; var str = ''; var obj = #{element_object}; while (obj != null) { try { str = document.defaultView.getComputedStyle(obj,null).visibility; if (str=='hidden') { val = 'false'; break; } str = #{DOCUMENT_VAR}.defaultView.getComputedStyle(obj,null).display; if (str=='none') { val = 'false'; break; } } catch(err) {} obj = obj.parentNode; } val;"
    jssh_socket.send("#{jssh_command}\n", 0)
    vals = read_socket()
    return (vals == 'false') ? false : true
  end
  public:visible?
end

Works for me!

The page I got this from says that the above will be in the next version of Firewatir… I hope so, because it doesn’t work without it!

FireWatir JSSH “Unable to connect” – FIXED!

Firewatir is awesome. But… sometimes Firefox takes too long to load (eg: if you have loads of plugins) causing firewatir to timeout and give the following error:

Unable to connect to machine : 127.0.0.1 on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option

This was a major pain; painful enough to send me looking through the firewatir code in the hope that I’ll be able to fix it. Turns out the logic that deals with starting firefox and establishing a connection to JSSH needs only a minor change to fix the problem. The relevant code is in the firefox.rb file within the firewatir gem, specifically the set_defaults method. Here’s a monkeypatch to override the necessary method:


require "rubygems"
require "firewatir"

class FireWatir::Firefox
  # fixes the timeout caused by firefox taking ages to
  # load, preventing a connection from being made to JSSH
  def set_defaults(no_of_tries = 0)
    begin
      $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
      $jssh_socket.sync = true
      read_socket()
    rescue
      no_of_tries += 1
      sleep 0.1
      retry if no_of_tries < 300
      raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
    end
    @error_checkers = []
  end
end

To use the patch, place it in a file that is read in before you use firewatir for the first time.

Firewatir tries to connect 3 times to JSSH after waiting for 2 seconds for Firefox to load. This is usually enough. But again, there are scenarios where 2 seconds and 3 connection attempts just isn’t enough. The very simple and slightly hacky fix that I tried worked beautifully… instead of trying 3 times, try 300 times instead with a simple sleep 0.1 just before the retry. This gives Firefox around 30 seconds to get its act together. As soon as it is ready, the test begins. It hasn’t caused any execution slowdown – in fact, now that I don’t have to run ‘failed’ tests at the end of a test run, I can just run everything once, saving loads of time! The joys of one-click-testing…
Since putting in this fix, I haven’t seen the problem again!