Watir-WebDriver with GhostDriver on OSX: headless browser testing

GhostDriver has been released which means it is now easy to run reliable headless WebDriver tests on Mac OSX.

Steps to get working on OSX

  1. First make sure you have homebrew installed
  2. Run
    brew update

    then

    brew install phantomjs

    which should install PhantomJS 1.8.1 or newer

  3. Run irb and start using GhostDriver!
    require 'watir-webdriver'
    b = Watir::Browser.new :phantomjs
    b.goto "www.google.com"
    b.url #"http://www.google.com.au/"
    b.title #"Google"

I’ve tested it on a large test suite (123 scenarios) and it behaves the same as other browsers with full JavaScript support. It took 8m13s in total: surprisingly it is slightly slower than ChromeDriver (7m30s) in my testing, but a little faster than the Firefox Driver (9m33s).

Well done to all involved in this project. It’s great to see a reliable, realistic headless browser with full JavaScript support for WebDriver finally released.

And yes, in case you’re wondering, it does screenshots!

Getting the WebDriver driver from a WebDriver element

In watir-webdriver it is really easy to access both the webdriver driver and the watir browser objects from an element:

require 'watir-webdriver'
b = Watir::Browser.new
b.goto 'www.google.com'
button = b.button(name: 'btnK')
button.driver #webdriver
button.browser #watir browser

I never knew how to do this in C# until someone named Robert left a comment yesterday on an old blog post with instructions on how to do so.

You can get the webdriver by using.
var driver = ((IWrapsDriver)webElement).WrappedDriver;

Neat. This means the C# check image present extension method I wrote about previously can be implemented directly from the element itself:

public static bool IsImageVisible(this IWebElement image)
{
  var driver = ((IWrapsDriver)image).WrappedDriver;
  var script = TestConfig.DriverType == "ie"
             ? "return arguments[0].complete"
             : "return (typeof arguments[0].naturalWidth!=\"undefined\"" +
             " && arguments[0].naturalWidth>0)";
  return (bool)((IJavaScriptExecutor)driver).ExecuteScript(script, image);
}

This means it can be simply called like:

Driver.FindElement(By.Id("blah")).IsImageVisible();

instead of the cumbersome:

Driver.IsImageVisible(Driver.FindElement(By.Id("blah")));

Thanks Robert!

Checking an image is actually visible using WebDriver

I didn’t realize it’s actually a little tricky to check that an image is loaded when using WebDriver. WebDriver will only complain if the image tag you’re looking for isn’t in the DOM, not if the image link is broken and not actually visible.

For example, in watir-webdriver (ruby), this doesn’t really work as I would expect as the image isn’t actually visible on the ‘brokenimage’ page.

require 'watir-webdriver'
b = Watir::Browser.new :firefox
b.goto 'https://dl.dropbox.com/u/18859962/brokenimage.html'
puts b.image(id: 'watermelon').visible? #true but is not visible

The way to check that is is actually visible is to check a JavaScript property ‘naturalWidth’ is greater than 0.

b = Watir::Browser.new :firefox
b.goto 'https://dl.dropbox.com/u/18859962/brokenimage.html'
puts b.execute_script("return (typeof arguments[0].naturalWidth!=\"undefined\" && arguments[0].naturalWidth>0)", b.image(id: 'watermelon'))

Unfortunately this doesn’t work in IE, so you should use the ‘complete’ JavaScript method in IE (which doesn’t work in other browsers):

b = Watir::Browser.new :firefox
b.goto 'https://dl.dropbox.com/u/18859962/brokenimage.html'
puts b.execute_script("return arguments[0].complete", b.image(id: 'watermelon'))

In C#, you can wrap this up into a WebDriver extension method so you can this directly from Driver passing in the image element.

public static bool IsImageVisible(this IWebDriver driver, IWebElement image)
  {
    var script = TestConfig.DriverType == "ie"
                ? "return arguments[0].complete"
                : "return (typeof arguments[0].naturalWidth!=\"undefined\"" +
                  " && arguments[0].naturalWidth > 0)";
    return (bool) ((IJavaScriptExecutor) driver).ExecuteScript(script, image);
  }

