Modal Dialog Present exception Selenium-Webdriver

23 Mar 2012 | By Matt Robbins | Comments | Tags testing webdriver selenium capybara ruby

Let me tell you an incredibly boring story…

We have a scenario where a user clicks a link on a test page, some JS runs creating a cookie, the page reloads and then the cookie is read by JS on the next page as it loads and then is deleted.

In my test I wanted to assert that the cookie was created and the only way I could do this using Webdriver was by popping up a JS alert on the test page when the link is clicked to delay the page reload and then using Webdriver read the cookies.

#whilst the page reload is blocked by the JS alert
cookie = page.driver.browser.manage.cookie_named 'foo'
alert = page.driver.browser.switch_to.alert #using js alert on acceptance test page to stop execution and allow time to check presence of cookie
alert.send('accept')

This hack worked fine and served the purpose it was intended for….

That was until the guys at Selenium got all responsible and my tests started failing with webdriver throwing a ‘Modal dialog present’ exception.

I can’t find anything in the change logs but it looks like they have done this deliberately which makes sense…why would you want to interact with the page when a modal dialog is present????

Oh well…thought I would just mention it in case anybody else was wondering why this had happened!

Using Selenium Webdriver and Browsermob Proxy through a corporate proxy

04 Jan 2012 | By Matt Robbins | Comments | Tags testing capybara webdriver selenium browsermob

Browsermob Proxy proxy is a really useful tool that enables the user to effectively have an add-on api to ‘out of the box’ Selenium Webdriver which enables you to do things such as set header values, dump out har data etc. There is also a Ruby binding maintained as a separate gem which can be found here.

The problem I had was how to use this through/behind a corporate proxy, obviously it works by starting Firefox with a profile pointing to the browsermob proxy, which would then need to be told to navigate through a corporate proxy/firewall.

It turns out that the Java project does support this in Beta 4 and is documented here. Note this feature is not currently in the released binary (Beta 3) so you will need to ‘git clone’ the project and ‘mvn install’ to generate the jar file.

I then added a crude monkey patch which can be seen below, obviously when the next Beta of the Java code is released I can hopefuly submit a pull request for the Gem, which will be more elegant and better thought out, but for now if you need the hack here is the code:

require 'rubygems'
require 'bundler/setup'
require 'selenium/webdriver'
require 'browsermob/proxy'

#Patch required for proxy support - note: proxy support only added in beta-4 (not yet released as binary) and the patch is required for the gem to use this
#functionality.  Once beta-4 is released (Java Code) then a pull request could be submitted for the Gem.
module BrowserMob
  module Proxy
    class Client
      def self.from(server_url)
        port = JSON.parse(
          RestClient.post(URI.join(server_url, "proxy?httpProxy=myproxyhost:80").to_s, '')
        ).fetch('port')

        uri = URI.parse(File.join(server_url, "proxy", port.to_s))
        resource = RestClient::Resource.new(uri.to_s)

        Client.new resource, uri.host, port
      end
    end
  end
end

server = BrowserMob::Proxy::Server.new("/Users/robbim02/dev/utils/browsermob-proxy-2.0-beta-3/bin/browsermob-proxy") #=> #<BrowserMob::Proxy::Server:0x000001022c6ea8 ...>

server.start

proxy = server.create_proxy #=> #<BrowserMob::Proxy::Client:0x0000010224bdc0 ...>

profile = Selenium::WebDriver::Firefox::Profile.new #=> #<Selenium::WebDriver::Firefox::Profile:0x000001022bf748 ...>
profile.proxy = proxy.selenium_proxy

driver = Selenium::WebDriver.for :firefox, :profile => profile

proxy.new_har "google"
driver.get "http://www.google.com"

har = proxy.har #=> #<HAR::Archive:0x-27066c42d7e75fa6>
har.entries.first.request.url #=> "http://google.com"
har.save_to "tmp/google.har"

proxy.close
driver.quit

Cucumber 'requires' option - autoload magic for your objects

24 Nov 2011 | By Matt Robbins | Comments | Tags testing cucumber ruby

Once your cucumber projects get to a certain size you almost certainly start to think about moving away from monolithic steps files and toward a more object based approach.

This blog post describes how you might go about doing this and is definitely worth a read.

So, I wanted to have a project structure something like that depicted below:

/~features/
/ /~examples/
/ / /~step-definitions/
/ / / /-steps.rb
/ / /-example.feature
/ /~support/
/ / /-env.rb/
/ / /~objects/
/ / / /-myobjects.rb

And the good news is that this worked fine when I ran my cukes as follows:

cucumber -p default

However, when I ran with a specific feature it blew up as it failed to load my custom objects (myobjects.rb).

cucumber -p default features/examples/example.feature
uninitialized constant MyCustomObject (NameError)

So like every good hacker I started to delve down into the innards of the Cucumber runtime code, which whilst interesting was unnecessary had I read the help!!!

-r, --require LIBRARY|DIR        Require files before executing the features. If this
                                     option is not specified, all *.rb files that are
                                     siblings or below the features will be loaded auto-
                                     matically. Automatic loading is disabled when this
                                     option is specified, and all loading becomes explicit.
                                     Files under directories named "support" are always
                                     loaded first.
                                     This option can be specified multiple times.

Note if you run a feature with tags then all code under /features will be pulled in, as when you run with no specificity.

So to always include all your code under /features, but still run a single feature - do the following:

cucumber -p default --require features features/examples/example.feature

Custom User-Agent using Selenium-Webdriver Remote and Ruby bindings/Capybara

08 Nov 2011 | By Matt Robbins | Comments | Tags testing webdriver selenium cucumber ruby

As far as I understand it the Selenium-Webdriver project made an architectural decision not to provide an API for maniplulating headers when running ‘in-browser’ tests. This implied that the only way of doing this would be to use an intermediate proxy such as browsermob-proxy, which for most people might be fine. However within my work infrastructure introducing such a proxy was not feasible.

So when I needed to do some ‘in-browser’ testing using different User-Agents I was delighted to find there is in fact a way around this…using Chrome Switches.

You set these using the Webdriver capabilities when starting your tests, below is an example using the Ruby bindings directly and as an alternative using Capybara. This example is using the API for the client bindings directly (i.e. opening browser on your local machine)

#starting the browser from the client bindings, setting a custom User-Agent
driver = Selenium::WebDriver.for :chrome, :switches => ['--user-agent=Mozilla/5.0 (PLAYSTATION 3; 3.55)']
#example using Capybara (0.4.1.2)
opts[:switches] = ['--user-agent=Mozilla/5.0 (PLAYSTATION 3; 3.55)']
Capybara::Driver::Selenium.new(app,opts)

The api is a little different when using the remote Webdriver…

#starting the browser from the remote bindings, setting a custom User-Agent
remote_opts['chrome.switches'] = ['--user-agent=Mozilla/5.0 (PLAYSTATION 3; 3.55)']
remote_caps = Selenium::WebDriver::Remote::Capabilities.new(remote_opts)
driver = WebDriver.for(:remote, :desired_capabilities => remote_caps)
#example using Capybara (0.4.1.2)
remote_opts['chrome.switches'] = ['--user-agent=Mozilla/5.0 (PLAYSTATION 3; 3.55)']
remote_caps = Selenium::WebDriver::Remote::Capabilities.new(remote_opts)
opts[:desired_capabilities] = caps
Capybara::Driver::Selenium.new(app,opts)

Note: if your using Capybara 1 or greater I think the only difference would be the namespacing of the Selenium Driver…so something like:

Capaybara::Selenium::Driver.new(app,opts)

How to maximise Gvim in Ubuntu

01 Oct 2011 | By Matt Robbins | Comments | Tags gvim vim ubuntu

Something that has always troubled me is getting GVim to maximise when using Ubuntu…“WHATTTT” you say “that’s easy…just add this to your .gvimrc”

set lines=999
set columns=999

Well that did work - but it always moved Gvim into another workspace - one of those things that doesn’t really matter, but never the less you just can’t let go…it annoyed me everytime I opened GVim.

At last I have found a much better solution…one that I am sure any seasoned Ubuntu user knows about but which I had never heard of…Devilspie

Roughly speaking this is what you do:

Install devilspie:

sudo apt-get install devilspie

Create a script:

cd ~/.devilspie
touch max_gvim.ds

Edit the script and add the follwing:

(if
  (contains(window_name) "Vim")
  (maximize)
 )

Add devilspie to your startup applications and voila…maximised GVim!

How to serve rubygems over ssl using client certification and Bundler

13 Sep 2011 | By Matt Robbins | Comments | Tags testing ruby bundler

I was recently researching whether we could serve gems over a client certified ssl connection, so thought I would drop the results here in order that others can do the same if they need to.

Assuming you have have your Apache setup to do client certification and you have generated self signed certs for testing, then you can serve gems by doing the following:

  1. Create a /gem directory in the document root
  2. Run the index command from the directory above the /gem directory
gem generate_index -d .

I am assuming you are using Bundler to manage your gem dependencies and that you invoke Bundler from the command line. If this is the case then you need to do three things:

  1. Monkey Patch rubygems to do the client certifaction
  2. Invoke Bundler programatically after this patch has been applied (note, invoking it as a shell command won’t work because this will start a new process and your monkey patch will not be effective)
  3. Provide the command line options you would normally pass to Bundler as config in the file .bundle/config. Note you can’t provide these programatically because Bundler uses Thor to parse command line options and Thor will freeze the options and prevent you from meddling with the resulting hash in your patch.

Here is the code I am using to invoke bundler:

require 'rubygems/remote_fetcher'
require 'bundler'
require 'bundler/cli'

class Gem::RemoteFetcher
  def connection_for(uri)
    net_http_args = [uri.host, uri.port]

    if @proxy_uri then
      net_http_args += [
        @proxy_uri.host,
        @proxy_uri.port,
        @proxy_uri.user,
        @proxy_uri.password
      ]
    end

    connection_id = [Thread.current.object_id, *net_http_args].join ':'
    @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
    connection = @connections[connection_id]

    if uri.scheme == 'https' and not connection.started? then
      require 'net/https'
      connection.use_ssl = true
      pem = File.read("/path/to/your/certwithkey.pem")
      connection.cert = OpenSSL::X509::Certificate.new(pem)
      connection.key = OpenSSL::PKey::RSA.new(pem)
      connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end

    connection.start unless connection.started?

    connection
  rescue Errno::EHOSTDOWN => e
    raise FetchError.new(e.message, uri)
  end
end

Bundler::CLI.new.send('install')

Here is my config file which lives in the .bundle/config file within the project structure and which simulates the command line options ‘–binstubs –path vendor/bundle’:

---
BUNDLE_PATH: vendor/bundle
BUNDLE_BIN: bin
BUNDLE_DISABLE_SHARED_GEMS: "1"

By the way…if you know a cleaner way to do this…please let me know!!

Checking an element has focus using Selenium-Webdriver and Capybara

11 Jul 2011 | By Matt Robbins | Comments | Tags testing capybara webdriver selenium cucumber

Have you ever wanted to check which element currently has focus using Capybara and/or Selenium-Webdriver? The API is not totally obvious so I thought I would put up one option here.

Using vanilla Webdriver:

focused_element = driver.switch_to.active_element 
#now check for something on the found element

Using Capybara:

focused_element = page.driver.browser.switch_to.active_element
#now check for something on the found element

Note: there is no direct api for this in Capybara as far as I am aware, hence the need to revert to the underlying driver.

All Hail Selenium Grid 2

06 Jul 2011 | By Matt Robbins | Comments | Tags testing grid webdriver selenium

NOTE: this post is based on the selenium-server-standalone-2.0.0 release.

So we finally have Grid 2, Grid 2 is a complete re-write of Selenium Grid and brings us the following features:

  1. Support for Selenium-Webdriver (Selenium 2)
  2. Full backwards compatibility with Selenium-RC (Selenium 1)
  3. Significant optimisations both in terms of efficiency and functionality, the headline being you no longer need a 1:1 mapping betweeen RC instances and browsers which meant huge memory consumption. Now a single node server can support all browsers for that node.

If you have never heard of Selenium Grid we are talking about the ability to run distributed parallel Selenium tests across multiple machines, physical or virtual and all the administrative functionality that you would expect to need to support that.

I have only just started looking at Grid 2 so this is going to be a very brief overview. Hopefuly as I explore its functionality further i’ll do some more involved posts.

