Run JavaScript in Selenium tests. Easily.

March 6, 2009 Pivotal Labs

Here’s the gist of this post: gist.github.com/58876

Ever since I’ve started using Webrat, a lot of the pain of Selenium has gone away
for me. There’s still a little bit of pain though. Part of it is caused by the fact
that it’s harder than it should be to just execute arbitrary bits of JavaScript in
in your current window under test. Well no more. Here’s a helper:

module SeleniumHelpers
  # Execute JavaScript in the context of your Selenium window
  def run_javascript(javascript)
    driver.get_eval <<-JS
      (function() {
        with(this) {
          #{javascript}
        }
      }).call(selenium.browserbot.getCurrentWindow());
    JS
  end

  private

  # If running in regular Selenium context, get_eval is defined on self.
  def driver
    respond_to?(:selenium) ? send(:selenium) : self
  end
end

To use it with Cucumber, do like so:

World do |world|
  world.extend(SeleniumHelpers)
  world
end

To use it with POS, do like so:

class JavaScriptHelperTest < SeleniumTestCase
  include SeleniumHelpers

  # your tests go here...
end

Now what?

Now to run JavaScript in your Selenium window, just call run_javascript. Note
that it’s always going to return a String, so you may have to massage the output
a tad:

checked_boxes_count = run_javascript <<-JS
  jQuery('input[type=checkbox]:checked').size();
JS

checked_boxes_count         # => "3"
checked_boxes_count.to_i   # => 3

Cooler stuff

While Webrat’s DSL for traversing web apps is awesome, I’ve always found the
alternatives (Polonium for example) to not jive well with how I think. They’re
way better than talking directly to Selenium, you’re still locked in to a certain
style. The run_javascript helper makes it easier to write your own helpers that
fit your own style.

module ElementHelpers
  class Element
    def initialize(context, selector)
      @context, @selector = context, selector
    end

    def hide!
      call(:hide)
    end

    def show!
      call(:show)
    end

    def visible?
      call(:is, ':visible') == 'true'
    end

    private

    def call(fn, *args)
      @context.run_javascript <<-JS
        return jQuery(#{@selector.inspect})[#{fn.to_s.inspect}](#{args.map(&:inspect).join(', ')});
      JS
    end
  end

  def locate(selector)
    Element.new(self, selector)
  end
end

Now you can write your tests like so:

class JavaScriptHelperTest < ActiveSupport::TestCase
  include SeleniumHelpers
  include ElementHelpers

  def setup
    @element = locate('#all')
  end

  def test_visible_by_default
    assert @element.visible?
  end

  def test_hide_element
    @element.hide!
    assert ! @element.visible?
  end

  def test_show_element
    @element.hide! # setup
    @element.show!
    assert @element.visible?
  end
end

Credit should go to Brian Takita, since he did most of the hard work and I just wrote a method. Let me
know if you have any issues or ideas with the helper, and may all your tests be green.

About the Author

Biography

Previous
Sinatra Overview
Sinatra Overview

Blake Mizerany of Heroku talks about building lightweight RESTful web services with his Ruby framework Sina...

Next
Parselets and SelectorGadget
Parselets and SelectorGadget

Andrew Cantino and Kyle Maxwell talk about Parselets.com, a cross-language toolset for developer-generated ...