# encoding: utf-8 # # = Controller Test Helpers # # Methods in this class are available to all the functional tests. # # == Request helpers # reget:: Resets request and calls +get+. # login:: Login a user. # logout:: Logout current user. # make_admin:: Make current user an admin and turn on admin mode. # get_with_dump:: Send GET, no login required. # requires_login:: Send GET, login required. # requires_user:: Send GET, certain user must be logged in. # post_with_dump:: Send POST, no login required. # post_requires_login:: Send POST, login required. # post_requires_user:: Send POST, certain user must be logged in. # html_dump:: Dump response body to file for W3C validation. # get_without_clearing_flash:: Wrapper: calls +get+ without clearing flash errors. # post_without_clearing_flash:: Wrapper: calls +post+ without clearing flash errors. # # == HTML Helpers # get_last_flash:: Retrieve the current list of errors or last set rendered. # url_for:: Get URL for +link_to+ style Hash of args. # extract_links:: Get Array of show_object links on page. # extract_error_from_body:: Extract error and stacktrace from 500 response body. # # == HTML Assertions # assert_link_in_html:: A given link exists. # assert_no_link_in_html:: A given link does not exist. # assert_form_action:: A form posting to a given action exists. # assert_response_equal_file:: Response body is same as copy in a file. # assert_request:: Check heuristics of an arbitrary request. # assert_response:: Check that last request resulted in a given redirect / render. # ################################################################################ module ControllerExtensions ############################################################################## # # :section: Request helpers # ############################################################################## # Make sure we clear out the last errors before each request. def get(*args) if @without_clearing_flash @without_clearing_flash = nil elsif session.is_a?(ActionController::TestSession) flash[:rendered_notice] = nil session[:notice] = nil end super end # Make sure we clear out the last errors before each request. def post(*args) if @without_clearing_flash @without_clearing_flash = nil elsif session.is_a?(ActionController::TestSession) flash[:rendered_notice] = nil session[:notice] = nil end super end # Second "get" won't update request_uri, so we must reset the request. def reget(*args) @request = @request.class.new get(*args) end # Call +get+ without clearing the flash (which we do by default). def get_without_clearing_flash(*args) @without_clearing_flash = true get(*args) end # Call +post+ without clearing the flash (which we do by default). def post_without_clearing_flash(*args) @without_clearing_flash = true post(*args) end # Log a user in (affects session only). def login(user='rolf', password='testpassword') user = User.authenticate(user, password) assert(user, "Failed to authenticate user <#{user}> with password <#{password}>.") @request.session[:user_id] = user.id User.current = user end # Log a user out (affects session only). def logout @request.session[:user_id] = nil @request.session[:admin] = nil User.current = nil end # Make the logged-in user admin and turn on admin mode. def make_admin(user='rolf', password='testpassword') user = login(user, password) @request.session[:admin] = true if !user.admin user.admin = 1 user.save end return user end # Send a GET request, and save the result in a file for w3c validation. # # # Send request, but ignore response. # get(:action, params) # # # Send request, and save response in ../html/action_0.html. # get_with_dump(:action, params) # def get_with_dump(page, params={}) get(page, params) html_dump(page, @response.body, params) end # Send a POST request, and save the result in a file for w3c validation. # # # Send request, but ignore response. # post(:action, params) # # # Send request, and save response in ../html/action_0.html. # post_with_dump(:action, params) # def post_with_dump(page, params={}) post(page, params) html_dump(page, @response.body, params) end # Send GET request to a page that should require login. # # # Make sure only logged-in users get to see this page. # requires_login(:edit_name, :id => 1) # def requires_login(page, *args) either_requires_either(:get, page, nil, *args) end # Send POST request to a page that should require login. # # # Make sure only logged-in users get to post this page. # post_requires_login(:edit_name, :id => 1) # def post_requires_login(page, *args) either_requires_either(:post, page, nil, *args) end # Send GET request to a page that should require a specific user. # # # Make sure only reviewers can see this page (non-reviewers get # # redirected to "show_location"). # requires_user(:review_authors, :show_location, :id => 1) # requires_user(:review_authors, [:location, :show_location], :id => 1) # def requires_user(*args) either_requires_either(:get, *args) end # Send POST request to a page that should require login. # # # Make sure only owner can edit observation (non-owners get # # redirected to "show_observation"). # post_requires_user(:edit_obs, :show_obs, :notes => 'new notes') # post_requires_user(:edit_obs, [:observer, :show_obs], :notes => 'new notes') # def post_requires_user(*args) either_requires_either(:post, *args) end # Helper used by the blah_requires_blah methods. # method:: [Request method: :get or :post. -- Supplied automatically by all four "public" methods.] # page:: Name of action. # altpage:: [Name of page redirected to if user wrong. -- Only include in +requires_user+ and +post_requires_user+.] # params:: Hash of parameters for action. # stay_on_page:: Does it render template of same name as action if succeeds? # username:: Which user should be logged in (default is 'rolf'). # password:: Which password should it try to use (default is 'testpassword'). # # # Make sure only logged-in users get to see this page, and that it # # render the template of the same name when it succeeds. # requires_login(:edit_name, :id => 1) # # # Make sure only logged-in users get to post this page, but that it # # renders the template of a different name (or redirects) on success. # post_requires_login(:edit_name, :id => 1, false) # # # Make sure only reviewers can see this page (non-reviewers get # # redirected to "show_location"), and that it renders # # the template of the same name when it succeeds. # requires_user(:review_authors, {:id => 1}, :show_location) # # # Make sure only owner can edit observation (non-owners get # # redirected to "show_observation"), and that it redirects to # # "show_observation" when it succeeds (last argument). # post_requires_user(:edit_observation, {:notes => 'new notes'}, # :show_observation, [:show_observation]) # # # Even more general case where second case renders a template: # post_requires_user(:action, params, # {:controller => controller1, :action => :access_denied, ...}, # :success_template) # # # Even more general case where both cases redirect: # post_requires_user(:action, params, # {:controller => controller1, :action => :access_denied, ...}, # {:controller => controller2, :action => :succeeded, ...}) # def either_requires_either(method, page, altpage, params={}, username='rolf', password='testpassword') assert_request( :method => method, :action => page, :params => params, :user => (params[:username] or username), :password => (params[:password] or password), :require_login => :login, :require_user => altpage ? [altpage].flatten : nil ) end # The whole purpose of this is to create a directory full of sample HTML # files that we can run the W3C validator on -- this has nothing to do with # debugging! This happens automatically if following directory exists: # # RAILS_ROOT/../html # # Files are created: # # show_user_0.html # show_user_1.html # show_user_2.html # etc. # def html_dump(label, html, params) html_dir = '../html' if File.directory?(html_dir) and html[0..11] != '
' file_name = "#{html_dir}/#{label}.html" count = 0 while File.exists?(file_name) file_name = "#{html_dir}/#{label}_#{count}.html" count += 1 if count > 100 raise(RangeError, "More than 100 files found with a label of '#{label}'") end end print "Creating html_dump file: #{file_name}\n" file = File.new(file_name, "w") # show_params(file, params, "params") file.write(html) file.close end end # Add the hash of parameters to the dump file for diagnostics. def show_params(file, hash, prefix) if hash.is_a?(Hash) hash.each {|k,v| show_params(file, v, "#{prefix}[#{k.to_s}]")} else file.write("#{prefix} = [#{hash.to_s}].*?<.pre>/m) {|x| x.gsub(/\s*\n/, "
")}
str.sub!(/^.*?/m, '')
str.sub!(/<.div>.*/m, '')
str.sub!(/
/m, '')
str.sub!(/RAILS.*?<.p>/, '')
str.gsub!(/<.p>/m, '')
str.gsub!(/\s+/m, ' ')
str.gsub!('
', "\n")
str.gsub!('
', "\n\n")
str.gsub!(': ', "\n")
str.gsub!(/<.*?>/, '')
str.gsub!(/^ */, '')
str.gsub!(/\n\n+/, "\n\n")
str.sub!(/\A\s*/, "\n")
str.sub!(/\s*\Z/, "\n")
end
##############################################################################
#
# :section: HTML assertions
#
##############################################################################
# Assert the LACK of existence of a given link in the response body, and
# check that it points to the right place.
def assert_no_link_in_html(label, msg=nil)
clean_our_backtrace do
extract_links(:label => label) do |link|
assert_block(build_message(msg, "Expected HTML *not* to contain link called >.", label)) {false}
end
assert_block('') { true } # to count the assertion
end
end
# Assert the existence of a given link in the response body, and check
# that it points to the right place.
def assert_link_in_html(label, url_opts, msg=nil)
clean_our_backtrace do
url = url_for(url_opts)
found_it = false
extract_links(:label => label) do |link|
if link.url != url
assert_block(build_message(msg, "Expected > link to point to >, instead it points to >", label, url, url2)) {false}
else
found_it = true
break
end
end
if found_it
assert_block('') { true } # to count the assertion
else
assert_block(build_message(msg, "Expected HTML to contain link called >.", label)) {false}
end
end
end
# Assert that a form exists which posts to the given url.
def assert_form_action(url_opts, msg=nil)
clean_our_backtrace do
url_opts[:only_path] = true if url_opts[:only_path].nil?
url = @controller.url_for(url_opts)
url.force_encoding('UTF-8') if url.respond_to?(:force_encoding)
url = URI.unescape(url)
# Find each occurrance of