The two most useful sources of information I have found thus far are:

  1. This page on the Selenium Wiki
  2. This video from the Selenium Conference of a presentation by the authors…

Pretty much everything you need to get started is on the Wiki page but below are a few things that might be useful to clarify in order to get the ‘out of the box’ behaviour working.

To start the admin server:

java -jar selenium-server-standalone-2.0rc3.jar -role hub

To start the node server:

java -jar selenium-server-standalone-2.0rc3.jar -role webdriver -hub http://127.0.0.1:4444/grid/register -port 5555 (Webdriver)
java -jar selenium-server-standalone-2.0rc3.jar -role rc -hub http://127.0.0.1:4444/grid/register -port 5556 (RC)

Note: the port numbers are not particularly significant but if you want to run Webdriver and RC nodes they must run on different ports.

Once this is running you should be able to browse to the admin console and see the status of your grid:

http://hostname:4444/grid/console

grid2 setup

IMPORTANT - By default the grid will start with a default set of browsers, 5 Firefox, 5 Chrome, 1 IE - you can change this configuration by supplying command line options when you start the node server, see the Selenium Wiki for more details.

Alternatively you can supply a config file in json format e.g.

{
"capabilities":
        [
                {
                        "browserName":"firefox",
                        "maxInstances":5
                },
                {
                        "browserName":"chrome",
                        "maxInstances":5
                },
                {
                        "browserName":"internet explorer",
                        "version":"7",
                        "platform":"WINDOWS",
                        "maxInstances":1
                }
        ],
"configuration":
        {
                "cleanUpCycle":2000,
                "timeout":30000,
                "proxy":"org.openqa.grid.selenium.proxy.WebDriverRemoteProxy",
                "maxSession":5,
                "url":"http://192.168.102.130:5555/wd/hub",

        }
}

Then start the node as follows:

java -jar selenium-server-standalone-2.0rc3.jar -role webdriver -nodeConfig myconfig.json -hub http://127.0.0.1:4444/grid/register

Note: I found that when using Windows VMWare images you need to ensure the following:

  1. Configure nodes with actual IP address as above (as opposed to localhost, even if running node on same machine as hub)
  2. Windows Firewall is turned off (obvious but worth mentioning)

Running Tests

It does not matter whether you run tests from plain Selenium-Webdriver, Cucumber driving Selenium-Webdriver, Cucumber & Capybara using Selenium-Webdriver, the determining factor that will result in your tests running on the grid is the options and capabilities you set when configuring your driver, here is an example of some working sets of Capabilities that I have been using when running with Cucumber/Capybara/Selenium-Webdriver.

IE

