actionpack 5.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +429 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller.rb +27 -0
- data/lib/abstract_controller/asset_paths.rb +12 -0
- data/lib/abstract_controller/base.rb +265 -0
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/caching/fragments.rb +166 -0
- data/lib/abstract_controller/callbacks.rb +212 -0
- data/lib/abstract_controller/collector.rb +43 -0
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +194 -0
- data/lib/abstract_controller/logger.rb +14 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
- data/lib/abstract_controller/rendering.rb +127 -0
- data/lib/abstract_controller/translation.rb +31 -0
- data/lib/abstract_controller/url_for.rb +35 -0
- data/lib/action_controller.rb +66 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/base.rb +276 -0
- data/lib/action_controller/caching.rb +46 -0
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +78 -0
- data/lib/action_controller/metal.rb +256 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +274 -0
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +16 -0
- data/lib/action_controller/metal/data_streaming.rb +152 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
- data/lib/action_controller/metal/exceptions.rb +53 -0
- data/lib/action_controller/metal/flash.rb +61 -0
- data/lib/action_controller/metal/force_ssl.rb +99 -0
- data/lib/action_controller/metal/head.rb +60 -0
- data/lib/action_controller/metal/helpers.rb +123 -0
- data/lib/action_controller/metal/http_authentication.rb +519 -0
- data/lib/action_controller/metal/implicit_render.rb +73 -0
- data/lib/action_controller/metal/instrumentation.rb +107 -0
- data/lib/action_controller/metal/live.rb +312 -0
- data/lib/action_controller/metal/mime_responds.rb +313 -0
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +293 -0
- data/lib/action_controller/metal/redirecting.rb +133 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +122 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
- data/lib/action_controller/metal/rescue.rb +28 -0
- data/lib/action_controller/metal/streaming.rb +223 -0
- data/lib/action_controller/metal/strong_parameters.rb +1086 -0
- data/lib/action_controller/metal/testing.rb +16 -0
- data/lib/action_controller/metal/url_for.rb +58 -0
- data/lib/action_controller/railtie.rb +89 -0
- data/lib/action_controller/railties/helpers.rb +24 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +629 -0
- data/lib/action_dispatch.rb +112 -0
- data/lib/action_dispatch/http/cache.rb +222 -0
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +84 -0
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +132 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
- data/lib/action_dispatch/http/mime_type.rb +342 -0
- data/lib/action_dispatch/http/mime_types.rb +50 -0
- data/lib/action_dispatch/http/parameter_filter.rb +86 -0
- data/lib/action_dispatch/http/parameters.rb +126 -0
- data/lib/action_dispatch/http/rack_cache.rb +63 -0
- data/lib/action_dispatch/http/request.rb +430 -0
- data/lib/action_dispatch/http/response.rb +519 -0
- data/lib/action_dispatch/http/upload.rb +84 -0
- data/lib/action_dispatch/http/url.rb +350 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/journey/formatter.rb +189 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
- data/lib/action_dispatch/journey/nodes/node.rb +140 -0
- data/lib/action_dispatch/journey/parser.rb +199 -0
- data/lib/action_dispatch/journey/parser.y +50 -0
- data/lib/action_dispatch/journey/parser_extras.rb +31 -0
- data/lib/action_dispatch/journey/path/pattern.rb +198 -0
- data/lib/action_dispatch/journey/route.rb +203 -0
- data/lib/action_dispatch/journey/router.rb +156 -0
- data/lib/action_dispatch/journey/router/utils.rb +102 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +64 -0
- data/lib/action_dispatch/journey/visitors.rb +268 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/middleware/callbacks.rb +36 -0
- data/lib/action_dispatch/middleware/cookies.rb +685 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +300 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
- data/lib/action_dispatch/middleware/reloader.rb +12 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
- data/lib/action_dispatch/middleware/request_id.rb +43 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
- data/lib/action_dispatch/middleware/ssl.rb +150 -0
- data/lib/action_dispatch/middleware/stack.rb +116 -0
- data/lib/action_dispatch/middleware/static.rb +130 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +55 -0
- data/lib/action_dispatch/request/session.rb +234 -0
- data/lib/action_dispatch/request/utils.rb +78 -0
- data/lib/action_dispatch/routing.rb +260 -0
- data/lib/action_dispatch/routing/endpoint.rb +17 -0
- data/lib/action_dispatch/routing/inspector.rb +225 -0
- data/lib/action_dispatch/routing/mapper.rb +2267 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
- data/lib/action_dispatch/routing/redirection.rb +201 -0
- data/lib/action_dispatch/routing/route_set.rb +890 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
- data/lib/action_dispatch/routing/url_for.rb +236 -0
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions.rb +24 -0
- data/lib/action_dispatch/testing/assertions/response.rb +107 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
- data/lib/action_dispatch/testing/integration.rb +652 -0
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +50 -0
- data/lib/action_dispatch/testing/test_request.rb +71 -0
- data/lib/action_dispatch/testing/test_response.rb +53 -0
- data/lib/action_pack.rb +26 -0
- data/lib/action_pack/gem_version.rb +17 -0
- data/lib/action_pack/version.rb +10 -0
- metadata +318 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "active_support/core_ext/hash/indifferent_access"
|
5
|
+
require "active_support/core_ext/string/access"
|
6
|
+
require "action_controller/metal/exceptions"
|
7
|
+
|
8
|
+
module ActionDispatch
|
9
|
+
module Assertions
|
10
|
+
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
|
11
|
+
module RoutingAssertions
|
12
|
+
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
|
13
|
+
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
|
14
|
+
#
|
15
|
+
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
|
16
|
+
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
|
17
|
+
# and a :method containing the required HTTP verb.
|
18
|
+
#
|
19
|
+
# # Asserts that POSTing to /items will call the create action on ItemsController
|
20
|
+
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
|
21
|
+
#
|
22
|
+
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
|
23
|
+
# to assert that values in the query string will end up in the params hash correctly. To test query strings you must use the extras
|
24
|
+
# argument because appending the query string on the path directly will not work. For example:
|
25
|
+
#
|
26
|
+
# # Asserts that a path of '/items/list/1?view=print' returns the correct options
|
27
|
+
# assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
|
28
|
+
#
|
29
|
+
# The +message+ parameter allows you to pass in an error message that is displayed upon failure.
|
30
|
+
#
|
31
|
+
# # Check the default route (i.e., the index action)
|
32
|
+
# assert_recognizes({controller: 'items', action: 'index'}, 'items')
|
33
|
+
#
|
34
|
+
# # Test a specific action
|
35
|
+
# assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
|
36
|
+
#
|
37
|
+
# # Test an action with a parameter
|
38
|
+
# assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
|
39
|
+
#
|
40
|
+
# # Test a custom route
|
41
|
+
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
|
42
|
+
def assert_recognizes(expected_options, path, extras = {}, msg = nil)
|
43
|
+
if path.is_a?(Hash) && path[:method].to_s == "all"
|
44
|
+
[:get, :post, :put, :delete].each do |method|
|
45
|
+
assert_recognizes(expected_options, path.merge(method: method), extras, msg)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
request = recognized_request_for(path, extras, msg)
|
49
|
+
|
50
|
+
expected_options = expected_options.clone
|
51
|
+
|
52
|
+
expected_options.stringify_keys!
|
53
|
+
|
54
|
+
msg = message(msg, "") {
|
55
|
+
sprintf("The recognized options <%s> did not match <%s>, difference:",
|
56
|
+
request.path_parameters, expected_options)
|
57
|
+
}
|
58
|
+
|
59
|
+
assert_equal(expected_options, request.path_parameters, msg)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
|
64
|
+
# The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
|
65
|
+
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
|
66
|
+
#
|
67
|
+
# The +defaults+ parameter is unused.
|
68
|
+
#
|
69
|
+
# # Asserts that the default action is generated for a route with no action
|
70
|
+
# assert_generates "/items", controller: "items", action: "index"
|
71
|
+
#
|
72
|
+
# # Tests that the list action is properly routed
|
73
|
+
# assert_generates "/items/list", controller: "items", action: "list"
|
74
|
+
#
|
75
|
+
# # Tests the generation of a route with a parameter
|
76
|
+
# assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
|
77
|
+
#
|
78
|
+
# # Asserts that the generated route gives us our custom route
|
79
|
+
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
|
80
|
+
def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
|
81
|
+
if expected_path =~ %r{://}
|
82
|
+
fail_on(URI::InvalidURIError, message) do
|
83
|
+
uri = URI.parse(expected_path)
|
84
|
+
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
85
|
+
end
|
86
|
+
else
|
87
|
+
expected_path = "/#{expected_path}" unless expected_path.first == "/"
|
88
|
+
end
|
89
|
+
# Load routes.rb if it hasn't been loaded.
|
90
|
+
|
91
|
+
options = options.clone
|
92
|
+
generated_path, query_string_keys = @routes.generate_extras(options, defaults)
|
93
|
+
found_extras = options.reject { |k, _| ! query_string_keys.include? k }
|
94
|
+
|
95
|
+
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
|
96
|
+
assert_equal(extras, found_extras, msg)
|
97
|
+
|
98
|
+
msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
|
99
|
+
expected_path)
|
100
|
+
assert_equal(expected_path, generated_path, msg)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
|
104
|
+
# <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
|
105
|
+
# and +assert_generates+ into one step.
|
106
|
+
#
|
107
|
+
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
|
108
|
+
# +message+ parameter allows you to specify a custom error message to display upon failure.
|
109
|
+
#
|
110
|
+
# # Asserts a basic route: a controller with the default action (index)
|
111
|
+
# assert_routing '/home', controller: 'home', action: 'index'
|
112
|
+
#
|
113
|
+
# # Test a route generated with a specific controller, action, and parameter (id)
|
114
|
+
# assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
|
115
|
+
#
|
116
|
+
# # Asserts a basic route (controller + default action), with an error message if it fails
|
117
|
+
# assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
|
118
|
+
#
|
119
|
+
# # Tests a route, providing a defaults hash
|
120
|
+
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
|
121
|
+
#
|
122
|
+
# # Tests a route with an HTTP method
|
123
|
+
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
|
124
|
+
def assert_routing(path, options, defaults = {}, extras = {}, message = nil)
|
125
|
+
assert_recognizes(options, path, extras, message)
|
126
|
+
|
127
|
+
controller, default_controller = options[:controller], defaults[:controller]
|
128
|
+
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
|
129
|
+
options[:controller] = "/#{controller}"
|
130
|
+
end
|
131
|
+
|
132
|
+
generate_options = options.dup.delete_if { |k, _| defaults.key?(k) }
|
133
|
+
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
|
134
|
+
end
|
135
|
+
|
136
|
+
# A helper to make it easier to test different route configurations.
|
137
|
+
# This method temporarily replaces @routes with a new RouteSet instance.
|
138
|
+
#
|
139
|
+
# The new instance is yielded to the passed block. Typically the block
|
140
|
+
# will create some routes using <tt>set.draw { match ... }</tt>:
|
141
|
+
#
|
142
|
+
# with_routing do |set|
|
143
|
+
# set.draw do
|
144
|
+
# resources :users
|
145
|
+
# end
|
146
|
+
# assert_equal "/users", users_path
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
def with_routing
|
150
|
+
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
|
151
|
+
if defined?(@controller) && @controller
|
152
|
+
old_controller, @controller = @controller, @controller.clone
|
153
|
+
_routes = @routes
|
154
|
+
|
155
|
+
@controller.singleton_class.include(_routes.url_helpers)
|
156
|
+
|
157
|
+
if @controller.respond_to? :view_context_class
|
158
|
+
@controller.view_context_class = Class.new(@controller.view_context_class) do
|
159
|
+
include _routes.url_helpers
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
yield @routes
|
164
|
+
ensure
|
165
|
+
@routes = old_routes
|
166
|
+
if defined?(@controller) && @controller
|
167
|
+
@controller = old_controller
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# ROUTES TODO: These assertions should really work in an integration context
|
172
|
+
def method_missing(selector, *args, &block)
|
173
|
+
if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)
|
174
|
+
@controller.send(selector, *args, &block)
|
175
|
+
else
|
176
|
+
super
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
# Recognizes the route for a given path.
|
182
|
+
def recognized_request_for(path, extras = {}, msg)
|
183
|
+
if path.is_a?(Hash)
|
184
|
+
method = path[:method]
|
185
|
+
path = path[:path]
|
186
|
+
else
|
187
|
+
method = :get
|
188
|
+
end
|
189
|
+
|
190
|
+
request = ActionController::TestRequest.create @controller.class
|
191
|
+
|
192
|
+
if path =~ %r{://}
|
193
|
+
fail_on(URI::InvalidURIError, msg) do
|
194
|
+
uri = URI.parse(path)
|
195
|
+
request.env["rack.url_scheme"] = uri.scheme || "http"
|
196
|
+
request.host = uri.host if uri.host
|
197
|
+
request.port = uri.port if uri.port
|
198
|
+
request.path = uri.path.to_s.empty? ? "/" : uri.path
|
199
|
+
end
|
200
|
+
else
|
201
|
+
path = "/#{path}" unless path.first == "/"
|
202
|
+
request.path = path
|
203
|
+
end
|
204
|
+
|
205
|
+
request.request_method = method if method
|
206
|
+
|
207
|
+
params = fail_on(ActionController::RoutingError, msg) do
|
208
|
+
@routes.recognize_path(path, method: method, extras: extras)
|
209
|
+
end
|
210
|
+
request.path_parameters = params.with_indifferent_access
|
211
|
+
|
212
|
+
request
|
213
|
+
end
|
214
|
+
|
215
|
+
def fail_on(exception_class, message)
|
216
|
+
yield
|
217
|
+
rescue exception_class => e
|
218
|
+
raise Minitest::Assertion, message || e.message
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,652 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "uri"
|
5
|
+
require "active_support/core_ext/kernel/singleton_class"
|
6
|
+
require "active_support/core_ext/object/try"
|
7
|
+
require "rack/test"
|
8
|
+
require "minitest"
|
9
|
+
|
10
|
+
require "action_dispatch/testing/request_encoder"
|
11
|
+
|
12
|
+
module ActionDispatch
|
13
|
+
module Integration #:nodoc:
|
14
|
+
module RequestHelpers
|
15
|
+
# Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
|
16
|
+
# for more details.
|
17
|
+
def get(path, **args)
|
18
|
+
process(:get, path, **args)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Performs a POST request with the given parameters. See ActionDispatch::Integration::Session#process
|
22
|
+
# for more details.
|
23
|
+
def post(path, **args)
|
24
|
+
process(:post, path, **args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Performs a PATCH request with the given parameters. See ActionDispatch::Integration::Session#process
|
28
|
+
# for more details.
|
29
|
+
def patch(path, **args)
|
30
|
+
process(:patch, path, **args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Performs a PUT request with the given parameters. See ActionDispatch::Integration::Session#process
|
34
|
+
# for more details.
|
35
|
+
def put(path, **args)
|
36
|
+
process(:put, path, **args)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Performs a DELETE request with the given parameters. See ActionDispatch::Integration::Session#process
|
40
|
+
# for more details.
|
41
|
+
def delete(path, **args)
|
42
|
+
process(:delete, path, **args)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process
|
46
|
+
# for more details.
|
47
|
+
def head(path, *args)
|
48
|
+
process(:head, path, *args)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Follow a single redirect response. If the last response was not a
|
52
|
+
# redirect, an exception will be raised. Otherwise, the redirect is
|
53
|
+
# performed on the location header.
|
54
|
+
def follow_redirect!
|
55
|
+
raise "not a redirect! #{status} #{status_message}" unless redirect?
|
56
|
+
get(response.location)
|
57
|
+
status
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# An instance of this class represents a set of requests and responses
|
62
|
+
# performed sequentially by a test process. Because you can instantiate
|
63
|
+
# multiple sessions and run them side-by-side, you can also mimic (to some
|
64
|
+
# limited extent) multiple simultaneous users interacting with your system.
|
65
|
+
#
|
66
|
+
# Typically, you will instantiate a new session using
|
67
|
+
# IntegrationTest#open_session, rather than instantiating
|
68
|
+
# Integration::Session directly.
|
69
|
+
class Session
|
70
|
+
DEFAULT_HOST = "www.example.com"
|
71
|
+
|
72
|
+
include Minitest::Assertions
|
73
|
+
include TestProcess, RequestHelpers, Assertions
|
74
|
+
|
75
|
+
%w( status status_message headers body redirect? ).each do |method|
|
76
|
+
delegate method, to: :response, allow_nil: true
|
77
|
+
end
|
78
|
+
|
79
|
+
%w( path ).each do |method|
|
80
|
+
delegate method, to: :request, allow_nil: true
|
81
|
+
end
|
82
|
+
|
83
|
+
# The hostname used in the last request.
|
84
|
+
def host
|
85
|
+
@host || DEFAULT_HOST
|
86
|
+
end
|
87
|
+
attr_writer :host
|
88
|
+
|
89
|
+
# The remote_addr used in the last request.
|
90
|
+
attr_accessor :remote_addr
|
91
|
+
|
92
|
+
# The Accept header to send.
|
93
|
+
attr_accessor :accept
|
94
|
+
|
95
|
+
# A map of the cookies returned by the last response, and which will be
|
96
|
+
# sent with the next request.
|
97
|
+
def cookies
|
98
|
+
_mock_session.cookie_jar
|
99
|
+
end
|
100
|
+
|
101
|
+
# A reference to the controller instance used by the last request.
|
102
|
+
attr_reader :controller
|
103
|
+
|
104
|
+
# A reference to the request instance used by the last request.
|
105
|
+
attr_reader :request
|
106
|
+
|
107
|
+
# A reference to the response instance used by the last request.
|
108
|
+
attr_reader :response
|
109
|
+
|
110
|
+
# A running counter of the number of requests processed.
|
111
|
+
attr_accessor :request_count
|
112
|
+
|
113
|
+
include ActionDispatch::Routing::UrlFor
|
114
|
+
|
115
|
+
# Create and initialize a new Session instance.
|
116
|
+
def initialize(app)
|
117
|
+
super()
|
118
|
+
@app = app
|
119
|
+
|
120
|
+
reset!
|
121
|
+
end
|
122
|
+
|
123
|
+
def url_options
|
124
|
+
@url_options ||= default_url_options.dup.tap do |url_options|
|
125
|
+
url_options.reverse_merge!(controller.url_options) if controller
|
126
|
+
|
127
|
+
if @app.respond_to?(:routes)
|
128
|
+
url_options.reverse_merge!(@app.routes.default_url_options)
|
129
|
+
end
|
130
|
+
|
131
|
+
url_options.reverse_merge!(host: host, protocol: https? ? "https" : "http")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Resets the instance. This can be used to reset the state information
|
136
|
+
# in an existing session instance, so it can be used from a clean-slate
|
137
|
+
# condition.
|
138
|
+
#
|
139
|
+
# session.reset!
|
140
|
+
def reset!
|
141
|
+
@https = false
|
142
|
+
@controller = @request = @response = nil
|
143
|
+
@_mock_session = nil
|
144
|
+
@request_count = 0
|
145
|
+
@url_options = nil
|
146
|
+
|
147
|
+
self.host = DEFAULT_HOST
|
148
|
+
self.remote_addr = "127.0.0.1"
|
149
|
+
self.accept = "text/xml,application/xml,application/xhtml+xml," \
|
150
|
+
"text/html;q=0.9,text/plain;q=0.8,image/png," \
|
151
|
+
"*/*;q=0.5"
|
152
|
+
|
153
|
+
unless defined? @named_routes_configured
|
154
|
+
# the helpers are made protected by default--we make them public for
|
155
|
+
# easier access during testing and troubleshooting.
|
156
|
+
@named_routes_configured = true
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Specify whether or not the session should mimic a secure HTTPS request.
|
161
|
+
#
|
162
|
+
# session.https!
|
163
|
+
# session.https!(false)
|
164
|
+
def https!(flag = true)
|
165
|
+
@https = flag
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns +true+ if the session is mimicking a secure HTTPS request.
|
169
|
+
#
|
170
|
+
# if session.https?
|
171
|
+
# ...
|
172
|
+
# end
|
173
|
+
def https?
|
174
|
+
@https
|
175
|
+
end
|
176
|
+
|
177
|
+
# Performs the actual request.
|
178
|
+
#
|
179
|
+
# - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
|
180
|
+
# as a symbol.
|
181
|
+
# - +path+: The URI (as a String) on which you want to perform the
|
182
|
+
# request.
|
183
|
+
# - +params+: The HTTP parameters that you want to pass. This may
|
184
|
+
# be +nil+,
|
185
|
+
# a Hash, or a String that is appropriately encoded
|
186
|
+
# (<tt>application/x-www-form-urlencoded</tt> or
|
187
|
+
# <tt>multipart/form-data</tt>).
|
188
|
+
# - +headers+: Additional headers to pass, as a Hash. The headers will be
|
189
|
+
# merged into the Rack env hash.
|
190
|
+
# - +env+: Additional env to pass, as a Hash. The headers will be
|
191
|
+
# merged into the Rack env hash.
|
192
|
+
#
|
193
|
+
# This method is rarely used directly. Use +#get+, +#post+, or other standard
|
194
|
+
# HTTP methods in integration tests. +#process+ is only required when using a
|
195
|
+
# request method that doesn't have a method defined in the integration tests.
|
196
|
+
#
|
197
|
+
# This method returns the response status, after performing the request.
|
198
|
+
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
|
199
|
+
# then that object's <tt>@response</tt> instance variable will point to a Response object
|
200
|
+
# which one can use to inspect the details of the response.
|
201
|
+
#
|
202
|
+
# Example:
|
203
|
+
# process :get, '/author', params: { since: 201501011400 }
|
204
|
+
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
205
|
+
request_encoder = RequestEncoder.encoder(as)
|
206
|
+
headers ||= {}
|
207
|
+
|
208
|
+
if method == :get && as == :json && params
|
209
|
+
headers["X-Http-Method-Override"] = "GET"
|
210
|
+
method = :post
|
211
|
+
end
|
212
|
+
|
213
|
+
if path =~ %r{://}
|
214
|
+
path = build_expanded_path(path) do |location|
|
215
|
+
https! URI::HTTPS === location if location.scheme
|
216
|
+
|
217
|
+
if url_host = location.host
|
218
|
+
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
219
|
+
url_host += ":#{location.port}" if default != location.port
|
220
|
+
host! url_host
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
hostname, port = host.split(":")
|
226
|
+
|
227
|
+
request_env = {
|
228
|
+
:method => method,
|
229
|
+
:params => request_encoder.encode_params(params),
|
230
|
+
|
231
|
+
"SERVER_NAME" => hostname,
|
232
|
+
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
233
|
+
"HTTPS" => https? ? "on" : "off",
|
234
|
+
"rack.url_scheme" => https? ? "https" : "http",
|
235
|
+
|
236
|
+
"REQUEST_URI" => path,
|
237
|
+
"HTTP_HOST" => host,
|
238
|
+
"REMOTE_ADDR" => remote_addr,
|
239
|
+
"CONTENT_TYPE" => request_encoder.content_type,
|
240
|
+
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
241
|
+
}
|
242
|
+
|
243
|
+
wrapped_headers = Http::Headers.from_hash({})
|
244
|
+
wrapped_headers.merge!(headers) if headers
|
245
|
+
|
246
|
+
if xhr
|
247
|
+
wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
|
248
|
+
wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
249
|
+
end
|
250
|
+
|
251
|
+
# This modifies the passed request_env directly.
|
252
|
+
if wrapped_headers.present?
|
253
|
+
Http::Headers.from_hash(request_env).merge!(wrapped_headers)
|
254
|
+
end
|
255
|
+
if env.present?
|
256
|
+
Http::Headers.from_hash(request_env).merge!(env)
|
257
|
+
end
|
258
|
+
|
259
|
+
session = Rack::Test::Session.new(_mock_session)
|
260
|
+
|
261
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
262
|
+
# Make sure requested path is always a full URI.
|
263
|
+
session.request(build_full_uri(path, request_env), request_env)
|
264
|
+
|
265
|
+
@request_count += 1
|
266
|
+
@request = ActionDispatch::Request.new(session.last_request.env)
|
267
|
+
response = _mock_session.last_response
|
268
|
+
@response = ActionDispatch::TestResponse.from_response(response)
|
269
|
+
@response.request = @request
|
270
|
+
@html_document = nil
|
271
|
+
@url_options = nil
|
272
|
+
|
273
|
+
@controller = @request.controller_instance
|
274
|
+
|
275
|
+
response.status
|
276
|
+
end
|
277
|
+
|
278
|
+
# Set the host name to use in the next request.
|
279
|
+
#
|
280
|
+
# session.host! "www.example.com"
|
281
|
+
alias :host! :host=
|
282
|
+
|
283
|
+
private
|
284
|
+
def _mock_session
|
285
|
+
@_mock_session ||= Rack::MockSession.new(@app, host)
|
286
|
+
end
|
287
|
+
|
288
|
+
def build_full_uri(path, env)
|
289
|
+
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
|
290
|
+
end
|
291
|
+
|
292
|
+
def build_expanded_path(path)
|
293
|
+
location = URI.parse(path)
|
294
|
+
yield location if block_given?
|
295
|
+
path = location.path
|
296
|
+
location.query ? "#{path}?#{location.query}" : path
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
module Runner
|
301
|
+
include ActionDispatch::Assertions
|
302
|
+
|
303
|
+
APP_SESSIONS = {}
|
304
|
+
|
305
|
+
attr_reader :app
|
306
|
+
|
307
|
+
def initialize(*args, &blk)
|
308
|
+
super(*args, &blk)
|
309
|
+
@integration_session = nil
|
310
|
+
end
|
311
|
+
|
312
|
+
def before_setup # :nodoc:
|
313
|
+
@app = nil
|
314
|
+
super
|
315
|
+
end
|
316
|
+
|
317
|
+
def integration_session
|
318
|
+
@integration_session ||= create_session(app)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Reset the current session. This is useful for testing multiple sessions
|
322
|
+
# in a single test case.
|
323
|
+
def reset!
|
324
|
+
@integration_session = create_session(app)
|
325
|
+
end
|
326
|
+
|
327
|
+
def create_session(app)
|
328
|
+
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
|
329
|
+
# If the app is a Rails app, make url_helpers available on the session.
|
330
|
+
# This makes app.url_for and app.foo_path available in the console.
|
331
|
+
if app.respond_to?(:routes)
|
332
|
+
include app.routes.url_helpers
|
333
|
+
include app.routes.mounted_helpers
|
334
|
+
end
|
335
|
+
}
|
336
|
+
klass.new(app)
|
337
|
+
end
|
338
|
+
|
339
|
+
def remove! # :nodoc:
|
340
|
+
@integration_session = nil
|
341
|
+
end
|
342
|
+
|
343
|
+
%w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
|
344
|
+
define_method(method) do |*args|
|
345
|
+
# reset the html_document variable, except for cookies/assigns calls
|
346
|
+
unless method == "cookies" || method == "assigns"
|
347
|
+
@html_document = nil
|
348
|
+
end
|
349
|
+
|
350
|
+
integration_session.__send__(method, *args).tap do
|
351
|
+
copy_session_variables!
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# Open a new session instance. If a block is given, the new session is
|
357
|
+
# yielded to the block before being returned.
|
358
|
+
#
|
359
|
+
# session = open_session do |sess|
|
360
|
+
# sess.extend(CustomAssertions)
|
361
|
+
# end
|
362
|
+
#
|
363
|
+
# By default, a single session is automatically created for you, but you
|
364
|
+
# can use this method to open multiple sessions that ought to be tested
|
365
|
+
# simultaneously.
|
366
|
+
def open_session
|
367
|
+
dup.tap do |session|
|
368
|
+
session.reset!
|
369
|
+
yield session if block_given?
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Copy the instance variables from the current session instance into the
|
374
|
+
# test instance.
|
375
|
+
def copy_session_variables! #:nodoc:
|
376
|
+
@controller = @integration_session.controller
|
377
|
+
@response = @integration_session.response
|
378
|
+
@request = @integration_session.request
|
379
|
+
end
|
380
|
+
|
381
|
+
def default_url_options
|
382
|
+
integration_session.default_url_options
|
383
|
+
end
|
384
|
+
|
385
|
+
def default_url_options=(options)
|
386
|
+
integration_session.default_url_options = options
|
387
|
+
end
|
388
|
+
|
389
|
+
private
|
390
|
+
def respond_to_missing?(method, _)
|
391
|
+
integration_session.respond_to?(method) || super
|
392
|
+
end
|
393
|
+
|
394
|
+
# Delegate unhandled messages to the current session instance.
|
395
|
+
def method_missing(method, *args, &block)
|
396
|
+
if integration_session.respond_to?(method)
|
397
|
+
integration_session.public_send(method, *args, &block).tap do
|
398
|
+
copy_session_variables!
|
399
|
+
end
|
400
|
+
else
|
401
|
+
super
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
# An integration test spans multiple controllers and actions,
|
408
|
+
# tying them all together to ensure they work together as expected. It tests
|
409
|
+
# more completely than either unit or functional tests do, exercising the
|
410
|
+
# entire stack, from the dispatcher to the database.
|
411
|
+
#
|
412
|
+
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
|
413
|
+
# using the get/post methods:
|
414
|
+
#
|
415
|
+
# require "test_helper"
|
416
|
+
#
|
417
|
+
# class ExampleTest < ActionDispatch::IntegrationTest
|
418
|
+
# fixtures :people
|
419
|
+
#
|
420
|
+
# def test_login
|
421
|
+
# # get the login page
|
422
|
+
# get "/login"
|
423
|
+
# assert_equal 200, status
|
424
|
+
#
|
425
|
+
# # post the login and follow through to the home page
|
426
|
+
# post "/login", params: { username: people(:jamis).username,
|
427
|
+
# password: people(:jamis).password }
|
428
|
+
# follow_redirect!
|
429
|
+
# assert_equal 200, status
|
430
|
+
# assert_equal "/home", path
|
431
|
+
# end
|
432
|
+
# end
|
433
|
+
#
|
434
|
+
# However, you can also have multiple session instances open per test, and
|
435
|
+
# even extend those instances with assertions and methods to create a very
|
436
|
+
# powerful testing DSL that is specific for your application. You can even
|
437
|
+
# reference any named routes you happen to have defined.
|
438
|
+
#
|
439
|
+
# require "test_helper"
|
440
|
+
#
|
441
|
+
# class AdvancedTest < ActionDispatch::IntegrationTest
|
442
|
+
# fixtures :people, :rooms
|
443
|
+
#
|
444
|
+
# def test_login_and_speak
|
445
|
+
# jamis, david = login(:jamis), login(:david)
|
446
|
+
# room = rooms(:office)
|
447
|
+
#
|
448
|
+
# jamis.enter(room)
|
449
|
+
# jamis.speak(room, "anybody home?")
|
450
|
+
#
|
451
|
+
# david.enter(room)
|
452
|
+
# david.speak(room, "hello!")
|
453
|
+
# end
|
454
|
+
#
|
455
|
+
# private
|
456
|
+
#
|
457
|
+
# module CustomAssertions
|
458
|
+
# def enter(room)
|
459
|
+
# # reference a named route, for maximum internal consistency!
|
460
|
+
# get(room_url(id: room.id))
|
461
|
+
# assert(...)
|
462
|
+
# ...
|
463
|
+
# end
|
464
|
+
#
|
465
|
+
# def speak(room, message)
|
466
|
+
# post "/say/#{room.id}", xhr: true, params: { message: message }
|
467
|
+
# assert(...)
|
468
|
+
# ...
|
469
|
+
# end
|
470
|
+
# end
|
471
|
+
#
|
472
|
+
# def login(who)
|
473
|
+
# open_session do |sess|
|
474
|
+
# sess.extend(CustomAssertions)
|
475
|
+
# who = people(who)
|
476
|
+
# sess.post "/login", params: { username: who.username,
|
477
|
+
# password: who.password }
|
478
|
+
# assert(...)
|
479
|
+
# end
|
480
|
+
# end
|
481
|
+
# end
|
482
|
+
#
|
483
|
+
# Another longer example would be:
|
484
|
+
#
|
485
|
+
# A simple integration test that exercises multiple controllers:
|
486
|
+
#
|
487
|
+
# require 'test_helper'
|
488
|
+
#
|
489
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
490
|
+
# test "login and browse site" do
|
491
|
+
# # login via https
|
492
|
+
# https!
|
493
|
+
# get "/login"
|
494
|
+
# assert_response :success
|
495
|
+
#
|
496
|
+
# post "/login", params: { username: users(:david).username, password: users(:david).password }
|
497
|
+
# follow_redirect!
|
498
|
+
# assert_equal '/welcome', path
|
499
|
+
# assert_equal 'Welcome david!', flash[:notice]
|
500
|
+
#
|
501
|
+
# https!(false)
|
502
|
+
# get "/articles/all"
|
503
|
+
# assert_response :success
|
504
|
+
# assert_select 'h1', 'Articles'
|
505
|
+
# end
|
506
|
+
# end
|
507
|
+
#
|
508
|
+
# As you can see the integration test involves multiple controllers and
|
509
|
+
# exercises the entire stack from database to dispatcher. In addition you can
|
510
|
+
# have multiple session instances open simultaneously in a test and extend
|
511
|
+
# those instances with assertion methods to create a very powerful testing
|
512
|
+
# DSL (domain-specific language) just for your application.
|
513
|
+
#
|
514
|
+
# Here's an example of multiple sessions and custom DSL in an integration test
|
515
|
+
#
|
516
|
+
# require 'test_helper'
|
517
|
+
#
|
518
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
519
|
+
# test "login and browse site" do
|
520
|
+
# # User david logs in
|
521
|
+
# david = login(:david)
|
522
|
+
# # User guest logs in
|
523
|
+
# guest = login(:guest)
|
524
|
+
#
|
525
|
+
# # Both are now available in different sessions
|
526
|
+
# assert_equal 'Welcome david!', david.flash[:notice]
|
527
|
+
# assert_equal 'Welcome guest!', guest.flash[:notice]
|
528
|
+
#
|
529
|
+
# # User david can browse site
|
530
|
+
# david.browses_site
|
531
|
+
# # User guest can browse site as well
|
532
|
+
# guest.browses_site
|
533
|
+
#
|
534
|
+
# # Continue with other assertions
|
535
|
+
# end
|
536
|
+
#
|
537
|
+
# private
|
538
|
+
#
|
539
|
+
# module CustomDsl
|
540
|
+
# def browses_site
|
541
|
+
# get "/products/all"
|
542
|
+
# assert_response :success
|
543
|
+
# assert_select 'h1', 'Products'
|
544
|
+
# end
|
545
|
+
# end
|
546
|
+
#
|
547
|
+
# def login(user)
|
548
|
+
# open_session do |sess|
|
549
|
+
# sess.extend(CustomDsl)
|
550
|
+
# u = users(user)
|
551
|
+
# sess.https!
|
552
|
+
# sess.post "/login", params: { username: u.username, password: u.password }
|
553
|
+
# assert_equal '/welcome', sess.path
|
554
|
+
# sess.https!(false)
|
555
|
+
# end
|
556
|
+
# end
|
557
|
+
# end
|
558
|
+
#
|
559
|
+
# See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
|
560
|
+
# use +get+, etc.
|
561
|
+
#
|
562
|
+
# === Changing the request encoding
|
563
|
+
#
|
564
|
+
# You can also test your JSON API easily by setting what the request should
|
565
|
+
# be encoded as:
|
566
|
+
#
|
567
|
+
# require "test_helper"
|
568
|
+
#
|
569
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
570
|
+
# test "creates articles" do
|
571
|
+
# assert_difference -> { Article.count } do
|
572
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
573
|
+
# end
|
574
|
+
#
|
575
|
+
# assert_response :success
|
576
|
+
# assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
|
577
|
+
# end
|
578
|
+
# end
|
579
|
+
#
|
580
|
+
# The +as+ option passes an "application/json" Accept header (thereby setting
|
581
|
+
# the request format to JSON unless overridden), sets the content type to
|
582
|
+
# "application/json" and encodes the parameters as JSON.
|
583
|
+
#
|
584
|
+
# Calling +parsed_body+ on the response parses the response body based on the
|
585
|
+
# last response MIME type.
|
586
|
+
#
|
587
|
+
# Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
|
588
|
+
# types you've registered, you can add your own encoders with:
|
589
|
+
#
|
590
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
591
|
+
# param_encoder: -> params { params.to_wibble },
|
592
|
+
# response_parser: -> body { body }
|
593
|
+
#
|
594
|
+
# Where +param_encoder+ defines how the params should be encoded and
|
595
|
+
# +response_parser+ defines how the response body should be parsed through
|
596
|
+
# +parsed_body+.
|
597
|
+
#
|
598
|
+
# Consult the Rails Testing Guide for more.
|
599
|
+
|
600
|
+
class IntegrationTest < ActiveSupport::TestCase
|
601
|
+
include TestProcess::FixtureFile
|
602
|
+
|
603
|
+
module UrlOptions
|
604
|
+
extend ActiveSupport::Concern
|
605
|
+
def url_options
|
606
|
+
integration_session.url_options
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
module Behavior
|
611
|
+
extend ActiveSupport::Concern
|
612
|
+
|
613
|
+
include Integration::Runner
|
614
|
+
include ActionController::TemplateAssertions
|
615
|
+
|
616
|
+
included do
|
617
|
+
include ActionDispatch::Routing::UrlFor
|
618
|
+
include UrlOptions # don't let UrlFor override the url_options method
|
619
|
+
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
|
620
|
+
@@app = nil
|
621
|
+
end
|
622
|
+
|
623
|
+
module ClassMethods
|
624
|
+
def app
|
625
|
+
if defined?(@@app) && @@app
|
626
|
+
@@app
|
627
|
+
else
|
628
|
+
ActionDispatch.test_app
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
def app=(app)
|
633
|
+
@@app = app
|
634
|
+
end
|
635
|
+
|
636
|
+
def register_encoder(*args)
|
637
|
+
RequestEncoder.register_encoder(*args)
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def app
|
642
|
+
super || self.class.app
|
643
|
+
end
|
644
|
+
|
645
|
+
def document_root_element
|
646
|
+
html_document.root
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
include Behavior
|
651
|
+
end
|
652
|
+
end
|