aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/testing
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/testing')
-rw-r--r--actionpack/lib/action_dispatch/testing/assertion_response.rb28
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb9
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb18
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb501
-rw-r--r--actionpack/lib/action_dispatch/testing/request_encoder.rb54
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb7
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb34
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb9
9 files changed, 305 insertions, 361 deletions
diff --git a/actionpack/lib/action_dispatch/testing/assertion_response.rb b/actionpack/lib/action_dispatch/testing/assertion_response.rb
index 3fb81ff083..c37726957e 100644
--- a/actionpack/lib/action_dispatch/testing/assertion_response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertion_response.rb
@@ -1,14 +1,7 @@
module ActionDispatch
- # This is a class that abstracts away an asserted response.
- # It purposely does not inherit from Response, because it doesn't need it.
- # That means it does not have headers or a body.
- #
- # As an input to the initializer, we take a Fixnum, a String, or a Symbol.
- # If it's a Fixnum or String, we figure out what its symbolized name.
- # If it's a Symbol, we figure out what its corresponding code is.
- # The resulting code will be a Fixnum, for real HTTP codes, and it will
- # be a String for the pseudo-HTTP codes, such as:
- # :success, :missing, :redirect and :error
+ # This is a class that abstracts away an asserted response. It purposely
+ # does not inherit from Response because it doesn't need it. That means it
+ # does not have headers or a body.
class AssertionResponse
attr_reader :code, :name
@@ -19,6 +12,9 @@ module ActionDispatch
error: "5XX"
}
+ # Accepts a specific response status code as an Integer (404) or String
+ # ('404') or a response status range as a Symbol pseudo-code (:success,
+ # indicating any 200-299 status code).
def initialize(code_or_name)
if code_or_name.is_a?(Symbol)
@name = code_or_name
@@ -38,12 +34,12 @@ module ActionDispatch
private
- def code_from_name(name)
- GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
- end
+ def code_from_name(name)
+ GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
+ end
- def name_from_code(code)
- GENERIC_RESPONSE_CODES.invert[code] || Rack::Utils::HTTP_STATUS_CODES[code]
- end
+ def name_from_code(code)
+ GENERIC_RESPONSE_CODES.invert[code] || Rack::Utils::HTTP_STATUS_CODES[code]
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb
index fae266273e..b362931ef7 100644
--- a/actionpack/lib/action_dispatch/testing/assertions.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions.rb
@@ -1,9 +1,9 @@
-require 'rails-dom-testing'
+require "rails-dom-testing"
module ActionDispatch
module Assertions
- autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
- autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
+ autoload :ResponseAssertions, "action_dispatch/testing/assertions/response"
+ autoload :RoutingAssertions, "action_dispatch/testing/assertions/routing"
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index cd55b7d975..a2eaccd9ef 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -79,11 +79,16 @@ module ActionDispatch
def generate_response_message(expected, actual = @response.response_code)
"Expected response to be a <#{code_with_name(expected)}>,"\
" but was a <#{code_with_name(actual)}>"
- .concat location_if_redirected
+ .concat(location_if_redirected).concat(response_body_if_short)
+ end
+
+ def response_body_if_short
+ return "" if @response.body.size > 500
+ "\nResponse body: #{@response.body}"
end
def location_if_redirected
- return '' unless @response.redirection? && @response.location.present?
+ return "" unless @response.redirection? && @response.location.present?
location = normalize_argument_to_redirection(@response.location)
" redirect to <#{location}>"
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index e7af27463c..e53bc6af12 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -1,7 +1,7 @@
-require 'uri'
-require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/string/access'
-require 'action_controller/metal/exceptions'
+require "uri"
+require "active_support/core_ext/hash/indifferent_access"
+require "active_support/core_ext/string/access"
+require "action_controller/metal/exceptions"
module ActionDispatch
module Assertions
@@ -82,7 +82,7 @@ module ActionDispatch
expected_path = uri.path.to_s.empty? ? "/" : uri.path
end
else
- expected_path = "/#{expected_path}" unless expected_path.first == '/'
+ expected_path = "/#{expected_path}" unless expected_path.first == "/"
end
# Load routes.rb if it hasn't been loaded.
@@ -117,7 +117,7 @@ module ActionDispatch
# # Tests a route, providing a defaults hash
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
#
- # # Tests a route with a HTTP method
+ # # Tests a route with an HTTP method
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)
@@ -127,7 +127,7 @@ module ActionDispatch
options[:controller] = "/#{controller}"
end
- generate_options = options.dup.delete_if{ |k, _| defaults.key?(k) }
+ generate_options = options.dup.delete_if { |k, _| defaults.key?(k) }
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
end
@@ -184,7 +184,7 @@ module ActionDispatch
end
# Assume given controller
- request = ActionController::TestRequest.create
+ request = ActionController::TestRequest.create @controller.class
if path =~ %r{://}
fail_on(URI::InvalidURIError, msg) do
@@ -202,7 +202,7 @@ module ActionDispatch
request.request_method = method if method
params = fail_on(ActionController::RoutingError, msg) do
- @routes.recognize_path(path, { :method => method, :extras => extras })
+ @routes.recognize_path(path, method: method, extras: extras)
end
request.path_parameters = params.with_indifferent_access
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index f4534b4173..e2ea96513c 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,106 +1,51 @@
-require 'stringio'
-require 'uri'
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/object/try'
-require 'active_support/core_ext/string/strip'
-require 'rack/test'
-require 'minitest'
+require "stringio"
+require "uri"
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/object/try"
+require "active_support/core_ext/string/strip"
+require "rack/test"
+require "minitest"
+
+require "action_dispatch/testing/request_encoder"
module ActionDispatch
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.
- # - +params+: 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 headers to pass, as a Hash. The headers will be
- # merged into the Rack env hash.
- # - +env+: Additional env to pass, as a Hash. The headers will be
- # merged into the Rack env hash.
- #
- # This method returns a Response object, which one can use to
- # inspect the details of the response. Furthermore, if this method was
- # called from an ActionDispatch::IntegrationTest object, then that
- # object's <tt>@response</tt> instance variable will point to the same
- # response object.
- #
- # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
- # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
- #
- # Example:
- #
- # get '/feed', params: { since: 201501011400 }
- # post '/profile', headers: { "X-Test-Header" => "testvalue" }
- def get(path, *args)
- process_with_kwargs(:get, path, *args)
+ # Performs a GET request with the given parameters. See +#process+ for more
+ # details.
+ def get(path, **args)
+ process(:get, path, **args)
end
- # Performs a POST request with the given parameters. See +#get+ for more
+ # Performs a POST request with the given parameters. See +#process+ for more
# details.
- def post(path, *args)
- process_with_kwargs(:post, path, *args)
+ def post(path, **args)
+ process(:post, path, **args)
end
- # Performs a PATCH request with the given parameters. See +#get+ for more
+ # Performs a PATCH request with the given parameters. See +#process+ for more
# details.
- def patch(path, *args)
- process_with_kwargs(:patch, path, *args)
+ def patch(path, **args)
+ process(:patch, path, **args)
end
- # Performs a PUT request with the given parameters. See +#get+ for more
+ # Performs a PUT request with the given parameters. See +#process+ for more
# details.
- def put(path, *args)
- process_with_kwargs(:put, path, *args)
+ def put(path, **args)
+ process(:put, path, **args)
end
- # Performs a DELETE request with the given parameters. See +#get+ for
+ # Performs a DELETE request with the given parameters. See +#process+ for
# more details.
- def delete(path, *args)
- process_with_kwargs(:delete, path, *args)
+ def delete(path, **args)
+ process(:delete, path, **args)
end
- # Performs a HEAD request with the given parameters. See +#get+ for more
+ # Performs a HEAD request with the given parameters. See +#process+ for more
# details.
def head(path, *args)
- process_with_kwargs(:head, path, *args)
- end
-
- # Performs an XMLHttpRequest request with the given parameters, mirroring
- # an AJAX request made from JavaScript.
- #
- # The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
- # +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
- # string; the headers are a hash.
- #
- # Example:
- #
- # xhr :get, '/feed', params: { since: 201501011400 }
- def xml_http_request(request_method, path, *args)
- if kwarg_request?(args)
- params, headers, env = args.first.values_at(:params, :headers, :env)
- else
- params = args[0]
- headers = args[1]
- env = {}
-
- if params.present? || headers.present?
- non_kwarg_request_warning
- end
- end
-
- ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
- xhr and xml_http_request methods are deprecated in favor of
- `get "/posts", xhr: true` and `post "/posts/1", xhr: true`
- MSG
-
- process(request_method, path, params: params, headers: headers, xhr: true)
+ process(:head, path, *args)
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
@@ -110,58 +55,6 @@ module ActionDispatch
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.
- #
- # Example:
- #
- # request_via_redirect :post, '/welcome',
- # params: { ref_id: 14 },
- # headers: { "X-Test-Header" => "testvalue" }
- def request_via_redirect(http_method, path, *args)
- process_with_kwargs(http_method, path, *args)
-
- 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, *args)
- ActiveSupport::Deprecation.warn('`get_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
- request_via_redirect(:get, path, *args)
- end
-
- # Performs a POST request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def post_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`post_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
- request_via_redirect(:post, path, *args)
- end
-
- # Performs a PATCH request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def patch_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`patch_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
- request_via_redirect(:patch, path, *args)
- end
-
- # Performs a PUT request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def put_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`put_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
- request_via_redirect(:put, path, *args)
- end
-
- # Performs a DELETE request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def delete_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`delete_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
- request_via_redirect(:delete, path, *args)
- end
end
# An instance of this class represents a set of requests and responses
@@ -176,14 +69,14 @@ module ActionDispatch
DEFAULT_HOST = "www.example.com"
include Minitest::Assertions
- include TestProcess, RequestHelpers, Assertions
+ include RequestHelpers, Assertions
%w( status status_message headers body redirect? ).each do |method|
- delegate method, :to => :response, :allow_nil => true
+ delegate method, to: :response, allow_nil: true
end
%w( path ).each do |method|
- delegate method, :to => :request, :allow_nil => true
+ delegate method, to: :request, allow_nil: true
end
# The hostname used in the last request.
@@ -234,7 +127,7 @@ module ActionDispatch
url_options.reverse_merge!(@app.routes.default_url_options)
end
- url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
+ url_options.reverse_merge!(host: host, protocol: https? ? "https" : "http")
end
end
@@ -280,168 +173,129 @@ module ActionDispatch
@https
end
- # Set the host name to use in the next request.
+ # Performs the actual request.
#
- # session.host! "www.example.com"
- alias :host! :host=
-
- private
- def _mock_session
- @_mock_session ||= Rack::MockSession.new(@app, host)
- end
-
- def process_with_kwargs(http_method, path, *args)
- if kwarg_request?(args)
- process(http_method, path, *args)
- else
- non_kwarg_request_warning if args.any?
- process(http_method, path, { params: args[0], headers: args[1] })
- end
- end
-
- REQUEST_KWARGS = %i(params headers env xhr)
- def kwarg_request?(args)
- args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
- end
-
- def non_kwarg_request_warning
- ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
- ActionDispatch::IntegrationTest HTTP request methods will accept only
- the following keyword arguments in future Rails versions:
- #{REQUEST_KWARGS.join(', ')}
-
- Examples:
-
- get '/profile',
- params: { id: 1 },
- headers: { 'X-Extra-Header' => '123' },
- env: { 'action_dispatch.custom' => 'custom' },
- xhr: true
- MSG
+ # - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
+ # as a symbol.
+ # - +path+: The URI (as a String) on which you want to perform the
+ # request.
+ # - +params+: 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 headers to pass, as a Hash. The headers will be
+ # merged into the Rack env hash.
+ # - +env+: Additional env to pass, as a Hash. The headers will be
+ # merged into the Rack env hash.
+ #
+ # This method is rarely used directly. Use +#get+, +#post+, or other standard
+ # HTTP methods in integration tests. +#process+ is only required when using a
+ # request method that doesn't have a method defined in the integration tests.
+ #
+ # This method returns a Response object, which one can use to
+ # inspect the details of the response. Furthermore, if this method was
+ # called from an ActionDispatch::IntegrationTest object, then that
+ # object's <tt>@response</tt> instance variable will point to the same
+ # response object.
+ #
+ # Example:
+ # process :get, '/author', params: { since: 201501011400 }
+ def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
+ request_encoder = RequestEncoder.encoder(as)
+ headers ||= {}
+
+ if method == :get && as == :json && params
+ headers["X-Http-Method-Override"] = "GET"
+ method = :post
end
- # Performs the actual request.
- def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
- request_encoder = RequestEncoder.encoder(as)
-
- if path =~ %r{://}
- location = URI.parse(path)
+ if path =~ %r{://}
+ path = build_expanded_path(path, request_encoder) do |location|
https! URI::HTTPS === location if location.scheme
+
if url_host = location.host
default = Rack::Request::DEFAULT_PORTS[location.scheme]
url_host += ":#{location.port}" if default != location.port
host! url_host
end
- path = request_encoder.append_format_to location.path
- path = location.query ? "#{path}?#{location.query}" : path
- else
- path = request_encoder.append_format_to path
end
+ elsif as
+ path = build_expanded_path(path, request_encoder)
+ end
- hostname, port = host.split(':')
-
- request_env = {
- :method => method,
- :params => request_encoder.encode_params(params),
-
- "SERVER_NAME" => hostname,
- "SERVER_PORT" => port || (https? ? "443" : "80"),
- "HTTPS" => https? ? "on" : "off",
- "rack.url_scheme" => https? ? "https" : "http",
-
- "REQUEST_URI" => path,
- "HTTP_HOST" => host,
- "REMOTE_ADDR" => remote_addr,
- "CONTENT_TYPE" => request_encoder.content_type,
- "HTTP_ACCEPT" => accept
- }
-
- if xhr
- headers ||= {}
- headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
- headers['HTTP_ACCEPT'] ||= [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
- end
+ hostname, port = host.split(":")
- # this modifies the passed request_env directly
- if headers.present?
- Http::Headers.from_hash(request_env).merge!(headers)
- end
- if env.present?
- Http::Headers.from_hash(request_env).merge!(env)
- end
+ request_env = {
+ :method => method,
+ :params => request_encoder.encode_params(params),
- session = Rack::Test::Session.new(_mock_session)
+ "SERVER_NAME" => hostname,
+ "SERVER_PORT" => port || (https? ? "443" : "80"),
+ "HTTPS" => https? ? "on" : "off",
+ "rack.url_scheme" => https? ? "https" : "http",
- # NOTE: rack-test v0.5 doesn't build a default uri correctly
- # Make sure requested path is always a full uri
- session.request(build_full_uri(path, request_env), request_env)
-
- @request_count += 1
- @request = ActionDispatch::Request.new(session.last_request.env)
- response = _mock_session.last_response
- @response = ActionDispatch::TestResponse.from_response(response)
- @response.request = @request
- @response.response_parser = RequestEncoder.parser(@response.content_type)
- @html_document = nil
- @url_options = nil
+ "REQUEST_URI" => path,
+ "HTTP_HOST" => host,
+ "REMOTE_ADDR" => remote_addr,
+ "CONTENT_TYPE" => request_encoder.content_type,
+ "HTTP_ACCEPT" => accept
+ }
- @controller = @request.controller_instance
+ wrapped_headers = Http::Headers.from_hash({})
+ wrapped_headers.merge!(headers) if headers
- response.status
+ if xhr
+ wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
+ wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
end
- def build_full_uri(path, env)
- "#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
+ # this modifies the passed request_env directly
+ if wrapped_headers.present?
+ Http::Headers.from_hash(request_env).merge!(wrapped_headers)
+ end
+ if env.present?
+ Http::Headers.from_hash(request_env).merge!(env)
end
- class RequestEncoder # :nodoc:
- @encoders = {}
-
- attr_reader :response_parser
-
- def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
- @mime = Mime[mime_name]
+ session = Rack::Test::Session.new(_mock_session)
- unless @mime
- raise ArgumentError, "Can't register a request encoder for " \
- "unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
- end
-
- @url_encoded_form = url_encoded_form
- @path_format = ".#{@mime.symbol}" unless @url_encoded_form
- @response_parser = response_parser || -> body { body }
- @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
- end
+ # NOTE: rack-test v0.5 doesn't build a default uri correctly
+ # Make sure requested path is always a full uri
+ session.request(build_full_uri(path, request_env), request_env)
- def append_format_to(path)
- path << @path_format unless @url_encoded_form
- path
- end
-
- def content_type
- @mime.to_s
- end
+ @request_count += 1
+ @request = ActionDispatch::Request.new(session.last_request.env)
+ response = _mock_session.last_response
+ @response = ActionDispatch::TestResponse.from_response(response)
+ @response.request = @request
+ @html_document = nil
+ @url_options = nil
- def encode_params(params)
- @param_encoder.call(params)
- end
+ @controller = @request.controller_instance
- def self.parser(content_type)
- mime = Mime::Type.lookup(content_type)
- encoder(mime ? mime.ref : nil).response_parser
- end
+ response.status
+ end
- def self.encoder(name)
- @encoders[name] || WWWFormEncoder
- end
+ # Set the host name to use in the next request.
+ #
+ # session.host! "www.example.com"
+ alias :host! :host=
- def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
- @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
- end
+ private
+ def _mock_session
+ @_mock_session ||= Rack::MockSession.new(@app, host)
+ end
- register_encoder :json, response_parser: -> body { JSON.parse(body) }
+ def build_full_uri(path, env)
+ "#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
+ end
- WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
+ def build_expanded_path(path, request_encoder)
+ location = URI.parse(path)
+ yield location if block_given?
+ path = request_encoder.append_format_to location.path
+ location.query ? "#{path}?#{location.query}" : path
end
end
@@ -452,9 +306,13 @@ module ActionDispatch
attr_reader :app
+ def initialize(*args, &blk)
+ super(*args, &blk)
+ @integration_session = nil
+ end
+
def before_setup # :nodoc:
@app = nil
- @integration_session = nil
super
end
@@ -488,7 +346,7 @@ module ActionDispatch
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
define_method(method) do |*args|
# reset the html_document variable, except for cookies/assigns calls
- unless method == 'cookies' || method == 'assigns'
+ unless method == "cookies" || method == "assigns"
@html_document = nil
end
@@ -530,7 +388,7 @@ module ActionDispatch
integration_session.default_url_options = options
end
- def respond_to?(method, include_private = false)
+ def respond_to_missing?(method, include_private = false)
integration_session.respond_to?(method, include_private) || super
end
@@ -699,70 +557,95 @@ module ActionDispatch
# end
# end
#
+ # See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
+ # use +get+, etc.
+ #
+ # === Changing the request encoding
+ #
# You can also test your JSON API easily by setting what the request should
# be encoded as:
#
- # require 'test_helper'
+ # require "test_helper"
#
# class ApiTest < ActionDispatch::IntegrationTest
- # test 'creates articles' do
+ # test "creates articles" do
# assert_difference -> { Article.count } do
- # post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
+ # post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
# end
#
# assert_response :success
- # assert_equal({ id: Arcticle.last.id, title: 'Ahoy!' }, response.parsed_body)
+ # assert_equal({ id: Arcticle.last.id, title: "Ahoy!" }, response.parsed_body)
# end
# end
#
- # The `as` option sets the format to JSON, sets the content type to
- # 'application/json' and encodes the parameters as JSON.
+ # The +as+ option sets the format to JSON, sets the content type to
+ # "application/json" and encodes the parameters as JSON.
#
- # Calling `parsed_body` on the response parses the response body as what
- # the last request was encoded as. If the request wasn't encoded `as` something,
- # it's the same as calling `body`.
+ # Calling +parsed_body+ on the response parses the response body based on the
+ # last response MIME type.
#
- # For any custom MIME Types you've registered, you can even add your own encoders with:
+ # For any custom MIME types you've registered, you can even add your own encoders with:
#
# ActionDispatch::IntegrationTest.register_encoder :wibble,
# param_encoder: -> params { params.to_wibble },
# response_parser: -> body { body }
#
- # Where `param_encoder` defines how the params should be encoded and
- # `response_parser` defines how the response body should be parsed through
- # `parsed_body`.
+ # Where +param_encoder+ defines how the params should be encoded and
+ # +response_parser+ defines how the response body should be parsed through
+ # +parsed_body+.
#
# Consult the Rails Testing Guide for more.
class IntegrationTest < ActiveSupport::TestCase
- include Integration::Runner
- include ActionController::TemplateAssertions
- include ActionDispatch::Routing::UrlFor
-
- @@app = nil
+ include TestProcess
- def self.app
- @@app || ActionDispatch.test_app
+ module UrlOptions
+ extend ActiveSupport::Concern
+ def url_options
+ integration_session.url_options
+ end
end
- def self.app=(app)
- @@app = app
- end
+ module Behavior
+ extend ActiveSupport::Concern
- def app
- super || self.class.app
- end
+ include Integration::Runner
+ include ActionController::TemplateAssertions
- def url_options
- integration_session.url_options
- end
+ included do
+ include ActionDispatch::Routing::UrlFor
+ include UrlOptions # don't let UrlFor override the url_options method
+ ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
+ @@app = nil
+ end
- def document_root_element
- html_document.root
- end
+ module ClassMethods
+ def app
+ if defined?(@@app) && @@app
+ @@app
+ else
+ ActionDispatch.test_app
+ end
+ end
- def self.register_encoder(*args)
- Integration::Session::RequestEncoder.register_encoder(*args)
+ def app=(app)
+ @@app = app
+ end
+
+ def register_encoder(*args)
+ RequestEncoder.register_encoder(*args)
+ end
+ end
+
+ def app
+ super || self.class.app
+ end
+
+ def document_root_element
+ html_document.root
+ end
end
+
+ include Behavior
end
end
diff --git a/actionpack/lib/action_dispatch/testing/request_encoder.rb b/actionpack/lib/action_dispatch/testing/request_encoder.rb
new file mode 100644
index 0000000000..b0b994b2d0
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/request_encoder.rb
@@ -0,0 +1,54 @@
+module ActionDispatch
+ class RequestEncoder # :nodoc:
+ @encoders = {}
+
+ attr_reader :response_parser
+
+ def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
+ @mime = Mime[mime_name]
+
+ unless @mime
+ raise ArgumentError, "Can't register a request encoder for " \
+ "unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
+ end
+
+ @url_encoded_form = url_encoded_form
+ @path_format = ".#{@mime.symbol}" unless @url_encoded_form
+ @response_parser = response_parser || -> body { body }
+ @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
+ end
+
+ def append_format_to(path)
+ if @url_encoded_form
+ path
+ else
+ path + @path_format
+ end
+ end
+
+ def content_type
+ @mime.to_s
+ end
+
+ def encode_params(params)
+ @param_encoder.call(params)
+ end
+
+ def self.parser(content_type)
+ mime = Mime::Type.lookup(content_type)
+ encoder(mime ? mime.ref : nil).response_parser
+ end
+
+ def self.encoder(name)
+ @encoders[name] || WWWFormEncoder
+ end
+
+ def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
+ @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
+ end
+
+ register_encoder :json, response_parser: -> body { JSON.parse(body) }
+
+ WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
+ end
+end
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index 1ecd7d14a7..8b03b776fa 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -1,5 +1,5 @@
-require 'action_dispatch/middleware/cookies'
-require 'action_dispatch/middleware/flash'
+require "action_dispatch/middleware/cookies"
+require "action_dispatch/middleware/flash"
module ActionDispatch
module TestProcess
@@ -34,7 +34,8 @@ module ActionDispatch
#
# post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
def fixture_file_upload(path, mime_type = nil, binary = false)
- if self.class.respond_to?(:fixture_path) && self.class.fixture_path
+ if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
+ !File.exist?(path)
path = File.join(self.class.fixture_path, path)
end
Rack::Test::UploadedFile.new(path, mime_type, binary)
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index ad1a7f7109..91b25ec155 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -1,12 +1,12 @@
-require 'active_support/core_ext/hash/indifferent_access'
-require 'rack/utils'
+require "active_support/core_ext/hash/indifferent_access"
+require "rack/utils"
module ActionDispatch
class TestRequest < Request
- DEFAULT_ENV = Rack::MockRequest.env_for('/',
- 'HTTP_HOST' => 'test.host',
- 'REMOTE_ADDR' => '0.0.0.0',
- 'HTTP_USER_AGENT' => 'Rails Testing',
+ DEFAULT_ENV = Rack::MockRequest.env_for("/",
+ "HTTP_HOST" => "test.host",
+ "REMOTE_ADDR" => "0.0.0.0",
+ "HTTP_USER_AGENT" => "Rails Testing",
)
# Create a new test request with default `env` values
@@ -22,23 +22,23 @@ module ActionDispatch
private_class_method :default_env
def request_method=(method)
- @env['REQUEST_METHOD'] = method.to_s.upcase
+ super(method.to_s.upcase)
end
def host=(host)
- @env['HTTP_HOST'] = host
+ set_header("HTTP_HOST", host)
end
def port=(number)
- @env['SERVER_PORT'] = number.to_i
+ set_header("SERVER_PORT", number.to_i)
end
def request_uri=(uri)
- @env['REQUEST_URI'] = uri
+ set_header("REQUEST_URI", uri)
end
def path=(path)
- @env['PATH_INFO'] = path
+ set_header("PATH_INFO", path)
end
def action=(action_name)
@@ -46,24 +46,24 @@ module ActionDispatch
end
def if_modified_since=(last_modified)
- @env['HTTP_IF_MODIFIED_SINCE'] = last_modified
+ set_header("HTTP_IF_MODIFIED_SINCE", last_modified)
end
def if_none_match=(etag)
- @env['HTTP_IF_NONE_MATCH'] = etag
+ set_header("HTTP_IF_NONE_MATCH", etag)
end
def remote_addr=(addr)
- @env['REMOTE_ADDR'] = addr
+ set_header("REMOTE_ADDR", addr)
end
def user_agent=(user_agent)
- @env['HTTP_USER_AGENT'] = user_agent
+ set_header("HTTP_USER_AGENT", user_agent)
end
def accept=(mime_types)
- @env.delete('action_dispatch.request.accepts')
- @env['HTTP_ACCEPT'] = Array(mime_types).collect(&:to_s).join(",")
+ delete_header("action_dispatch.request.accepts")
+ set_header("HTTP_ACCEPT", Array(mime_types).collect(&:to_s).join(","))
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 9d4b73a43d..5c89f9c75e 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -1,3 +1,5 @@
+require "action_dispatch/testing/request_encoder"
+
module ActionDispatch
# Integration test methods such as ActionDispatch::Integration::Session#get
# and ActionDispatch::Integration::Session#post return objects of class
@@ -10,6 +12,11 @@ module ActionDispatch
new response.status, response.headers, response.body
end
+ def initialize(*) # :nodoc:
+ super
+ @response_parser = RequestEncoder.parser(content_type)
+ end
+
# Was the response successful?
alias_method :success?, :successful?
@@ -19,8 +26,6 @@ module ActionDispatch
# Was there a server-side error?
alias_method :error?, :server_error?
- attr_writer :response_parser # :nodoc:
-
def parsed_body
@parsed_body ||= @response_parser.call(body)
end