diff options
Diffstat (limited to 'actionpack/lib/action_controller/testing/integration.rb')
-rw-r--r-- | actionpack/lib/action_controller/testing/integration.rb | 331 |
1 files changed, 128 insertions, 203 deletions
diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb index 037463e489..4f39ee6a01 100644 --- a/actionpack/lib/action_controller/testing/integration.rb +++ b/actionpack/lib/action_controller/testing/integration.rb @@ -4,6 +4,114 @@ require 'active_support/test_case' module ActionController module Integration #:nodoc: + module RequestHelpers + # Performs a GET request with the given parameters. + # + # - +path+: The URI (as a String) on which you want to perform a GET + # request. + # - +parameters+: The HTTP parameters that you want to pass. This may + # be +nil+, + # a Hash, or a String that is appropriately encoded + # (<tt>application/x-www-form-urlencoded</tt> or + # <tt>multipart/form-data</tt>). + # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will + # automatically be upcased, with the prefix 'HTTP_' added if needed. + # + # This method returns an Response object, which one can use to + # inspect the details of the response. Furthermore, if this method was + # called from an ActionController::IntegrationTest object, then that + # object's <tt>@response</tt> instance variable will point to the same + # response object. + # + # You can also perform POST, PUT, DELETE, and HEAD requests with +post+, + # +put+, +delete+, and +head+. + def get(path, parameters = nil, headers = nil) + process :get, path, parameters, headers + end + + # Performs a POST request with the given parameters. See get() for more + # details. + def post(path, parameters = nil, headers = nil) + process :post, path, parameters, headers + end + + # Performs a PUT request with the given parameters. See get() for more + # details. + def put(path, parameters = nil, headers = nil) + process :put, path, parameters, headers + end + + # Performs a DELETE request with the given parameters. See get() for + # more details. + def delete(path, parameters = nil, headers = nil) + process :delete, path, parameters, headers + end + + # Performs a HEAD request with the given parameters. See get() for more + # details. + def head(path, parameters = nil, headers = nil) + process :head, path, parameters, headers + end + + # Performs an XMLHttpRequest request with the given parameters, mirroring + # a request from the Prototype library. + # + # The request_method is :get, :post, :put, :delete or :head; the + # parameters are +nil+, a hash, or a url-encoded or multipart string; + # the headers are a hash. Keys are automatically upcased and prefixed + # with 'HTTP_' if not already. + def xml_http_request(request_method, path, parameters = nil, headers = nil) + headers ||= {} + headers['X-Requested-With'] = 'XMLHttpRequest' + headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + process(request_method, path, parameters, headers) + end + alias xhr :xml_http_request + + # Follow a single redirect response. If the last response was not a + # redirect, an exception will be raised. Otherwise, the redirect is + # performed on the location header. + def follow_redirect! + raise "not a redirect! #{status} #{status_message}" unless redirect? + get(response.location) + status + end + + # Performs a request using the specified method, following any subsequent + # redirect. Note that the redirects are followed until the response is + # not a redirect--this means you may run into an infinite loop if your + # redirect loops back to itself. + def request_via_redirect(http_method, path, parameters = nil, headers = nil) + process(http_method, path, parameters, headers) + follow_redirect! while redirect? + status + end + + # Performs a GET request, following any subsequent redirect. + # See +request_via_redirect+ for more information. + def get_via_redirect(path, parameters = nil, headers = nil) + request_via_redirect(:get, path, parameters, headers) + end + + # Performs a POST request, following any subsequent redirect. + # See +request_via_redirect+ for more information. + def post_via_redirect(path, parameters = nil, headers = nil) + request_via_redirect(:post, path, parameters, headers) + end + + # Performs a PUT request, following any subsequent redirect. + # See +request_via_redirect+ for more information. + def put_via_redirect(path, parameters = nil, headers = nil) + request_via_redirect(:put, path, parameters, headers) + end + + # Performs a DELETE request, following any subsequent redirect. + # See +request_via_redirect+ for more information. + def delete_via_redirect(path, parameters = nil, headers = nil) + request_via_redirect(:delete, path, parameters, headers) + end + end + # An integration Session instance represents a set of requests and responses # performed sequentially by some virtual user. Because you can instantiate # multiple sessions and run them side-by-side, you can also mimic (to some @@ -14,20 +122,17 @@ module ActionController # Integration::Session directly. class Session include Test::Unit::Assertions - include ActionController::TestCase::Assertions + include ActionDispatch::Assertions include ActionController::TestProcess + include RequestHelpers - # The integer HTTP status code of the last request. - attr_reader :status - - # The status message that accompanied the status code of the last request. - attr_reader :status_message - - # The body of the last request. - attr_reader :body + %w( status status_message headers body redirect? ).each do |method| + delegate method, :to => :response, :allow_nil => true + end - # The URI of the last request. - attr_reader :path + %w( path ).each do |method| + delegate method, :to => :request, :allow_nil => true + end # The hostname used in the last request. attr_accessor :host @@ -42,9 +147,6 @@ module ActionController # sent with the next request. attr_reader :cookies - # A map of the headers returned by the last response. - attr_reader :headers - # A reference to the controller instance used by the last request. attr_reader :controller @@ -69,8 +171,6 @@ module ActionController # # session.reset! def reset! - @status = @path = @headers = nil - @result = @status_message = nil @https = false @cookies = {} @controller = @request = @response = nil @@ -118,117 +218,6 @@ module ActionController @host = name end - # Follow a single redirect response. If the last response was not a - # redirect, an exception will be raised. Otherwise, the redirect is - # performed on the location header. - def follow_redirect! - raise "not a redirect! #{@status} #{@status_message}" unless redirect? - get(interpret_uri(headers['location'])) - status - end - - # Performs a request using the specified method, following any subsequent - # redirect. Note that the redirects are followed until the response is - # not a redirect--this means you may run into an infinite loop if your - # redirect loops back to itself. - def request_via_redirect(http_method, path, parameters = nil, headers = nil) - send(http_method, path, parameters, headers) - follow_redirect! while redirect? - status - end - - # Performs a GET request, following any subsequent redirect. - # See +request_via_redirect+ for more information. - def get_via_redirect(path, parameters = nil, headers = nil) - request_via_redirect(:get, path, parameters, headers) - end - - # Performs a POST request, following any subsequent redirect. - # See +request_via_redirect+ for more information. - def post_via_redirect(path, parameters = nil, headers = nil) - request_via_redirect(:post, path, parameters, headers) - end - - # Performs a PUT request, following any subsequent redirect. - # See +request_via_redirect+ for more information. - def put_via_redirect(path, parameters = nil, headers = nil) - request_via_redirect(:put, path, parameters, headers) - end - - # Performs a DELETE request, following any subsequent redirect. - # See +request_via_redirect+ for more information. - def delete_via_redirect(path, parameters = nil, headers = nil) - request_via_redirect(:delete, path, parameters, headers) - end - - # Returns +true+ if the last response was a redirect. - def redirect? - status/100 == 3 - end - - # Performs a GET request with the given parameters. - # - # - +path+: The URI (as a String) on which you want to perform a GET - # request. - # - +parameters+: The HTTP parameters that you want to pass. This may - # be +nil+, - # a Hash, or a String that is appropriately encoded - # (<tt>application/x-www-form-urlencoded</tt> or - # <tt>multipart/form-data</tt>). - # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will - # automatically be upcased, with the prefix 'HTTP_' added if needed. - # - # This method returns an Response object, which one can use to - # inspect the details of the response. Furthermore, if this method was - # called from an ActionController::IntegrationTest object, then that - # object's <tt>@response</tt> instance variable will point to the same - # response object. - # - # You can also perform POST, PUT, DELETE, and HEAD requests with +post+, - # +put+, +delete+, and +head+. - def get(path, parameters = nil, headers = nil) - process :get, path, parameters, headers - end - - # Performs a POST request with the given parameters. See get() for more - # details. - def post(path, parameters = nil, headers = nil) - process :post, path, parameters, headers - end - - # Performs a PUT request with the given parameters. See get() for more - # details. - def put(path, parameters = nil, headers = nil) - process :put, path, parameters, headers - end - - # Performs a DELETE request with the given parameters. See get() for - # more details. - def delete(path, parameters = nil, headers = nil) - process :delete, path, parameters, headers - end - - # Performs a HEAD request with the given parameters. See get() for more - # details. - def head(path, parameters = nil, headers = nil) - process :head, path, parameters, headers - end - - # Performs an XMLHttpRequest request with the given parameters, mirroring - # a request from the Prototype library. - # - # The request_method is :get, :post, :put, :delete or :head; the - # parameters are +nil+, a hash, or a url-encoded or multipart string; - # the headers are a hash. Keys are automatically upcased and prefixed - # with 'HTTP_' if not already. - def xml_http_request(request_method, path, parameters = nil, headers = nil) - headers ||= {} - headers['X-Requested-With'] = 'XMLHttpRequest' - headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') - process(request_method, path, parameters, headers) - end - alias xhr :xml_http_request - # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) @@ -238,19 +227,14 @@ module ActionController end private - # Tailors the session based on the given URI, setting the HTTPS value - # and the hostname. - def interpret_uri(path) - location = URI.parse(path) - https! URI::HTTPS === location if location.scheme - host! location.host if location.host - location.query ? "#{location.path}?#{location.query}" : location.path - end - # Performs the actual request. def process(method, path, parameters = nil, headers = nil) - path = interpret_uri(path) if path =~ %r{://} - @path = path + if path =~ %r{://} + location = URI.parse(path) + https! URI::HTTPS === location if location.scheme + host! location.host if location.host + path = location.query ? "#{location.path}?#{location.query}" : location.path + end [ControllerCapture, ActionController::ProcessWithTest].each do |mod| unless ActionController::Base < mod @@ -261,7 +245,7 @@ module ActionController ActionController::Base.clear_last_instantiation! opts = { - :method => method.to_s.upcase, + :method => method, :params => parameters, "SERVER_NAME" => host, @@ -279,7 +263,7 @@ module ActionController string << "#{name}=#{value}; " } } - env = ActionDispatch::Test::MockRequest.env_for(@path, opts) + env = Rack::MockRequest.env_for(path, opts) (headers || {}).each do |key, value| key = key.to_s.upcase.gsub(/-/, "_") @@ -289,30 +273,20 @@ module ActionController app = Rack::Lint.new(@app) status, headers, body = app.call(env) - response = ::Rack::MockResponse.new(status, headers, body) + mock_response = ::Rack::MockResponse.new(status, headers, body) @request_count += 1 - @request = Request.new(env) - - @response = Response.new - @response.status = @status = response.status - @response.headers = @headers = response.headers - @response.body = @body = response.body + @request = ActionDispatch::Request.new(env) + @response = ActionDispatch::TestResponse.from_response(mock_response) - @status_message = ActionDispatch::StatusCodes::STATUS_CODES[@status] @cookies.merge!(@response.cookies) @html_document = nil - # Decorate the response with the standard behavior of the - # TestResponse so that things like assert_response can be - # used in integration tests. - @response.extend(TestResponseBehavior) - if @controller = ActionController::Base.last_instantiation @controller.send(:set_test_assigns) end - return @status + return response.status end # Get a temporary URL writer object @@ -508,54 +482,5 @@ module ActionController # end class IntegrationTest < ActiveSupport::TestCase include Integration::Runner - - # Work around a bug in test/unit caused by the default test being named - # as a symbol (:default_test), which causes regex test filters - # (like "ruby test.rb -n /foo/") to fail because =~ doesn't work on - # symbols. - def initialize(name) #:nodoc: - super(name.to_s) - end - - # Work around test/unit's requirement that every subclass of TestCase have - # at least one test method. Note that this implementation extends to all - # subclasses, as well, so subclasses of IntegrationTest may also exist - # without any test methods. - def run(*args) #:nodoc: - return if @method_name == "default_test" - super - end - - # Because of how use_instantiated_fixtures and use_transactional_fixtures - # are defined, we need to treat them as special cases. Otherwise, users - # would potentially have to set their values for both Test::Unit::TestCase - # ActionController::IntegrationTest, since by the time the value is set on - # TestCase, IntegrationTest has already been defined and cannot inherit - # changes to those variables. So, we make those two attributes - # copy-on-write. - - class << self - def use_transactional_fixtures=(flag) #:nodoc: - @_use_transactional_fixtures = true - @use_transactional_fixtures = flag - end - - def use_instantiated_fixtures=(flag) #:nodoc: - @_use_instantiated_fixtures = true - @use_instantiated_fixtures = flag - end - - def use_transactional_fixtures #:nodoc: - @_use_transactional_fixtures ? - @use_transactional_fixtures : - superclass.use_transactional_fixtures - end - - def use_instantiated_fixtures #:nodoc: - @_use_instantiated_fixtures ? - @use_instantiated_fixtures : - superclass.use_instantiated_fixtures - end - end end end |