Permalink
Browse files

Write login integration tests using Webrat

Why do we need integration tests? We've been suffering a lot of
regressions in the Mephisto UI, because our functional tests don't reach
high enough up towards the browser, and whole classes of bugs manage to
slip through.

What is Webrat? Webrat is a "browser simulator" written in Ruby.  It
generates a DOM and allows us to fill in forms as though an actual user
were interacting with the site.

Why Webrat, and not Selenium, Watir, etc?  Webrat is recommended by the
Cucumber project as the default way to write user stories; it's very
fast; and it has a reasonable API.  Plus, Webrat actively maintained,
and very recent versions of Webrat can be used as a front-end to
Selenium.

Why Rails integration tests, and not Cucumber stories?  Since the people
contributing to Mephisto will largely be programmers, I decided to write
integration tests using a Ruby-based DSL.  Cucumber stories look really
interesting, but with no actual clients in the loop, the text-based format
is slightly less useful and has a steeper learning curve for programmers.

Since we're switching to a new integration testing tool, I moved a bunch of
code out of test_helper.rb and put it into our only existing integration
test, caching_test.rb.  I also switched blueprints.rb to set up user
passwords using 'password' and 'password_confirm' (instead of crypted and
salted values) to make it easier for tests to override
  • Loading branch information...
1 parent 959f828 commit d40725ed3da1b17ea5db51197d9f520bbde62bbb @emk emk committed Dec 27, 2008
View
@@ -89,6 +89,8 @@ def safe_to_load_application?
config.gem 'tzinfo', :version => '>= 0.3.12'
config.gem 'emk-safe_erb', :version => '>= 0.1.2', :lib => 'safe_erb',
:source => 'http://gems.github.com'
+ config.gem 'nokogiri', :version => '>= 1.1.0' # Used by webrat.
+ config.gem 'webrat', :version => '>= 0.3.2'
end
# Don't update this file, make custom tweaks in config/initializers/custom.rb,
View
@@ -27,8 +27,8 @@
email { Sham.email }
token { 'quentintoken' }
admin { false }
- salt { '7e3041ebc2fc05a40c60028e2c4901a81035d3cd' }
- crypted_password { '00742970dc9e6319f8019fd54864d3ea740f04b1' } # test
+ password { 'password' }
+ password_confirmation { 'password' }
end
Membership.blueprint do
@@ -22,7 +22,7 @@ def test_routing
def test_should_allow_site_admins_to_access_site
@user = User.make
Membership.make :user => @user, :site => @site, :admin => true
- @request.session[:user] = User.authenticate_for(@site, @user.login, 'test')
+ @request.session[:user] = User.authenticate_for(@site, @user.login, 'password')
get :index
assert_response :success
@@ -31,7 +31,7 @@ def test_should_allow_site_admins_to_access_site
def test_should_allow_site_members_to_acces_overview
@user = User.make
Membership.make :user => @user, :site => @site, :admin => false
- @request.session[:user] = User.authenticate_for(@site, @user.login, 'test')
+ @request.session[:user] = User.authenticate_for(@site, @user.login, 'password')
get :index
assert_response :success
@@ -45,7 +45,7 @@ def test_should_require_http_auth_on_feed
def test_should_allow_http_auth_on_feed
@user = User.make
Membership.make :user => @user, :site => @site, :admin => true
- @request.env['HTTP_AUTHORIZATION'] = "Basic #{Base64.encode64("#{@user.login}:test")}"
+ @request.env['HTTP_AUTHORIZATION'] = "Basic #{Base64.encode64("#{@user.login}:password")}"
get :feed
assert_response :success
end
@@ -72,7 +72,7 @@ def test_should_sort_future_items_in_todays_events
Membership.make :user => @admin, :site => @site, :admin => true
end
- @request.session[:user] = User.authenticate_for(@site, @admin.login, 'test')
+ @request.session[:user] = User.authenticate_for(@site, @admin.login, 'password')
get :index
assert assigns(:todays_events).include?(@event1), "#{assigns(:todays_events).collect(&:id).inspect}"
assert assigns(:todays_events).include?(@event2), "#{assigns(:todays_events).collect(&:id).inspect}"
@@ -1,4 +1,31 @@
require File.dirname(__FILE__) + '/../test_helper'
+
+# This file is much older than our other integration tests, and it predates
+# our decision to use Webrat. So we want to keep the code and techniques
+# in this file as isolated as possible from our other integration tests.
+
+class ActionController::Integration::Session
+ def login_as(login)
+ post 'http://test.host/account/login', :login => login, :password => 'test'
+ assert request.session[:user]
+ assert redirect?
+ end
+
+ def get_with_basic(url, options = {})
+ get url, nil, 'authorization' => "Basic #{Base64.encode64("#{options[:login]}:test")}"
+ end
+
+ def assert_redirected_to(url)
+ assert redirect?
+ assert_equal url, interpret_uri(headers["location"].first)
+ end
+
+ def assert_redirected_to!(url)
+ assert_redirected_to(url)
+ follow_redirect!
+ end
+end
+
class CachingTest < ActionController::IntegrationTest
fixtures :contents, :users, :sections, :assigned_sections, :sites
@@ -8,7 +35,7 @@ def setup
end
def test_should_expire_necessary_feeds_and_sections_when_publishing_article
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
@@ -25,7 +52,7 @@ def test_should_expire_necessary_feeds_and_sections_when_publishing_article
end
def test_should_expire_feeds_and_sections_when_publishing_article
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
@@ -39,7 +66,7 @@ def test_should_expire_feeds_and_sections_when_publishing_article
end
def test_should_only_expire_overview_when_creating_draft
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
@@ -61,7 +88,7 @@ def test_should_only_expire_overview_when_creating_draft
end
def test_should_only_expire_overview_when_revising_draft
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
@@ -82,7 +109,7 @@ def test_should_only_expire_overview_when_revising_draft
def test_should_expire_sections_when_publishing_draft
AssignedSection.delete_all 'id > 9'
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
@@ -100,7 +127,7 @@ def test_should_expire_sections_when_publishing_draft
end
def test_should_expire_articles_after_editing
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
assert_caches_page contents(:welcome).full_permalink do
@@ -128,7 +155,7 @@ def test_should_expire_articles_after_editing
end
def test_should_cache_and_expire_overview_feed_on_edited_article
- rss = visit
+ rss = visit_with_session
writer = login_as :quentin
assert_caches_page overview_path do
@@ -141,7 +168,7 @@ def test_should_cache_and_expire_overview_feed_on_edited_article
end
def test_should_not_expire_cache_on_new_comment
- visitor = visit
+ visitor = visit_with_session
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
end
@@ -152,22 +179,22 @@ def test_should_not_expire_cache_on_new_comment
end
def test_should_not_cache_comment_post
- visitor = visit
+ visitor = visit_with_session
assert_expires_pages "#{contents(:welcome).full_permalink}/comments" do
visitor.comment_on contents(:welcome), :author => 'bob', :body => 'what a wonderful post.'
end
end
def test_should_not_cache_comment_post_on_article_with_closed_comments
- visitor = visit
+ visitor = visit_with_session
contents(:welcome).update_attribute :comment_age, -1
assert_expires_pages "#{contents(:welcome).full_permalink}/comments" do
visitor.comment_on contents(:welcome), :author => 'bob', :body => 'what a wonderful post.'
end
end
def test_should_not_cache_comment_post_on_article_with_invalid_comment
- visitor = visit
+ visitor = visit_with_session
assert_expires_pages "#{contents(:welcome).full_permalink}/comments" do
assert_no_difference Comment, :count do
visitor.comment_on contents(:welcome), {}
@@ -176,14 +203,14 @@ def test_should_not_cache_comment_post_on_article_with_invalid_comment
end
def test_should_not_cache_comments_page_on_get
- visitor = visit
+ visitor = visit_with_session
assert_expires_pages "#{contents(:welcome).full_permalink}/comments" do
visitor.get "#{contents(:welcome).full_permalink}/comments"
end
end
def test_should_expire_cache_on_new_comment_if_approved
- visitor = visit
+ visitor = visit_with_session
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
end
@@ -198,7 +225,7 @@ def test_should_expire_cache_on_new_comment_if_approved
end
def test_should_expire_cache_when_comment_is_approved
- visitor = visit
+ visitor = visit_with_session
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
end
@@ -212,7 +239,7 @@ def test_should_expire_cache_when_comment_is_approved
end
def test_should_expire_cache_when_comment_is_unapproved
- visitor = visit
+ visitor = visit_with_session
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
end
@@ -226,8 +253,8 @@ def test_should_expire_cache_when_comment_is_unapproved
end
def test_should_expire_cache_when_approved_comment_is_deleted
- visitor = visit
- rss = visit
+ visitor = visit_with_session
+ rss = visit_with_session
writer = login_as :quentin
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
@@ -243,8 +270,8 @@ def test_should_expire_cache_when_approved_comment_is_deleted
end
def test_should_only_expire_overview_when_unapproved_comment_is_deleted
- visitor = visit
- rss = visit
+ visitor = visit_with_session
+ rss = visit_with_session
writer = login_as :quentin
assert_caches_page contents(:welcome).full_permalink do
visitor.read contents(:welcome)
@@ -263,7 +290,7 @@ def test_should_only_expire_overview_when_unapproved_comment_is_deleted
end
def test_should_not_cache_bad_urls
- visitor = visit
+ visitor = visit_with_session
pages = ['/about/blah', '/foo/bar', '2006/1/2/fasd']
assert_no_difference CachedPage, :count do
assert_expires_pages *pages do
@@ -273,7 +300,7 @@ def test_should_not_cache_bad_urls
end
def test_should_expire_section_cache_when_updating_section
- visitor = visit
+ visitor = visit_with_session
assert_caches_page section_url_for(:about) do
visitor.read sections(:about)
end
@@ -294,7 +321,7 @@ def test_should_expire_section_cache_when_updating_section
end
def test_should_expire_cache_when_updating_template
- visit_sections_and_feeds_with visit
+ visit_sections_and_feeds_with visit_with_session
assert_expires_pages section_url_for(:home), section_url_for(:about), feed_url_for(:home), feed_url_for(:about) do
login_as :quentin do |writer|
writer.update_template sites(:first).templates[:error], '<p>error!</p>'
@@ -303,7 +330,7 @@ def test_should_expire_cache_when_updating_template
end
def test_should_expire_new_assigned_section_to_article
- visitor = visit
+ visitor = visit_with_session
writer = login_as :quentin
visit_sections_and_feeds_with visitor
assert_expires_pages feed_url_for(:about), section_url_for(:about) do
@@ -319,7 +346,7 @@ def test_should_expire_new_assigned_section_to_article
end
def test_should_expire_section_when_removing_from_article
- visit_sections_and_feeds_with visit
+ visit_sections_and_feeds_with visit_with_session
assert_expires_pages section_url_for(:home), section_url_for(:about), feed_url_for(:home), feed_url_for(:about) do
login_as :quentin do |writer|
writer.revise contents(:site_map), :sections => [sections(:home)]
@@ -328,7 +355,7 @@ def test_should_expire_section_when_removing_from_article
end
def test_should_expire_section_and_article_cache_when_deleting_article
- visitor = visit
+ visitor = visit_with_session
visit_sections_and_feeds_with visitor
visitor.read contents(:site_map)
@@ -344,7 +371,7 @@ def test_should_expire_section_and_article_cache_when_deleting_article
end
def test_should_expire_resource_when_updating_resource
- visitor = visit
+ visitor = visit_with_session
assert_caches_page '/images/rails-logo.png' do
visitor.read '/images/rails-logo.png'
end
@@ -357,7 +384,7 @@ def test_should_expire_resource_when_updating_resource
end
def test_should_expire_resource_when_removing_resource
- visitor = visit
+ visitor = visit_with_session
assert_caches_page '/images/rails-logo.png' do
visitor.read '/images/rails-logo.png'
end
@@ -372,7 +399,7 @@ def test_should_expire_resource_when_removing_resource
# TODO test_should_expire_resource_when_uploading_resource
def test_should_not_cache_searches
- visitor = visit
+ visitor = visit_with_session
assert_expires_page "/search" do
visitor.get '/search'
visitor.get '/search', :q => 'foo'
@@ -382,15 +409,15 @@ def test_should_not_cache_searches
def test_should_not_cache_denied_route
assert_expires_page '/limited_deny' do
- visit { |v| v.get '/limited_deny' }
+ visit_with_session { |v| v.get '/limited_deny' }
end
assert_not_cached '/limited_deny'
end
def test_should_not_cache_redirected_route
assert_expires_page '/redirect/external' do
- visit do |v|
+ visit_with_session do |v|
v.get '/redirect/external'
assert v.redirect?
assert_equal 'http://external', v.headers["location"].first
@@ -399,6 +426,33 @@ def test_should_not_cache_redirected_route
end
protected
+ include Mephisto::Caching::ReferencedCachingTestHelper
+
+ # creates a session as a logged on user
+ def login_as(login)
+ visit_with_session do |sess|
+ sess.login_as login
+ yield sess if block_given?
+ end
+ end
+
+ # creates an anonymous session
+ def visit_with_session
+ open_session do |sess|
+ sess.host = 'test.host'
+ sess.extend Mephisto::Integration::Actor
+ yield sess if block_given?
+ end
+ end
+
+ def section_url_for(section, article = nil)
+ (article ? sections(section).to_page_url(contents(article)) : sections(section).to_url) * '/'
+ end
+
+ def feed_url_for(section)
+ "/feed/#{sections(section).to_feed_url * '/'}"
+ end
+
def visit_sections_and_feeds_with(visitor)
assert_difference CachedPage, :count, 4 do
assert_caches_page section_url_for(:home) do
Oops, something went wrong.

0 comments on commit d40725e

Please sign in to comment.