// Usage
Driver.IsImageVisible(Driver.FindElement(By.Id("watermelon)));

If it’s important that images load correctly in your application, you should probably start putting some of these in your WebDriver page objects. It’s simple to write a verify images method on a page that iterates through each image in the DOM and checks that it’s visible using the techniques above. Have fun.

Update: 30 November
I wrote about a slightly more elegant C# approach to do this directly from the element.

The webdriver-user-agent gem now supports random user agents

My webdriver-user-agent gem now supports random user agents. This idea belonged to Christoph Pilka who released the webdriver-user-agent-randomizer gem and suggested that we merge this feature back into the orginal gem.

Well, I have done it and now you can access this functionality like so:

require 'selenium-webdriver'
require 'webdriver-user-agent'
driver = UserAgent.driver(:agent => :random)
driver.execute_script('return navigator.userAgent')
# random agent like "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:0.9.2) Gecko/20010726 Netscape6/6.1"

See README for full details.

Webdriver select lists in ruby

Selecting an option from a select list using the selenium-webdriver gem:

require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.navigate.to 'http://www.shino.de/parkcalc/'
Selenium::WebDriver::Support::Select.new(driver.find_element(:id => 'ParkingLot')).select_by :text, 'Economy Parking'

is much simpler in watir-webdriver

require 'watir-webdriver'
browser = Watir::Browser.new
browser.goto 'http://www.shino.de/parkcalc/'
browser.select_list(:id => 'ParkingLot').select 'Economy Parking'

That is all.

Automatic Firefox authentication when using Selenium-WebDriver with AutoAuth

I came across a particularly challenging problem today automating a web app for a client that runs behind a corporate proxy on a different Windows domain. The corporate proxy used NTLM authentication, but since I was on a different domain, I couldn’t get Firefox to send this information automatically so an authentication dialog would always appear that looked similar to this (IE worked fine):

Normally with browser authentication it is fairly straightforward to embed the username and password into the URL and Firefox will pass this to the web application without any problems (it’ll even ignore the confirmation normally displayed to the user), but in this case it didn’t work as it was the proxy that was requesting the information, not the application.

require 'watir-webdriver'
b = Watir::Browser.new :firefox
b.goto 'http://admin:password@192.168.0.1'

I manually could get Firefox to store the credentials, but every time the WebDriver tests would run, this darn Authentication Required’ dialog would appear (without credentials if using the standard new WebDriver profile for each test run). I tried setting all sorts of Firefox about:config settings to do with NTLM but nothing would work. After lots of trial and error, and finding nothing useful on the Internet about this issue, a colleague pointed out a Firefox add-on called AutoAuth that automatically submits these dialogs using stored Firefox credentials. Voila!

Example using Watir-WebDriver (the quick way)

The easiest way is to the install the AutoAuth add-on on your default Firefox profile (the one that Firefox uses when launched manually), and store the credentials needed in the default Firefox password manager. All you then need to do is tell Watir-WebDriver to use the default profile:

require 'watir-webdriver'
b = Watir::Browser.new :firefox, :profile => 'default'
b.goto 'http://192.168.0.1'

The issue with the above code is that it’s not repeatable across machines, as the machine’s default profile must have AutoAuth installed, and the username and password in the password manager.

Example using Watir-WebDriver (the most repeatable way)

To make this more repeatable, first you need to create a Firefox profile by following the instructions here (we’ll call it WatirWebDriver).

Manually launch this profile and visit the site you need to authenticate to, enter the username and password and make sure you save the credentials in Firefox when prompted.

The script is then pretty simple: create a profile as a copy of the one you made, add the AutoAuth extension (download it and place the xpi file in your project directory), and visit the site:

profile = Selenium::WebDriver::Firefox::Profile.from_name 'WatirWebDriver'
profile.add_extension 'autoauth-2.1-fx+fn.xpi'
b = Watir::Browser.new :firefox, :profile => profile
b.goto 'http://192.168.0.1'

This script should visit the site and AutoAuth should kick in and automatically submit that pesky ‘Authentication Required’ dialog: take that!

Summary

Whilst this NTLM proxy authentication issue was a bit of an issue to begin with, we found a reasonable way to work around it. I don’t really like the dependency on an existing Firefox profile with the proxy credentials, but until I work out how to store credentials in a Firefox profile I create at runtime using Selenium (which I don’t believe is possible), I think that it’s necessary.

I’ve also updated the Watir-WebDriver Basic Browser Authentication page.