mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-05 15:31:47 +01:00
Started moving selenium on rails tests over to RSpec stories. See the updated README_DEVELOPERS for info on running the new tests.
This commit is contained in:
parent
85dc6f4898
commit
d47e863dfc
12 changed files with 257 additions and 74 deletions
|
|
@ -10,12 +10,13 @@
|
||||||
:title =>"Delete this note",
|
:title =>"Delete this note",
|
||||||
:class=>"delete_item",
|
:class=>"delete_item",
|
||||||
:id => "delete_note_"+note.id.to_s),
|
:id => "delete_note_"+note.id.to_s),
|
||||||
:update => dom_id(note),
|
{:update => dom_id(note),
|
||||||
:loading => visual_effect(:fade, dom_id(note, 'container')),
|
:loading => visual_effect(:fade, dom_id(note, 'container')),
|
||||||
:complete => "Element.remove('#{dom_id(note, 'container')}');",
|
:complete => "Element.remove('#{dom_id(note, 'container')}');",
|
||||||
:url => note_path(note),
|
:url => note_path(note),
|
||||||
:method => :delete,
|
:method => :delete,
|
||||||
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" ) + " " -%>
|
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" },
|
||||||
|
{ :class => 'delete_note' }) -%>
|
||||||
<%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
|
<%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
|
||||||
"Element.toggle('#{dom_id(note)}'); Element.toggle('#{dom_id(note, 'edit')}'); Effect.Appear('#{dom_id(note, 'edit')}'); Form.focusFirstElement('#{dom_id(note, 'edit_form')}');" ) + " | " %>
|
"Element.toggle('#{dom_id(note)}'); Element.toggle('#{dom_id(note, 'edit')}'); Effect.Appear('#{dom_id(note, 'edit')}'); Form.focusFirstElement('#{dom_id(note, 'edit_form')}');" ) + " | " %>
|
||||||
<%= link_to("In: " + note.project.name, project_path(note.project), :class=>"footer_link" ) %> |
|
<%= link_to("In: " + note.project.name, project_path(note.project), :class=>"footer_link" ) %> |
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,10 @@ To avoid showing the migrations as tests are run, add the following to your data
|
||||||
|
|
||||||
If you want to run tests using another database, that's fine, too. Just change your database.yml accordingly.
|
If you want to run tests using another database, that's fine, too. Just change your database.yml accordingly.
|
||||||
|
|
||||||
3. SELENIUM TESTS
|
3. SELENIUM TESTS (Selenium on Rails)
|
||||||
|
|
||||||
|
This testing style is deprecated and are being moved over to Selenium via RSpec stories by lukemelia (See #4 below for the new style).
|
||||||
|
|
||||||
To run selenium tests, start Tracks in test mode using
|
To run selenium tests, start Tracks in test mode using
|
||||||
|
|
||||||
script/server -e test
|
script/server -e test
|
||||||
|
|
@ -24,4 +26,25 @@ Then open a browser to
|
||||||
|
|
||||||
and interact with the test runner.
|
and interact with the test runner.
|
||||||
|
|
||||||
For more information about Selenium on Rails, see vendor/plugins/selenium-on-rails/README
|
For more information about Selenium on Rails, see vendor/plugins/selenium-on-rails/README
|
||||||
|
|
||||||
|
4. RSPEC STORY RUNNER TESTS
|
||||||
|
|
||||||
|
To run the stories, which are browser tests using selenium, start Tracks in test mode using
|
||||||
|
|
||||||
|
rake db:test:prepare
|
||||||
|
script/server -e test
|
||||||
|
|
||||||
|
Then start Selenium by running
|
||||||
|
|
||||||
|
selenium
|
||||||
|
|
||||||
|
which is a script installed with the Selenium gem (sudo gem install Selenium)
|
||||||
|
|
||||||
|
Once the site and selenium server are running, then run all stories with
|
||||||
|
|
||||||
|
script/story
|
||||||
|
|
||||||
|
or a specific set with
|
||||||
|
|
||||||
|
script/story stories/login/*.story
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ require 'spec'
|
||||||
require 'spec/rails'
|
require 'spec/rails'
|
||||||
require 'spec/story'
|
require 'spec/story'
|
||||||
require 'webrat/selenium'
|
require 'webrat/selenium'
|
||||||
|
require 'action_controller/test_process'
|
||||||
|
|
||||||
module Spec
|
module Spec
|
||||||
module Story
|
module Story
|
||||||
|
|
@ -47,10 +48,30 @@ class SeleniumRailsStory < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def should_not_see(text_or_regexp)
|
||||||
|
if text_or_regexp.is_a?(Regexp)
|
||||||
|
response.should_not have_tag("*", text_or_regexp)
|
||||||
|
else
|
||||||
|
response.should_not have_tag("*", /#{Regexp.escape(text_or_regexp)}/i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def response
|
def response
|
||||||
webrat_session.response_body
|
webrat_session.response_body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def logged_in_as(user)
|
||||||
|
visits("/selenium_helper/login?as=#{user.login}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def selenium
|
||||||
|
SeleniumDriverManager.instance.running_selenium_driver
|
||||||
|
end
|
||||||
|
|
||||||
|
def badge_count_should_show(count)
|
||||||
|
response.should have_tag('#badge_count', count.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
def method_missing(name, *args)
|
def method_missing(name, *args)
|
||||||
if webrat_session.respond_to?(name)
|
if webrat_session.respond_to?(name)
|
||||||
webrat_session.send(name, *args)
|
webrat_session.send(name, *args)
|
||||||
|
|
@ -70,6 +91,7 @@ end
|
||||||
|
|
||||||
class DatabaseResetListener
|
class DatabaseResetListener
|
||||||
include Singleton
|
include Singleton
|
||||||
|
|
||||||
def scenario_started(*args)
|
def scenario_started(*args)
|
||||||
if defined?(ActiveRecord::Base)
|
if defined?(ActiveRecord::Base)
|
||||||
connection = ActiveRecord::Base.connection
|
connection = ActiveRecord::Base.connection
|
||||||
|
|
@ -79,24 +101,23 @@ class DatabaseResetListener
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def scenario_succeeded(*args)
|
def method_missing sym, *args, &block
|
||||||
|
# ignore all messages you don't care about
|
||||||
end
|
end
|
||||||
alias :scenario_pending :scenario_succeeded
|
|
||||||
alias :scenario_failed :scenario_succeeded
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class CookieResetListener
|
class CookieResetListener
|
||||||
include Singleton
|
include Singleton
|
||||||
|
|
||||||
def scenario_started(*args)
|
def scenario_started(*args)
|
||||||
%w[tracks_login auth_token _session_id].each do |cookie_name|
|
%w[tracks_login auth_token _session_id].each do |cookie_name|
|
||||||
SeleniumDriverManager.instance.running_selenium_driver.get_eval("window.document.cookie = '#{cookie_name}=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';")
|
SeleniumDriverManager.instance.running_selenium_driver.get_eval("window.document.cookie = '#{cookie_name}=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def scenario_succeeded(*args)
|
def method_missing sym, *args, &block
|
||||||
|
# ignore all messages you don't care about
|
||||||
end
|
end
|
||||||
alias :scenario_pending :scenario_succeeded
|
|
||||||
alias :scenario_failed :scenario_succeeded
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class Spec::Story::Runner::ScenarioRunner
|
class Spec::Story::Runner::ScenarioRunner
|
||||||
|
|
|
||||||
41
stories/notes/view_add_remove.story
Normal file
41
stories/notes/view_add_remove.story
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
Story: View, add, remove notes
|
||||||
|
|
||||||
|
As a Tracks user
|
||||||
|
I want to view, add, and remove notes
|
||||||
|
So that I can keep important information easily accessible
|
||||||
|
|
||||||
|
Scenario: View and toggle notes
|
||||||
|
|
||||||
|
Given a logged in user Luis
|
||||||
|
And Luis has two projects with one note each
|
||||||
|
When Luis visits the notes page
|
||||||
|
Then two notes should be visible
|
||||||
|
And the badge should show 2
|
||||||
|
When Luis clicks Toggle Notes
|
||||||
|
Then the body of the notes should be shown
|
||||||
|
|
||||||
|
Scenario: Add a new note
|
||||||
|
|
||||||
|
Given a logged in user Luis
|
||||||
|
And Luis has one project Pass Final Exam with no notes
|
||||||
|
When Luis adds a note from the Pass Final Exam project page
|
||||||
|
Then Luis should see the note on the Pass Final Exam project page
|
||||||
|
And Luis should see the note on the notes page
|
||||||
|
And the badge on the notes page should show 1
|
||||||
|
|
||||||
|
Scenario: Delete note from notes page
|
||||||
|
|
||||||
|
Given a logged in user Luis
|
||||||
|
And Luis has one project Pass Final Exam with 2 notes
|
||||||
|
When Luis visits the notes page
|
||||||
|
And Luis deletes the first note
|
||||||
|
Then the first note should disappear
|
||||||
|
Then the badge should show 1
|
||||||
|
|
||||||
|
Scenario: Link to note
|
||||||
|
Given a logged in user Luis
|
||||||
|
And Luis has one project Pass Final Exam with 1 note
|
||||||
|
When Luis visits the Pass Final Exam project page
|
||||||
|
And clicks the icon next to the note
|
||||||
|
Then he should see the note text
|
||||||
|
|
||||||
|
|
@ -1,19 +1,6 @@
|
||||||
steps_for :login do
|
steps_for :login do
|
||||||
Given "an admin user Reinier with the password abracadabra" do
|
include_steps_for :users
|
||||||
@reinier = User.create!(:login => 'reinier', :password => 'abracadabra', :password_confirmation => 'abracadabra', :is_admin => true)
|
|
||||||
@reinier.create_preference
|
|
||||||
end
|
|
||||||
|
|
||||||
Given "Reinier is not logged in" do
|
|
||||||
end
|
|
||||||
|
|
||||||
Given "no users exist" do
|
|
||||||
User.delete_all
|
|
||||||
end
|
|
||||||
|
|
||||||
Given "a visitor named Reinier" do
|
|
||||||
end
|
|
||||||
|
|
||||||
When "Reinier submits the login form with an incorrect password" do
|
When "Reinier submits the login form with an incorrect password" do
|
||||||
fills_in 'Login', :with => 'reinier'
|
fills_in 'Login', :with => 'reinier'
|
||||||
fills_in 'Password', :with => 'incorrectpass'
|
fills_in 'Password', :with => 'incorrectpass'
|
||||||
|
|
|
||||||
94
stories/steps/notes.rb
Normal file
94
stories/steps/notes.rb
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
steps_for :notes do
|
||||||
|
include_steps_for :users
|
||||||
|
|
||||||
|
Given "Luis has two projects with one note each" do
|
||||||
|
project_a = @luis.projects.create!(:name => 'project A')
|
||||||
|
project_a.notes.create!(:user_id => @luis.id, :body => 'note for project A')
|
||||||
|
project_b = @luis.projects.create!(:name => 'project B')
|
||||||
|
project_b.notes.create!(:user_id => @luis.id, :body => 'note for project B')
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "Luis has one project Pass Final Exam with no notes" do
|
||||||
|
@exam_project = @luis.projects.create!(:name => 'Pass Final Exam')
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "Luis has one project Pass Final Exam with 1 note" do
|
||||||
|
Given "Luis has one project Pass Final Exam with no notes"
|
||||||
|
@exam_project.notes.create!(:user_id => @luis.id, :body => 'exam note 1')
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "Luis has one project Pass Final Exam with 2 notes" do
|
||||||
|
Given "Luis has one project Pass Final Exam with 1 note"
|
||||||
|
@exam_project.notes.create!(:user_id => @luis.id, :body => 'exam note 2')
|
||||||
|
end
|
||||||
|
|
||||||
|
When "Luis visits the notes page" do
|
||||||
|
visits '/notes'
|
||||||
|
end
|
||||||
|
|
||||||
|
When "Luis adds a note from the Pass Final Exam project page" do
|
||||||
|
When "Luis visits the Pass Final Exam project page"
|
||||||
|
clicks_link 'Add a note', :wait => :ajax
|
||||||
|
fills_in 'new_note_body', :with => 'new exam note'
|
||||||
|
clicks_button 'Add note', :wait => :ajax
|
||||||
|
end
|
||||||
|
|
||||||
|
When "Luis visits the Pass Final Exam project page" do
|
||||||
|
visits "/projects/#{@exam_project.to_param}"
|
||||||
|
end
|
||||||
|
|
||||||
|
When "Luis deletes the first note" do
|
||||||
|
selenium.click "css=a.delete_note"
|
||||||
|
selenium.get_confirmation.should =~ /delete/
|
||||||
|
end
|
||||||
|
|
||||||
|
When "clicks the icon next to the note" do
|
||||||
|
selenium.click "css=a.link_to_notes"
|
||||||
|
wait_for_page_to_load
|
||||||
|
end
|
||||||
|
|
||||||
|
When "Luis clicks Toggle Notes" do
|
||||||
|
clicks_link 'Toggle notes', :wait => :effects
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "the body of the notes should be shown" do
|
||||||
|
wait_for_effects
|
||||||
|
selenium.is_visible("css=body.notes").should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "Luis should see the note on the Pass Final Exam project page" do
|
||||||
|
should_see "new exam note"
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "Luis should see the note on the notes page" do
|
||||||
|
visits '/notes'
|
||||||
|
should_see "new exam note"
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "the badge on the notes page should show 1" do
|
||||||
|
badge_count_should_show(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "the first note should disappear" do
|
||||||
|
wait_for_ajax_and_effects
|
||||||
|
should_not_see 'exam note 1'
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "the badge should show 1" do
|
||||||
|
wait_for_ajax_and_effects
|
||||||
|
badge_count_should_show(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "the badge should show 2" do
|
||||||
|
badge_count_should_show(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "two notes should be visible" do
|
||||||
|
should_see 'note for project A'
|
||||||
|
should_see 'note for project B'
|
||||||
|
end
|
||||||
|
|
||||||
|
Then "he should see the note text" do
|
||||||
|
should_see 'exam note 1'
|
||||||
|
end
|
||||||
|
end
|
||||||
30
stories/steps/users.rb
Normal file
30
stories/steps/users.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
steps_for :users do
|
||||||
|
|
||||||
|
Given "an admin user Reinier with the password abracadabra" do
|
||||||
|
@reinier = User.create!(:login => 'reinier', :password => 'abracadabra', :password_confirmation => 'abracadabra', :is_admin => true)
|
||||||
|
@reinier.create_preference
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "an admin user Reinier" do
|
||||||
|
Given "an admin user Reinier with the password abracadabra"
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "a logged in user Luis" do
|
||||||
|
@luis = User.create!(:login => 'luis', :password => 'sesame', :password_confirmation => 'sesame', :is_admin => false)
|
||||||
|
@luis.create_preference
|
||||||
|
logged_in_as @luis
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "no users exist" do
|
||||||
|
User.delete_all
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "Reinier is not logged in" do
|
||||||
|
#nothing to do
|
||||||
|
end
|
||||||
|
|
||||||
|
Given "a visitor named Reinier" do
|
||||||
|
#nothing to do
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
setup :fixtures => :all
|
|
||||||
login :as => 'admin'
|
|
||||||
open "/notes/"
|
|
||||||
assert_text 'badge_count', '2'
|
|
||||||
|
|
||||||
# add new note
|
|
||||||
open "/projects/1"
|
|
||||||
click "css=#add_note_href"
|
|
||||||
type "css=#new_note_body", "new note"
|
|
||||||
click "add-new-note"
|
|
||||||
|
|
||||||
# wait until new note is saved
|
|
||||||
wait_for_text_not_present "new note"
|
|
||||||
wait_for_text_present "new note"
|
|
||||||
|
|
||||||
# check badge count is one more
|
|
||||||
open "/notes/"
|
|
||||||
assert_text 'badge_count', '3'
|
|
||||||
|
|
||||||
# delete note
|
|
||||||
click "css=#delete_note_1"
|
|
||||||
assert_confirmation "Are you sure that you want to delete the note '1'?"
|
|
||||||
|
|
||||||
# check badge decreased
|
|
||||||
wait_for_visible "flash"
|
|
||||||
wait_for_element_not_present "container_note_1"
|
|
||||||
assert_text 'badge_count', '2'
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
setup :fixtures => :all
|
|
||||||
login :as => 'admin'
|
|
||||||
open "/projects/1"
|
|
||||||
click_and_wait "css=#note_1 .link_to_notes"
|
|
||||||
assert_element_present "note_1"
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
setup :fixtures => :all
|
|
||||||
login :as => 'admin'
|
|
||||||
open "/notes/"
|
|
||||||
assert_element_present "note_1"
|
|
||||||
assert_element_present "note_2"
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
setup :fixtures => :all
|
|
||||||
login :as => 'admin'
|
|
||||||
open "/notes"
|
|
||||||
click "css=#toggle-notes-nav"
|
|
||||||
wait_for_element_present "css=body.notes"
|
|
||||||
|
|
@ -11,24 +11,52 @@ module Webrat
|
||||||
end
|
end
|
||||||
|
|
||||||
def fills_in(label_text, options)
|
def fills_in(label_text, options)
|
||||||
@selenium.type("webrat=#{Regexp.escape(label_text)}", "#{options[:with]}")
|
@selenium.type("webrat=#{label_text}", "#{options[:with]}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_body
|
def response_body
|
||||||
@selenium.get_html_source
|
@selenium.get_html_source
|
||||||
end
|
end
|
||||||
|
|
||||||
def clicks_button(button_text = nil)
|
def clicks_button(button_text = nil, options = {})
|
||||||
|
button_text, options = nil, button_text if button_text.is_a?(Hash) && options == {}
|
||||||
button_text ||= '*'
|
button_text ||= '*'
|
||||||
@selenium.click("button=#{button_text}")
|
@selenium.click("button=#{button_text}")
|
||||||
@selenium.wait_for_page_to_load()
|
wait_for_result(options[:wait])
|
||||||
end
|
end
|
||||||
|
|
||||||
def clicks_link(link_text)
|
def clicks_link(link_text, options = {})
|
||||||
@selenium.click("webratlink=#{Regexp.escape(link_text)}")
|
@selenium.click("webratlink=#{link_text}")
|
||||||
@selenium.wait_for_page_to_load()
|
wait_for_result(options[:wait])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def wait_for_result(wait_type)
|
||||||
|
if wait_type == :ajax
|
||||||
|
wait_for_ajax
|
||||||
|
elsif wait_type == :effects
|
||||||
|
wait_for_effects
|
||||||
|
else
|
||||||
|
wait_for_page_to_load
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_page_to_load(timeout = 15000)
|
||||||
|
@selenium.wait_for_page_to_load(timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_ajax(timeout = 15000)
|
||||||
|
@selenium.wait_for_condition "window.Ajax.activeRequestCount == 0", timeout
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_effects(timeout = 15000)
|
||||||
|
@selenium.wait_for_condition "window.Effect.Queue.size() == 0", timeout
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_ajax_and_effects
|
||||||
|
wait_for_ajax
|
||||||
|
wait_for_effects
|
||||||
|
end
|
||||||
|
|
||||||
def selects(option_text, options = {})
|
def selects(option_text, options = {})
|
||||||
id_or_name_or_label = options[:from]
|
id_or_name_or_label = options[:from]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue