Modal Dialog Present exception Selenium-Webdriver
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
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
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
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
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
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:
- Create a /gem directory in the document root
- 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:
- Monkey Patch rubygems to do the client certifaction
- 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)
- 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
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
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:
- Support for Selenium-Webdriver (Selenium 2)
- Full backwards compatibility with Selenium-RC (Selenium 1)
- 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:
- This page on the Selenium Wiki
- 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
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:
- Configure nodes with actual IP address as above (as opposed to localhost, even if running node on same machine as hub)
- 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
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
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!
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…
- 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’
- 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
- 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
- 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.