actionpack 7.0.8.7 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +90 -537
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +119 -106
- data/lib/abstract_controller/caching/fragments.rb +51 -52
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +94 -67
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +121 -91
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +14 -13
- data/lib/abstract_controller/translation.rb +12 -30
- data/lib/abstract_controller/url_for.rb +9 -5
- data/lib/abstract_controller.rb +8 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +78 -73
- data/lib/action_controller/base.rb +199 -141
- data/lib/action_controller/caching.rb +16 -11
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +21 -16
- data/lib/action_controller/log_subscriber.rb +19 -5
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +187 -174
- data/lib/action_controller/metal/content_security_policy.rb +26 -25
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +65 -54
- data/lib/action_controller/metal/default_headers.rb +6 -2
- data/lib/action_controller/metal/etag_with_flash.rb +4 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
- data/lib/action_controller/metal/exceptions.rb +19 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +20 -16
- data/lib/action_controller/metal/helpers.rb +64 -67
- data/lib/action_controller/metal/http_authentication.rb +212 -199
- data/lib/action_controller/metal/implicit_render.rb +21 -17
- data/lib/action_controller/metal/instrumentation.rb +22 -12
- data/lib/action_controller/metal/live.rb +125 -92
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +58 -58
- data/lib/action_controller/metal/permissions_policy.rb +14 -13
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +110 -84
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -82
- data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
- data/lib/action_controller/metal/rescue.rb +12 -8
- data/lib/action_controller/metal/streaming.rb +174 -132
- data/lib/action_controller/metal/strong_parameters.rb +598 -473
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +23 -14
- data/lib/action_controller/metal.rb +145 -61
- data/lib/action_controller/railtie.rb +25 -9
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +105 -66
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +157 -128
- data/lib/action_controller.rb +17 -3
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +28 -29
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +48 -45
- data/lib/action_dispatch/http/filter_parameters.rb +18 -8
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +23 -21
- data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
- data/lib/action_dispatch/http/mime_type.rb +60 -30
- data/lib/action_dispatch/http/mime_types.rb +5 -1
- data/lib/action_dispatch/http/parameters.rb +12 -10
- data/lib/action_dispatch/http/permissions_policy.rb +32 -27
- data/lib/action_dispatch/http/rack_cache.rb +4 -0
- data/lib/action_dispatch/http/request.rb +132 -79
- data/lib/action_dispatch/http/response.rb +136 -103
- data/lib/action_dispatch/http/upload.rb +19 -15
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +19 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +18 -15
- data/lib/action_dispatch/journey/route.rb +12 -9
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +13 -10
- data/lib/action_dispatch/journey/routes.rb +6 -4
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +4 -0
- data/lib/action_dispatch/middleware/cookies.rb +192 -194
- data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
- data/lib/action_dispatch/middleware/debug_view.rb +9 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
- data/lib/action_dispatch/middleware/executor.rb +9 -1
- data/lib/action_dispatch/middleware/flash.rb +65 -46
- data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
- data/lib/action_dispatch/middleware/reloader.rb +9 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
- data/lib/action_dispatch/middleware/request_id.rb +15 -8
- data/lib/action_dispatch/middleware/server_timing.rb +8 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
- data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
- data/lib/action_dispatch/middleware/ssl.rb +60 -45
- data/lib/action_dispatch/middleware/stack.rb +15 -9
- data/lib/action_dispatch/middleware/static.rb +40 -34
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
- data/lib/action_dispatch/railtie.rb +12 -4
- data/lib/action_dispatch/request/session.rb +39 -27
- data/lib/action_dispatch/request/utils.rb +10 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +59 -9
- data/lib/action_dispatch/routing/mapper.rb +686 -639
- data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
- data/lib/action_dispatch/routing/redirection.rb +52 -38
- data/lib/action_dispatch/routing/route_set.rb +106 -62
- data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
- data/lib/action_dispatch/routing/url_for.rb +131 -122
- data/lib/action_dispatch/routing.rb +152 -150
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +27 -19
- data/lib/action_dispatch/system_testing/driver.rb +16 -22
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +9 -7
- data/lib/action_dispatch/testing/assertions/response.rb +36 -26
- data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
- data/lib/action_dispatch/testing/assertions.rb +5 -1
- data/lib/action_dispatch/testing/integration.rb +240 -229
- data/lib/action_dispatch/testing/request_encoder.rb +6 -1
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +14 -9
- data/lib/action_dispatch/testing/test_request.rb +4 -2
- data/lib/action_dispatch/testing/test_response.rb +34 -19
- data/lib/action_dispatch.rb +52 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +18 -17
- metadata +86 -27
@@ -1,64 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "stringio"
|
4
6
|
require "uri"
|
5
7
|
require "rack/test"
|
6
|
-
require "
|
8
|
+
require "active_support/test_case"
|
7
9
|
|
8
10
|
require "action_dispatch/testing/request_encoder"
|
11
|
+
require "action_dispatch/testing/test_helpers/page_dump_helper"
|
9
12
|
|
10
13
|
module ActionDispatch
|
11
14
|
module Integration # :nodoc:
|
12
15
|
module RequestHelpers
|
13
|
-
# Performs a GET request with the given parameters. See
|
14
|
-
# for more details.
|
16
|
+
# Performs a GET request with the given parameters. See
|
17
|
+
# ActionDispatch::Integration::Session#process for more details.
|
15
18
|
def get(path, **args)
|
16
19
|
process(:get, path, **args)
|
17
20
|
end
|
18
21
|
|
19
|
-
# Performs a POST request with the given parameters. See
|
20
|
-
# for more details.
|
22
|
+
# Performs a POST request with the given parameters. See
|
23
|
+
# ActionDispatch::Integration::Session#process for more details.
|
21
24
|
def post(path, **args)
|
22
25
|
process(:post, path, **args)
|
23
26
|
end
|
24
27
|
|
25
|
-
# Performs a PATCH request with the given parameters. See
|
26
|
-
# for more details.
|
28
|
+
# Performs a PATCH request with the given parameters. See
|
29
|
+
# ActionDispatch::Integration::Session#process for more details.
|
27
30
|
def patch(path, **args)
|
28
31
|
process(:patch, path, **args)
|
29
32
|
end
|
30
33
|
|
31
|
-
# Performs a PUT request with the given parameters. See
|
32
|
-
# for more details.
|
34
|
+
# Performs a PUT request with the given parameters. See
|
35
|
+
# ActionDispatch::Integration::Session#process for more details.
|
33
36
|
def put(path, **args)
|
34
37
|
process(:put, path, **args)
|
35
38
|
end
|
36
39
|
|
37
|
-
# Performs a DELETE request with the given parameters. See
|
38
|
-
# for more details.
|
40
|
+
# Performs a DELETE request with the given parameters. See
|
41
|
+
# ActionDispatch::Integration::Session#process for more details.
|
39
42
|
def delete(path, **args)
|
40
43
|
process(:delete, path, **args)
|
41
44
|
end
|
42
45
|
|
43
|
-
# Performs a HEAD request with the given parameters. See
|
44
|
-
# for more details.
|
46
|
+
# Performs a HEAD request with the given parameters. See
|
47
|
+
# ActionDispatch::Integration::Session#process for more details.
|
45
48
|
def head(path, **args)
|
46
49
|
process(:head, path, **args)
|
47
50
|
end
|
48
51
|
|
49
|
-
# Performs an OPTIONS request with the given parameters. See
|
50
|
-
# for more details.
|
52
|
+
# Performs an OPTIONS request with the given parameters. See
|
53
|
+
# ActionDispatch::Integration::Session#process for more details.
|
51
54
|
def options(path, **args)
|
52
55
|
process(:options, path, **args)
|
53
56
|
end
|
54
57
|
|
55
|
-
# Follow a single redirect response. If the last response was not a
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
|
58
|
+
# Follow a single redirect response. If the last response was not a redirect, an
|
59
|
+
# exception will be raised. Otherwise, the redirect is performed on the location
|
60
|
+
# header. If the redirection is a 307 or 308 redirect, the same HTTP verb will
|
61
|
+
# be used when redirecting, otherwise a GET request will be performed. Any
|
62
|
+
# arguments are passed to the underlying request.
|
63
|
+
#
|
64
|
+
# The HTTP_REFERER header will be set to the previous url.
|
65
|
+
def follow_redirect!(headers: {}, **args)
|
62
66
|
raise "not a redirect! #{status} #{status_message}" unless redirect?
|
63
67
|
|
64
68
|
method =
|
@@ -68,19 +72,22 @@ module ActionDispatch
|
|
68
72
|
:get
|
69
73
|
end
|
70
74
|
|
71
|
-
|
75
|
+
if [ :HTTP_REFERER, "HTTP_REFERER" ].none? { |key| headers.key? key }
|
76
|
+
headers["HTTP_REFERER"] = request.url
|
77
|
+
end
|
78
|
+
|
79
|
+
public_send(method, response.location, headers: headers, **args)
|
72
80
|
status
|
73
81
|
end
|
74
82
|
end
|
75
83
|
|
76
|
-
# An instance of this class represents a set of requests and responses
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
84
|
+
# An instance of this class represents a set of requests and responses performed
|
85
|
+
# sequentially by a test process. Because you can instantiate multiple sessions
|
86
|
+
# and run them side-by-side, you can also mimic (to some limited extent)
|
87
|
+
# multiple simultaneous users interacting with your system.
|
80
88
|
#
|
81
|
-
# Typically, you will instantiate a new session using
|
82
|
-
#
|
83
|
-
# Integration::Session directly.
|
89
|
+
# Typically, you will instantiate a new session using Runner#open_session,
|
90
|
+
# rather than instantiating a Session directly.
|
84
91
|
class Session
|
85
92
|
DEFAULT_HOST = "www.example.com"
|
86
93
|
|
@@ -102,8 +109,8 @@ module ActionDispatch
|
|
102
109
|
# The Accept header to send.
|
103
110
|
attr_accessor :accept
|
104
111
|
|
105
|
-
# A map of the cookies returned by the last response, and which will be
|
106
|
-
#
|
112
|
+
# A map of the cookies returned by the last response, and which will be sent
|
113
|
+
# with the next request.
|
107
114
|
def cookies
|
108
115
|
_mock_session.cookie_jar
|
109
116
|
end
|
@@ -142,11 +149,10 @@ module ActionDispatch
|
|
142
149
|
end
|
143
150
|
end
|
144
151
|
|
145
|
-
# Resets the instance. This can be used to reset the state information
|
146
|
-
#
|
147
|
-
# condition.
|
152
|
+
# Resets the instance. This can be used to reset the state information in an
|
153
|
+
# existing session instance, so it can be used from a clean-slate condition.
|
148
154
|
#
|
149
|
-
#
|
155
|
+
# session.reset!
|
150
156
|
def reset!
|
151
157
|
@https = false
|
152
158
|
@controller = @request = @response = nil
|
@@ -161,62 +167,61 @@ module ActionDispatch
|
|
161
167
|
"*/*;q=0.5"
|
162
168
|
|
163
169
|
unless defined? @named_routes_configured
|
164
|
-
# the helpers are made protected by default--we make them public for
|
165
|
-
#
|
170
|
+
# the helpers are made protected by default--we make them public for easier
|
171
|
+
# access during testing and troubleshooting.
|
166
172
|
@named_routes_configured = true
|
167
173
|
end
|
168
174
|
end
|
169
175
|
|
170
176
|
# Specify whether or not the session should mimic a secure HTTPS request.
|
171
177
|
#
|
172
|
-
#
|
173
|
-
#
|
178
|
+
# session.https!
|
179
|
+
# session.https!(false)
|
174
180
|
def https!(flag = true)
|
175
181
|
@https = flag
|
176
182
|
end
|
177
183
|
|
178
|
-
# Returns
|
184
|
+
# Returns `true` if the session is mimicking a secure HTTPS request.
|
179
185
|
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
186
|
+
# if session.https?
|
187
|
+
# ...
|
188
|
+
# end
|
183
189
|
def https?
|
184
190
|
@https
|
185
191
|
end
|
186
192
|
|
187
193
|
# Performs the actual request.
|
188
194
|
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
# - +as+: Used for encoding the request with different content type.
|
206
|
-
# Supports +:json+ by default and will set the appropriate request headers.
|
207
|
-
# The headers will be merged into the Rack env hash.
|
195
|
+
# * `method`: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
|
196
|
+
# as a symbol.
|
197
|
+
# * `path`: The URI (as a String) on which you want to perform the request.
|
198
|
+
# * `params`: The HTTP parameters that you want to pass. This may be `nil`, a
|
199
|
+
# Hash, or a String that is appropriately encoded
|
200
|
+
# (`application/x-www-form-urlencoded` or `multipart/form-data`).
|
201
|
+
# * `headers`: Additional headers to pass, as a Hash. The headers will be
|
202
|
+
# merged into the Rack env hash.
|
203
|
+
# * `env`: Additional env to pass, as a Hash. The headers will be merged into
|
204
|
+
# the Rack env hash.
|
205
|
+
# * `xhr`: Set to `true` if you want to make an Ajax request. Adds request
|
206
|
+
# headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH. The
|
207
|
+
# headers will be merged into the Rack env hash.
|
208
|
+
# * `as`: Used for encoding the request with different content type. Supports
|
209
|
+
# `:json` by default and will set the appropriate request headers. The
|
210
|
+
# headers will be merged into the Rack env hash.
|
208
211
|
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
+
#
|
213
|
+
# This method is rarely used directly. Use RequestHelpers#get,
|
214
|
+
# RequestHelpers#post, or other standard HTTP methods in integration tests.
|
215
|
+
# `#process` is only required when using a request method that doesn't have a
|
216
|
+
# method defined in the integration tests.
|
212
217
|
#
|
213
218
|
# This method returns the response status, after performing the request.
|
214
|
-
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest
|
215
|
-
# then that object's
|
216
|
-
# which one can use to inspect the details of the response.
|
219
|
+
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest
|
220
|
+
# object, then that object's `@response` instance variable will point to a
|
221
|
+
# Response object which one can use to inspect the details of the response.
|
217
222
|
#
|
218
223
|
# Example:
|
219
|
-
#
|
224
|
+
# process :get, '/author', params: { since: 201501011400 }
|
220
225
|
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
221
226
|
request_encoder = RequestEncoder.encoder(as)
|
222
227
|
headers ||= {}
|
@@ -226,7 +231,7 @@ module ActionDispatch
|
|
226
231
|
method = :post
|
227
232
|
end
|
228
233
|
|
229
|
-
if
|
234
|
+
if path.include?("://")
|
230
235
|
path = build_expanded_path(path) do |location|
|
231
236
|
https! URI::HTTPS === location if location.scheme
|
232
237
|
|
@@ -252,10 +257,13 @@ module ActionDispatch
|
|
252
257
|
"REQUEST_URI" => path,
|
253
258
|
"HTTP_HOST" => host,
|
254
259
|
"REMOTE_ADDR" => remote_addr,
|
255
|
-
"CONTENT_TYPE" => request_encoder.content_type,
|
256
260
|
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
257
261
|
}
|
258
262
|
|
263
|
+
if request_encoder.content_type
|
264
|
+
request_env["CONTENT_TYPE"] = request_encoder.content_type
|
265
|
+
end
|
266
|
+
|
259
267
|
wrapped_headers = Http::Headers.from_hash({})
|
260
268
|
wrapped_headers.merge!(headers) if headers
|
261
269
|
|
@@ -274,8 +282,8 @@ module ActionDispatch
|
|
274
282
|
|
275
283
|
session = Rack::Test::Session.new(_mock_session)
|
276
284
|
|
277
|
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
278
|
-
#
|
285
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly Make sure requested
|
286
|
+
# path is always a full URI.
|
279
287
|
session.request(build_full_uri(path, request_env), request_env)
|
280
288
|
|
281
289
|
@request_count += 1
|
@@ -293,7 +301,7 @@ module ActionDispatch
|
|
293
301
|
|
294
302
|
# Set the host name to use in the next request.
|
295
303
|
#
|
296
|
-
#
|
304
|
+
# session.host! "www.example.com"
|
297
305
|
alias :host! :host=
|
298
306
|
|
299
307
|
private
|
@@ -335,16 +343,16 @@ module ActionDispatch
|
|
335
343
|
@integration_session ||= create_session(app)
|
336
344
|
end
|
337
345
|
|
338
|
-
# Reset the current session. This is useful for testing multiple sessions
|
339
|
-
#
|
346
|
+
# Reset the current session. This is useful for testing multiple sessions in a
|
347
|
+
# single test case.
|
340
348
|
def reset!
|
341
349
|
@integration_session = create_session(app)
|
342
350
|
end
|
343
351
|
|
344
352
|
def create_session(app)
|
345
353
|
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
|
346
|
-
# If the app is a Rails app, make url_helpers available on the session.
|
347
|
-
#
|
354
|
+
# If the app is a Rails app, make url_helpers available on the session. This
|
355
|
+
# makes app.url_for and app.foo_path available in the console.
|
348
356
|
if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
|
349
357
|
include app.routes.url_helpers
|
350
358
|
include app.routes.mounted_helpers
|
@@ -374,16 +382,15 @@ module ActionDispatch
|
|
374
382
|
RUBY
|
375
383
|
end
|
376
384
|
|
377
|
-
# Open a new session instance. If a block is given, the new session is
|
378
|
-
#
|
385
|
+
# Open a new session instance. If a block is given, the new session is yielded
|
386
|
+
# to the block before being returned.
|
379
387
|
#
|
380
|
-
#
|
381
|
-
#
|
382
|
-
#
|
388
|
+
# session = open_session do |sess|
|
389
|
+
# sess.extend(CustomAssertions)
|
390
|
+
# end
|
383
391
|
#
|
384
|
-
# By default, a single session is automatically created for you, but you
|
385
|
-
#
|
386
|
-
# simultaneously.
|
392
|
+
# By default, a single session is automatically created for you, but you can use
|
393
|
+
# this method to open multiple sessions that ought to be tested simultaneously.
|
387
394
|
def open_session
|
388
395
|
dup.tap do |session|
|
389
396
|
session.reset!
|
@@ -400,8 +407,8 @@ module ActionDispatch
|
|
400
407
|
root_session ? root_session.assertions = assertions : super
|
401
408
|
end
|
402
409
|
|
403
|
-
# Copy the instance variables from the current session instance into the
|
404
|
-
#
|
410
|
+
# Copy the instance variables from the current session instance into the test
|
411
|
+
# instance.
|
405
412
|
def copy_session_variables! # :nodoc:
|
406
413
|
@controller = @integration_session.controller
|
407
414
|
@response = @integration_session.response
|
@@ -422,211 +429,213 @@ module ActionDispatch
|
|
422
429
|
end
|
423
430
|
|
424
431
|
# Delegate unhandled messages to the current session instance.
|
425
|
-
def method_missing(method,
|
432
|
+
def method_missing(method, ...)
|
426
433
|
if integration_session.respond_to?(method)
|
427
|
-
integration_session.public_send(method,
|
434
|
+
integration_session.public_send(method, ...).tap do
|
428
435
|
copy_session_variables!
|
429
436
|
end
|
430
437
|
else
|
431
438
|
super
|
432
439
|
end
|
433
440
|
end
|
434
|
-
ruby2_keywords(:method_missing)
|
435
441
|
end
|
436
442
|
end
|
437
443
|
|
438
|
-
# An integration test spans multiple controllers and actions,
|
439
|
-
#
|
440
|
-
#
|
441
|
-
#
|
444
|
+
# An integration test spans multiple controllers and actions, tying them all
|
445
|
+
# together to ensure they work together as expected. It tests more completely
|
446
|
+
# than either unit or functional tests do, exercising the entire stack, from the
|
447
|
+
# dispatcher to the database.
|
442
448
|
#
|
443
|
-
# At its simplest, you simply extend
|
444
|
-
# using the get/
|
449
|
+
# At its simplest, you simply extend `IntegrationTest` and write your tests
|
450
|
+
# using the Integration::RequestHelpers#get and/or
|
451
|
+
# Integration::RequestHelpers#post methods:
|
445
452
|
#
|
446
|
-
#
|
453
|
+
# require "test_helper"
|
447
454
|
#
|
448
|
-
#
|
449
|
-
#
|
455
|
+
# class ExampleTest < ActionDispatch::IntegrationTest
|
456
|
+
# fixtures :people
|
450
457
|
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
454
|
-
#
|
458
|
+
# def test_login
|
459
|
+
# # get the login page
|
460
|
+
# get "/login"
|
461
|
+
# assert_equal 200, status
|
455
462
|
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
463
|
+
# # post the login and follow through to the home page
|
464
|
+
# post "/login", params: { username: people(:jamis).username,
|
465
|
+
# password: people(:jamis).password }
|
466
|
+
# follow_redirect!
|
467
|
+
# assert_equal 200, status
|
468
|
+
# assert_equal "/home", path
|
469
|
+
# end
|
462
470
|
# end
|
463
|
-
# end
|
464
|
-
#
|
465
|
-
# However, you can also have multiple session instances open per test, and
|
466
|
-
# even extend those instances with assertions and methods to create a very
|
467
|
-
# powerful testing DSL that is specific for your application. You can even
|
468
|
-
# reference any named routes you happen to have defined.
|
469
471
|
#
|
470
|
-
#
|
472
|
+
# However, you can also have multiple session instances open per test, and even
|
473
|
+
# extend those instances with assertions and methods to create a very powerful
|
474
|
+
# testing DSL that is specific for your application. You can even reference any
|
475
|
+
# named routes you happen to have defined.
|
471
476
|
#
|
472
|
-
#
|
473
|
-
# fixtures :people, :rooms
|
477
|
+
# require "test_helper"
|
474
478
|
#
|
475
|
-
#
|
476
|
-
#
|
477
|
-
# room = rooms(:office)
|
479
|
+
# class AdvancedTest < ActionDispatch::IntegrationTest
|
480
|
+
# fixtures :people, :rooms
|
478
481
|
#
|
479
|
-
#
|
480
|
-
#
|
482
|
+
# def test_login_and_speak
|
483
|
+
# jamis, david = login(:jamis), login(:david)
|
484
|
+
# room = rooms(:office)
|
481
485
|
#
|
482
|
-
#
|
483
|
-
#
|
484
|
-
# end
|
486
|
+
# jamis.enter(room)
|
487
|
+
# jamis.speak(room, "anybody home?")
|
485
488
|
#
|
486
|
-
#
|
489
|
+
# david.enter(room)
|
490
|
+
# david.speak(room, "hello!")
|
491
|
+
# end
|
487
492
|
#
|
488
|
-
#
|
489
|
-
#
|
490
|
-
#
|
491
|
-
#
|
492
|
-
#
|
493
|
-
#
|
493
|
+
# private
|
494
|
+
#
|
495
|
+
# module CustomAssertions
|
496
|
+
# def enter(room)
|
497
|
+
# # reference a named route, for maximum internal consistency!
|
498
|
+
# get(room_url(id: room.id))
|
499
|
+
# assert(...)
|
500
|
+
# ...
|
501
|
+
# end
|
502
|
+
#
|
503
|
+
# def speak(room, message)
|
504
|
+
# post "/say/#{room.id}", xhr: true, params: { message: message }
|
505
|
+
# assert(...)
|
506
|
+
# ...
|
507
|
+
# end
|
494
508
|
# end
|
495
509
|
#
|
496
|
-
# def
|
497
|
-
#
|
498
|
-
#
|
499
|
-
#
|
510
|
+
# def login(who)
|
511
|
+
# open_session do |sess|
|
512
|
+
# sess.extend(CustomAssertions)
|
513
|
+
# who = people(who)
|
514
|
+
# sess.post "/login", params: { username: who.username,
|
515
|
+
# password: who.password }
|
516
|
+
# assert(...)
|
517
|
+
# end
|
500
518
|
# end
|
501
|
-
#
|
502
|
-
#
|
503
|
-
# def login(who)
|
504
|
-
# open_session do |sess|
|
505
|
-
# sess.extend(CustomAssertions)
|
506
|
-
# who = people(who)
|
507
|
-
# sess.post "/login", params: { username: who.username,
|
508
|
-
# password: who.password }
|
509
|
-
# assert(...)
|
510
|
-
# end
|
511
|
-
# end
|
512
|
-
# end
|
519
|
+
# end
|
513
520
|
#
|
514
521
|
# Another longer example would be:
|
515
522
|
#
|
516
523
|
# A simple integration test that exercises multiple controllers:
|
517
524
|
#
|
518
|
-
#
|
525
|
+
# require "test_helper"
|
519
526
|
#
|
520
|
-
#
|
521
|
-
#
|
522
|
-
#
|
523
|
-
#
|
524
|
-
#
|
525
|
-
#
|
527
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
528
|
+
# test "login and browse site" do
|
529
|
+
# # login via https
|
530
|
+
# https!
|
531
|
+
# get "/login"
|
532
|
+
# assert_response :success
|
526
533
|
#
|
527
|
-
#
|
528
|
-
#
|
529
|
-
#
|
530
|
-
#
|
534
|
+
# post "/login", params: { username: users(:david).username, password: users(:david).password }
|
535
|
+
# follow_redirect!
|
536
|
+
# assert_equal '/welcome', path
|
537
|
+
# assert_equal 'Welcome david!', flash[:notice]
|
531
538
|
#
|
532
|
-
#
|
533
|
-
#
|
534
|
-
#
|
535
|
-
#
|
539
|
+
# https!(false)
|
540
|
+
# get "/articles/all"
|
541
|
+
# assert_response :success
|
542
|
+
# assert_select 'h1', 'Articles'
|
543
|
+
# end
|
536
544
|
# end
|
537
|
-
# end
|
538
545
|
#
|
539
546
|
# As you can see the integration test involves multiple controllers and
|
540
547
|
# exercises the entire stack from database to dispatcher. In addition you can
|
541
|
-
# have multiple session instances open simultaneously in a test and extend
|
542
|
-
#
|
543
|
-
#
|
548
|
+
# have multiple session instances open simultaneously in a test and extend those
|
549
|
+
# instances with assertion methods to create a very powerful testing DSL
|
550
|
+
# (domain-specific language) just for your application.
|
544
551
|
#
|
545
552
|
# Here's an example of multiple sessions and custom DSL in an integration test
|
546
553
|
#
|
547
|
-
#
|
554
|
+
# require "test_helper"
|
548
555
|
#
|
549
|
-
#
|
550
|
-
#
|
551
|
-
#
|
552
|
-
#
|
553
|
-
#
|
554
|
-
#
|
556
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
557
|
+
# test "login and browse site" do
|
558
|
+
# # User david logs in
|
559
|
+
# david = login(:david)
|
560
|
+
# # User guest logs in
|
561
|
+
# guest = login(:guest)
|
555
562
|
#
|
556
|
-
#
|
557
|
-
#
|
558
|
-
#
|
563
|
+
# # Both are now available in different sessions
|
564
|
+
# assert_equal 'Welcome david!', david.flash[:notice]
|
565
|
+
# assert_equal 'Welcome guest!', guest.flash[:notice]
|
559
566
|
#
|
560
|
-
#
|
561
|
-
#
|
562
|
-
#
|
563
|
-
#
|
567
|
+
# # User david can browse site
|
568
|
+
# david.browses_site
|
569
|
+
# # User guest can browse site as well
|
570
|
+
# guest.browses_site
|
564
571
|
#
|
565
|
-
#
|
566
|
-
#
|
572
|
+
# # Continue with other assertions
|
573
|
+
# end
|
567
574
|
#
|
568
|
-
#
|
575
|
+
# private
|
569
576
|
#
|
570
|
-
#
|
571
|
-
#
|
572
|
-
#
|
573
|
-
#
|
574
|
-
#
|
577
|
+
# module CustomDsl
|
578
|
+
# def browses_site
|
579
|
+
# get "/products/all"
|
580
|
+
# assert_response :success
|
581
|
+
# assert_select 'h1', 'Products'
|
582
|
+
# end
|
575
583
|
# end
|
576
|
-
# end
|
577
584
|
#
|
578
|
-
#
|
579
|
-
#
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
583
|
-
#
|
584
|
-
#
|
585
|
-
#
|
585
|
+
# def login(user)
|
586
|
+
# open_session do |sess|
|
587
|
+
# sess.extend(CustomDsl)
|
588
|
+
# u = users(user)
|
589
|
+
# sess.https!
|
590
|
+
# sess.post "/login", params: { username: u.username, password: u.password }
|
591
|
+
# assert_equal '/welcome', sess.path
|
592
|
+
# sess.https!(false)
|
593
|
+
# end
|
586
594
|
# end
|
587
|
-
#
|
588
|
-
# end
|
595
|
+
# end
|
589
596
|
#
|
590
|
-
# See the
|
591
|
-
#
|
597
|
+
# See the [request helpers documentation]
|
598
|
+
# (rdoc-ref:ActionDispatch::Integration::RequestHelpers) for help
|
599
|
+
# on how to use `get`, etc.
|
592
600
|
#
|
593
|
-
#
|
601
|
+
# ### Changing the request encoding
|
594
602
|
#
|
595
|
-
# You can also test your JSON API easily by setting what the request should
|
596
|
-
#
|
603
|
+
# You can also test your JSON API easily by setting what the request should be
|
604
|
+
# encoded as:
|
597
605
|
#
|
598
|
-
#
|
606
|
+
# require "test_helper"
|
599
607
|
#
|
600
|
-
#
|
601
|
-
#
|
602
|
-
#
|
603
|
-
#
|
604
|
-
#
|
608
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
609
|
+
# test "creates articles" do
|
610
|
+
# assert_difference -> { Article.count } do
|
611
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
612
|
+
# end
|
605
613
|
#
|
606
|
-
#
|
607
|
-
#
|
614
|
+
# assert_response :success
|
615
|
+
# assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
|
616
|
+
# end
|
608
617
|
# end
|
609
|
-
# end
|
610
618
|
#
|
611
|
-
# The
|
619
|
+
# The `as` option passes an "application/json" Accept header (thereby setting
|
612
620
|
# the request format to JSON unless overridden), sets the content type to
|
613
621
|
# "application/json" and encodes the parameters as JSON.
|
614
622
|
#
|
615
|
-
# Calling
|
616
|
-
# last response MIME type.
|
623
|
+
# Calling TestResponse#parsed_body on the response parses the response body
|
624
|
+
# based on the last response MIME type.
|
617
625
|
#
|
618
|
-
# Out of the box, only
|
619
|
-
#
|
626
|
+
# Out of the box, only `:json` is supported. But for any custom MIME types
|
627
|
+
# you've registered, you can add your own encoders with:
|
620
628
|
#
|
621
|
-
#
|
622
|
-
#
|
623
|
-
#
|
629
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
630
|
+
# param_encoder: -> params { params.to_wibble },
|
631
|
+
# response_parser: -> body { body }
|
624
632
|
#
|
625
|
-
# Where
|
626
|
-
#
|
627
|
-
#
|
633
|
+
# Where `param_encoder` defines how the params should be encoded and
|
634
|
+
# `response_parser` defines how the response body should be parsed through
|
635
|
+
# TestResponse#parsed_body.
|
628
636
|
#
|
629
|
-
# Consult the Rails Testing Guide
|
637
|
+
# Consult the [Rails Testing Guide](https://guides.rubyonrails.org/testing.html)
|
638
|
+
# for more.
|
630
639
|
|
631
640
|
class IntegrationTest < ActiveSupport::TestCase
|
632
641
|
include TestProcess::FixtureFile
|
@@ -643,10 +652,12 @@ module ActionDispatch
|
|
643
652
|
|
644
653
|
include Integration::Runner
|
645
654
|
include ActionController::TemplateAssertions
|
655
|
+
include TestHelpers::PageDumpHelper
|
646
656
|
|
647
657
|
included do
|
648
658
|
include ActionDispatch::Routing::UrlFor
|
649
659
|
include UrlOptions # don't let UrlFor override the url_options method
|
660
|
+
include ActionDispatch::Assertions::RoutingAssertions::WithIntegrationRouting
|
650
661
|
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
|
651
662
|
@@app = nil
|
652
663
|
end
|