{:url=>"http://hostname:5555/wd/hub", :browser=>:remote, :desired_capabilities=>#<Selenium::WebDriver::Remote::Capabilities:0x47336dc2 @capabilities={:browser_name=>:"internet explorer", :version=>"", :platform=>"WINDOWS", :javascript_enabled=>false, :css_selectors_enabled=>false, :takes_screenshot=>false, :native_events=>false, :rotatable=>false, :firefox_profile=>nil, :proxy=>nil, :url=>"http://192.168.102.128:5555/wd/hub"}>, :http_client=>#<Selenium::WebDriver::Remote::Http::Default:0x17d1e01f @proxy=nil, @timeout=nil>}

Firefox

{:url=>"http://hostname:5555/wd/hub", :browser=>:remote, :desired_capabilities=>#<Selenium::WebDriver::Remote::Capabilities:0x3ced3512 @capabilities={:browser_name=>:firefox, :version=>"", :platform=>"WINDOWS", :javascript_enabled=>false, :css_selectors_enabled=>false, :takes_screenshot=>false, :native_events=>false, :rotatable=>false, :firefox_profile=>#<Selenium::WebDriver::Firefox::Profile:0x1e64a937 @extensions={}, @secure_ssl=false, @load_no_focus_lib=false, @untrusted_issuer=true, @model=nil, @additional_prefs={"network.proxy.type"=>"1", "network.proxy.no_proxies_on"=>"*.noproxy.myhost.co.uk", "network.proxy.http"=>"proxy_host", "network.proxy.https"=>"proxy_host", "network.proxy.http_port"=>"80", "network.proxy.https_port"=>"80"}, @native_events=false>, :proxy=>nil, :url=>"http://hostname:5555/wd/hub"}>, :http_client=>#<Selenium::WebDriver::Remote::Http::Default:0x53133637 @proxy=nil, @timeout=nil>}

Parallelism on the Client

If you are using ruby to drive your tests this is most easily done using either the parallel gem or it’s more featureful brother the the parallel tests gem.

Essentially the parallel family of gems will allow you to run either parallel across cores and/or threads. Obviously the choice will be determined by your execution environment e.g. if running in a CI queue your job may only have a single core therefore you are better to go for multiple threading also when all we are doing is feeding some code to a remote server that is going to run the tests then having multiple threads may seem more sensible.

Conclusions

So there you have it…I have bearly scratched the surface of what I believe Grid 2 can offer, from listening to the presentation by the authors there is mouch more to discover. Hopefuly I will get a chance to bring some of this out in future posts.

Qunit in the cloud - with Selenium Webdriver, RC and SauceLabs

30 Jun 2011 | By Matt Robbins | Comments | Tags testing qunit webdriver selenium javascript

I’ll steer well clear of the debate around peoples preferences for Javascript unit testing frameworks and just settle on the fact that when all’s said and done Qunit seems to be the one most commonly used.

Some of the devs in my team wanted an easy way to run Qunit tests against multiple browsers, and whilst I would have loved to offer them a local Selenium Grid and a bunch of VMs I couldn’t…the maintenance and cost overhead would have been too much.

However, I had been using the great service provided by the folks at SauceLabs for my Cucumber tests and thought that this would be a quick win for running Qunit tests against a bunch of browsers. Essentially SauceLabs provide a Selenium Grid in the cloud…and provides support for both Selenim-Webdriver and ‘old-school’ Selenium-RC.

I had previously used a nice little project written one of the people at Sauce for running parallel Cucumber tests using Selenium-RC, which can be found here.

Note this uses the excellent parallel gem to allow easy multi threading, and you may also want to check out parallel_tests for a super easy way of running Cucumber tests across multiple cores.

Taking cucumber_sauce as my starting point I added support for Selenium-Webdriver and got rid of the Cucumber overhead (no need as all we are doing is hitting a single Qunit test page).

I ended up with qunit-sauce, nothing clever and most of it was taken from the original project by Sean Grove (thank you Sean)…but it will certainly serve a useful purpose for my team…and hopefully squash a few extra bugs on the way!

Capybara Android

24 Jun 2011 | By | Comments | Tags

layout: post title: Running Capybara with Selenium-Webdriver Android Driver author: Matt Robbins categories: - testing - cucumber - capybara - android —

The Selenium-Webdriver project has developed a driver for Android which can be run both on the emulator or a physical device. This page has pretty much all you need to get going, but I thought I would clarify a few things that confused me when I ws trying to get this all working through Cucumber and Capybara.

When setting up the emulator as per the instructions on the Selenium wiki, in order to generate a list of targets you need to ensure you have actually installed the relevant SDK packages. To do this start the Android SDK and AVD Manager:

~/android-sdk/tools/android

Which should bring up a screen similar to the one below, select ‘Installed packages’ and add some…then you’ll have some targets to select!

android packages

After you have followed all the other steps and started your emulator then you should be able to write your features and steps as normal (though there are some gotchas…see below). You env.rb should look something like the one below, then run your tests through Cucumber as normal.

require 'cucumber'
require 'capybara/cucumber'

Capybara.register_driver :selenium_android do |app|
  Capybara::Driver::Selenium.new(app, {:browser => :remote, :url => "http://localhost:8080/wd/hub"})
end

Capybara.current_driver = :selenium_android

Gotchas…

  1. I found that I needed to start the Webdriver server (Jetty Server) on the emulator manually i.e. open the emulator then select to view applications and select ‘Webdriver’
  2. I also found that if I started the actual browser manually on the emulator this prevented tests from running and I received ‘End of file reached (EOFError)’ errors
  3. In order to see tests running in the browser it seemed that you needed to have the Webdriver server application open and visible on the device emulator
  4. Lots of the functionality you’re used to using in Capybara may not work as expected, this is due to very limited support for X-Path and CSS selectors in the Android driver…it may actually be better to use the Selenium-Webdriver gem directly.
1 2 3