diff options
author | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2011-05-10 10:37:18 +0200 |
---|---|---|
committer | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2011-05-13 13:23:06 +0200 |
commit | 5c2d4c06c186d605e8ae0df958bcf287db77909c (patch) | |
tree | 86e46ae04367b64fe37b3c2b3888b332e82c30a9 | |
parent | A working rspec test (diff) | |
download | council-webapp-5c2d4c06c186d605e8ae0df958bcf287db77909c.tar.gz council-webapp-5c2d4c06c186d605e8ae0df958bcf287db77909c.tar.bz2 council-webapp-5c2d4c06c186d605e8ae0df958bcf287db77909c.zip |
Cucumber with a login feature demonstrating cookies work
-rw-r--r-- | site/Gemfile | 5 | ||||
-rw-r--r-- | site/Gemfile.lock | 48 | ||||
-rw-r--r-- | site/config/cucumber.yml | 8 | ||||
-rw-r--r-- | site/features/login.feature | 20 | ||||
-rw-r--r-- | site/features/step_definitions/login_steps.rb | 15 | ||||
-rw-r--r-- | site/features/step_definitions/web_steps.rb | 190 | ||||
-rw-r--r-- | site/features/support/env.rb | 5 | ||||
-rw-r--r-- | site/features/support/paths.rb | 36 | ||||
-rw-r--r-- | site/features/support/selectors.rb | 39 | ||||
-rw-r--r-- | site/lib/tasks/cucumber.rake | 50 | ||||
-rwxr-xr-x | site/script/cucumber | 10 |
11 files changed, 426 insertions, 0 deletions
diff --git a/site/Gemfile b/site/Gemfile index 7bac170..33fb3f8 100644 --- a/site/Gemfile +++ b/site/Gemfile @@ -6,6 +6,11 @@ group :development, :test do gem 'ruby-debug' gem 'rspec-rails' gem 'shoulda' + + gem 'cucumber-rails' + gem 'capybara' + gem 'database_cleaner' + gem 'launchy' end gem "hobo", ">= 1.3.0.pre28" diff --git a/site/Gemfile.lock b/site/Gemfile.lock index 94fd74f..e80cd7d 100644 --- a/site/Gemfile.lock +++ b/site/Gemfile.lock @@ -30,13 +30,42 @@ GEM activesupport (3.0.3) arel (2.0.9) builder (3.0.0) + capybara (0.4.1.2) + celerity (>= 0.7.9) + culerity (>= 0.2.4) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + selenium-webdriver (>= 0.0.27) + xpath (~> 0.1.3) + celerity (0.8.8) + childprocess (0.1.8) + ffi (~> 1.0.6) columnize (0.3.2) + configuration (1.2.0) + cucumber (0.10.2) + builder (>= 2.1.2) + diff-lcs (>= 1.1.2) + gherkin (>= 2.3.5) + json (>= 1.4.6) + term-ansicolor (>= 1.0.5) + cucumber-rails (0.4.0) + cucumber (>= 0.10.1) + nokogiri (>= 1.4.4) + rack-test (>= 0.5.7) + culerity (0.2.15) + database_cleaner (0.6.6) diff-lcs (1.1.2) dryml (1.3.0.pre28) actionpack (>= 3.0.0) hobo_support (= 1.3.0.pre28) erubis (2.6.6) abstract (>= 1.0.0) + ffi (1.0.7) + rake (>= 0.8.7) + gherkin (2.3.5) + json (>= 1.4.6) hobo (1.3.0.pre28) dryml (= 1.3.0.pre28) hobo_fields (= 1.3.0.pre28) @@ -49,6 +78,11 @@ GEM hobo_support (1.3.0.pre28) rails (>= 3.0.0) i18n (0.5.0) + json (1.4.6) + json_pure (1.5.1) + launchy (0.4.0) + configuration (>= 0.0.5) + rake (>= 0.8.1) linecache (0.43) mail (2.2.15) activesupport (>= 2.3.6) @@ -56,6 +90,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.16) + nokogiri (1.4.4) polyglot (0.3.1) rack (1.2.2) rack-mount (0.6.14) @@ -94,21 +129,34 @@ GEM ruby-debug-base (~> 0.10.4.0) ruby-debug-base (0.10.4) linecache (>= 0.3) + rubyzip (0.9.4) + selenium-webdriver (0.1.4) + childprocess (>= 0.1.7) + ffi (>= 1.0.7) + json_pure + rubyzip shoulda (2.11.3) sqlite3 (1.3.3) sqlite3-ruby (1.3.3) sqlite3 (>= 1.3.3) + term-ansicolor (1.0.5) thor (0.14.6) treetop (1.4.9) polyglot (>= 0.3.1) tzinfo (0.3.25) will_paginate (3.0.pre2) + xpath (0.1.3) + nokogiri (~> 1.3) PLATFORMS ruby DEPENDENCIES + capybara + cucumber-rails + database_cleaner hobo (>= 1.3.0.pre28) + launchy rails (= 3.0.3) rspec-rails ruby-debug diff --git a/site/config/cucumber.yml b/site/config/cucumber.yml new file mode 100644 index 0000000..19b288d --- /dev/null +++ b/site/config/cucumber.yml @@ -0,0 +1,8 @@ +<% +rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" +rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" +std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip" +%> +default: <%= std_opts %> features +wip: --tags @wip:3 --wip features +rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip diff --git a/site/features/login.feature b/site/features/login.feature new file mode 100644 index 0000000..8ba5be3 --- /dev/null +++ b/site/features/login.feature @@ -0,0 +1,20 @@ +Feature: Login + In order to use site + I want to be able to login + + Scenario: Email + password login + Given example user + When I am on the homepage + When I follow "Login" + Then I should be on the login page + + When I login as example user + Then I should see "You have logged in." + + Scenario: Login, then look around and see you're still logged in + Given example user + When I am on the login page + And I login as example user + + When I follow "Logged in as Example" + Then I should see "Log out" diff --git a/site/features/step_definitions/login_steps.rb b/site/features/step_definitions/login_steps.rb new file mode 100644 index 0000000..52fbcd3 --- /dev/null +++ b/site/features/step_definitions/login_steps.rb @@ -0,0 +1,15 @@ +Given /^example user$/ do + user = User.new :name => "Example", :email_address => "example@example.com", + :password => "Example" + user.save! +end + +When /^I login as "([^"]*)" with password "([^"]*)"$/ do |email, password| + When "I fill in \"login\" with \"#{email}\"" + When "I fill in \"password\" with \"#{password}\"" + When 'I press "Login"' +end + +When /^I login as example user$/ do + When 'I login as "example@example.com" with password "Example"' +end diff --git a/site/features/step_definitions/web_steps.rb b/site/features/step_definitions/web_steps.rb new file mode 100644 index 0000000..d615b65 --- /dev/null +++ b/site/features/step_definitions/web_steps.rb @@ -0,0 +1,190 @@ +require 'uri' +require 'cgi' +require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths")) +require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors")) + +module WithinHelpers + def with_scope(locator) + locator ? within(*selector_for(locator)) { yield } : yield + end +end +World(WithinHelpers) + +# Single-line step scoper +When /^(.*) within ([^:]+)$/ do |step, parent| + with_scope(parent) { When step } +end + +# Multi-line step scoper +When /^(.*) within ([^:]+):$/ do |step, parent, table_or_string| + with_scope(parent) { When "#{step}:", table_or_string } +end + +Given /^(?:|I )am on (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )go to (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )press "([^"]*)"$/ do |button| + click_button(button) +end + +When /^(?:|I )follow "([^"]*)"$/ do |link| + click_link(link) +end + +When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value| + fill_in(field, :with => value) +end + +When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field| + fill_in(field, :with => value) +end + +# Use this to fill in an entire form with data from a table. Example: +# +# When I fill in the following: +# | Account Number | 5002 | +# | Expiry date | 2009-11-01 | +# | Note | Nice guy | +# | Wants Email? | | +# +# TODO: Add support for checkbox, select og option +# based on naming conventions. +# +When /^(?:|I )fill in the following:$/ do |fields| + fields.rows_hash.each do |name, value| + When %{I fill in "#{name}" with "#{value}"} + end +end + +When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field| + select(value, :from => field) +end + +When /^(?:|I )check "([^"]*)"$/ do |field| + check(field) +end + +When /^(?:|I )uncheck "([^"]*)"$/ do |field| + uncheck(field) +end + +When /^(?:|I )choose "([^"]*)"$/ do |field| + choose(field) +end + +When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field| + attach_file(field, File.expand_path(path)) +end + +Then /^(?:|I )should see "([^"]*)"$/ do |text| + if page.respond_to? :should + page.should have_content(text) + else + assert page.has_content?(text) + end +end + +Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp| + regexp = Regexp.new(regexp) + + if page.respond_to? :should + page.should have_xpath('//*', :text => regexp) + else + assert page.has_xpath?('//*', :text => regexp) + end +end + +Then /^(?:|I )should not see "([^"]*)"$/ do |text| + if page.respond_to? :should + page.should have_no_content(text) + else + assert page.has_no_content?(text) + end +end + +Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp| + regexp = Regexp.new(regexp) + + if page.respond_to? :should + page.should have_no_xpath('//*', :text => regexp) + else + assert page.has_no_xpath?('//*', :text => regexp) + end +end + +Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value| + with_scope(parent) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should + field_value.should =~ /#{value}/ + else + assert_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value| + with_scope(parent) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should_not + field_value.should_not =~ /#{value}/ + else + assert_no_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent| + with_scope(parent) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_true + else + assert field_checked + end + end +end + +Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent| + with_scope(parent) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_false + else + assert !field_checked + end + end +end + +Then /^(?:|I )should be on (.+)$/ do |page_name| + current_path = URI.parse(current_url).path + if current_path.respond_to? :should + current_path.should == path_to(page_name) + else + assert_equal path_to(page_name), current_path + end +end + +Then /^(?:|I )should have the following query string:$/ do |expected_pairs| + query = URI.parse(current_url).query + actual_params = query ? CGI.parse(query) : {} + expected_params = {} + expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')} + + if actual_params.respond_to? :should + actual_params.should == expected_params + else + assert_equal expected_params, actual_params + end +end + +Then /^show me the page$/ do + save_and_open_page +end diff --git a/site/features/support/env.rb b/site/features/support/env.rb new file mode 100644 index 0000000..a7389e9 --- /dev/null +++ b/site/features/support/env.rb @@ -0,0 +1,5 @@ +require 'cucumber/rails' +Capybara.default_selector = :css +Capybara.default_driver = :selenium +ActionController::Base.allow_rescue = false +DatabaseCleaner.strategy = :transaction diff --git a/site/features/support/paths.rb b/site/features/support/paths.rb new file mode 100644 index 0000000..6ea5ce1 --- /dev/null +++ b/site/features/support/paths.rb @@ -0,0 +1,36 @@ +module NavigationHelpers + # Maps a name to a path. Used by the + # + # When /^I go to (.+)$/ do |page_name| + # + # step definition in web_steps.rb + # + def path_to(page_name) + case page_name + + when /the home\s?page/ + '/' + + when /the login page/ + user_login_path + + # Add more mappings here. + # Here is an example that pulls values out of the Regexp: + # + # when /^(.*)'s profile page$/i + # user_profile_path(User.find_by_login($1)) + + else + begin + page_name =~ /the (.*) page/ + path_components = $1.split(/\s+/) + self.send(path_components.push('path').join('_').to_sym) + rescue Object => e + raise "Can't find mapping from \"#{page_name}\" to a path.\n" + + "Now, go and add a mapping in #{__FILE__}" + end + end + end +end + +World(NavigationHelpers) diff --git a/site/features/support/selectors.rb b/site/features/support/selectors.rb new file mode 100644 index 0000000..0856127 --- /dev/null +++ b/site/features/support/selectors.rb @@ -0,0 +1,39 @@ +module HtmlSelectorsHelpers + # Maps a name to a selector. Used primarily by the + # + # When /^(.+) within (.+)$/ do |step, scope| + # + # step definitions in web_steps.rb + # + def selector_for(locator) + case locator + + when /the page/ + "html > body" + + # Add more mappings here. + # Here is an example that pulls values out of the Regexp: + # + # when /the (notice|error|info) flash/ + # ".flash.#{$1}" + + # You can also return an array to use a different selector + # type, like: + # + # when /the header/ + # [:xpath, "//header"] + + # This allows you to provide a quoted selector as the scope + # for "within" steps as was previously the default for the + # web steps: + when /"(.+)"/ + $1 + + else + raise "Can't find mapping from \"#{locator}\" to a selector.\n" + + "Now, go and add a mapping in #{__FILE__}" + end + end +end + +World(HtmlSelectorsHelpers)
\ No newline at end of file diff --git a/site/lib/tasks/cucumber.rake b/site/lib/tasks/cucumber.rake new file mode 100644 index 0000000..ce49e1c --- /dev/null +++ b/site/lib/tasks/cucumber.rake @@ -0,0 +1,50 @@ +unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks + +vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first +$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? + +begin + require 'cucumber/rake/task' + + namespace :cucumber do + Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| + t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. + t.fork = true # You may get faster startup if you set this to false + t.profile = 'default' + end + + Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'wip' + end + + Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'rerun' + end + + desc 'Run all features' + task :all => [:ok, :wip] + end + desc 'Alias for cucumber:ok' + task :cucumber => 'cucumber:ok' + + task :default => :cucumber + + task :features => :cucumber do + STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" + end + + # In case we don't have ActiveRecord, append a no-op task that we can depend upon. + task 'db:test:prepare' do + end +rescue LoadError + desc 'cucumber rake task not available (cucumber not installed)' + task :cucumber do + abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' + end +end + +end diff --git a/site/script/cucumber b/site/script/cucumber new file mode 100755 index 0000000..7fa5c92 --- /dev/null +++ b/site/script/cucumber @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first +if vendored_cucumber_bin + load File.expand_path(vendored_cucumber_bin) +else + require 'rubygems' unless ENV['NO_RUBYGEMS'] + require 'cucumber' + load Cucumber::BINARY +end |