BinaryWebPark

JavaScript Testing with Cucumber, Capybara, Poltergeist, PhantomJS, and the Twitter Bootstrap Modal Dialog Box

January 28, 2013

I made the title of this article a mouthful to show how many libraries you have to use to do some Javascript testing. I did what I’m about to do in this article recently in order to figure out where my Javascript tests were going wrong in a more complicated project.

What You Need to Have Installed In Your Rails Application

  1. Cucumber gem
  2. Capybara gem
  3. Poltergeist gem
  4. Twitter bootstrap (see http://railsapps.github.com/twitter-bootstrap-rails.html for a good resource on how to configure your app with bootstrap)
  5. PhantomJS installed on your system (I talk about how to install it on Ubuntu Linux here; in addition, there’s some nice installation instructions at the main PhantomJS site).

A Small Shortcut To Help You Get Up and Running

You can fork a copy of the application over at https://github.com/treble37/rails3-poltergeist-rspec-cucumber-capybara-devise-example.

The bootstrap-modal-dialog-test branch (not the master) has all the code I’m going to talk about in this upcoming tutorial. You may want to start on the master branch instead if you want to implement the changes yourself. Please note that I forked this application from someone else’s application skeleton in order to quickly prototype this test. Hooray for open source! Then install the required gems in your gemset (I’m assuming you’re using rvm) via the bundle install command.

What I actually changed in my forked repository

Change app/features/support/env.rb to use the poltergeist driver

require 'cucumber/rails'
require 'capybara/poltergeist'
:
Capybara.javascript_driver=:poltergeist

Assets

Daniel’s tutorial tells you to do it, but you need to remember to include Bootstrap’s javascript via the require directive in your application.js file as follows:

//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require_tree .

Bootstrap stuff

After pasting in the code from Daniel Kehoe’s tutorial into app/views/layouts/_navigation.html.erb, I changed the “Login” and “Logout” links to “Sign in” and “Sign out” so they would be compatible with the Cucumber/Capybara integration tests.

Adding the Bootstrap Modal Dialog HTML Code

I just copied the “Launch demo modal” code from the Bootstrap site at http://twitter.github.com/bootstrap/javascript.html#modals and pasted it into application.html.erb. One thing I did do was encapsulate it into a Boostrap container class:

<div class="container">
  <div class="content">
    <div class="row">
      <div class="span12">
        <!-- Button to trigger modal -->
        <a href="#myModal" role="button" class="btn" data-toggle="modal"
          >Launch demo modal</a
        >

        <!-- Modal -->
      </div>
    </div>
  </div>
</div>
<!--! end of .container -->

Cucumber Scenario

Since I forked a skeleton application with cucumber scenarios built in I only had to add the following scenario:

Scenario: modal dialog test
  Given homepage When I click the 'Launch demo modal' link
  And I click the 'Close' button
  Then I should see the homepage content

Cucumber Step(s)

I just added a simple step to make sure the user could see the home page content after the modal dialog box was closed:

Then /^I should see the homepage content$/ do
  page.should have_content("Listing posts")
end

Time to run the tests

At the command line, type bundle exec cucumber features/.

Hmm…an error…“undefined_method”

node_name' for nil:NilClass (NoMethodError)...

Bootstrap Modal Test

The hack

After some Googling, it seems that Capybara likes to have its input elements (like buttons and links) clicked inside a form element. So I then encapsulated the modal html code in a form tag as follows:

<!-- Button to trigger modal -->
<a class="btn" href="#myModal" data-toggle="modal">Launch demo modal</a>
<!-- Modal -->

And now all your tests should be green.

Another Cucumber Scenario

Scenario: show post in modal dialog
  Given a post has been created Given homepage
  When I click the 'Show' link
  Then I should see a popup with the post information
  And I click the 'google' link Then I should see the google home page

Cucumber Step(s)

Then /^I should see a popup with the post information$/ do
  page.should have_content("Post Title: Post X Title")
end

Then /^I should see the google home page$/ do
  current_url.should == "http://www.google.com/"
end

Add show.js.erb view

$('#myModalLabel').html('Post Title: <%=j @post.title %>');
$('#myModal .modal-body').html('Post Body: <%=j @post.body %>google');
$('#myModal').modal('show');

Time to run the tests

At the command line, type _bundle exec cucumber features/_.

Hmm…an error…(RSpec::Expectations::ExpectationNotMetError)

It turns out we need to add an @javascript tag before our scenario. This tells Capybara to use the poltergeist javascript driver so it can find the popup box that has a Javascript generated link to Google.com

@javascript
  Scenario: show post in modal dialog
    Given a post has been created
    Given homepage
    :

Now our tests pass.