actionpack 7.1.5.1 → 8.1.2
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 +308 -523
- data/README.rdoc +1 -1
- data/lib/abstract_controller/asset_paths.rb +6 -2
- data/lib/abstract_controller/base.rb +104 -105
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +8 -3
- data/lib/abstract_controller/callbacks.rb +70 -62
- data/lib/abstract_controller/collector.rb +7 -7
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +71 -84
- data/lib/abstract_controller/logger.rb +4 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -13
- data/lib/abstract_controller/translation.rb +12 -13
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +76 -72
- data/lib/action_controller/base.rb +199 -126
- data/lib/action_controller/caching.rb +16 -14
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +21 -18
- data/lib/action_controller/log_subscriber.rb +23 -2
- data/lib/action_controller/metal/allow_browser.rb +133 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +217 -175
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +72 -63
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +16 -9
- data/lib/action_controller/metal/flash.rb +13 -14
- data/lib/action_controller/metal/head.rb +15 -11
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +209 -201
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +16 -14
- data/lib/action_controller/metal/live.rb +177 -128
- 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 +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +22 -12
- data/lib/action_controller/metal/rate_limiting.rb +92 -0
- data/lib/action_controller/metal/redirecting.rb +213 -94
- data/lib/action_controller/metal/renderers.rb +78 -57
- data/lib/action_controller/metal/rendering.rb +111 -77
- data/lib/action_controller/metal/request_forgery_protection.rb +182 -143
- data/lib/action_controller/metal/rescue.rb +20 -9
- data/lib/action_controller/metal/streaming.rb +118 -195
- data/lib/action_controller/metal/strong_parameters.rb +720 -530
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +86 -60
- data/lib/action_controller/railtie.rb +36 -15
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +41 -36
- data/lib/action_controller/structured_event_subscriber.rb +116 -0
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +160 -131
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/constants.rb +8 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +163 -35
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +54 -39
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +89 -41
- data/lib/action_dispatch/http/mime_type.rb +25 -21
- data/lib/action_dispatch/http/mime_types.rb +3 -0
- data/lib/action_dispatch/http/param_builder.rb +187 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/parameters.rb +14 -12
- data/lib/action_dispatch/http/permissions_policy.rb +25 -36
- data/lib/action_dispatch/http/query_parser.rb +55 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +141 -92
- data/lib/action_dispatch/http/response.rb +137 -77
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +187 -89
- data/lib/action_dispatch/journey/formatter.rb +21 -9
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +34 -11
- data/lib/action_dispatch/journey/gtg/transition_table.rb +47 -53
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +8 -6
- data/lib/action_dispatch/journey/parser.rb +99 -195
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +54 -38
- data/lib/action_dispatch/journey/router/utils.rb +22 -27
- data/lib/action_dispatch/journey/router.rb +63 -83
- data/lib/action_dispatch/journey/routes.rb +11 -2
- data/lib/action_dispatch/journey/scanner.rb +46 -42
- data/lib/action_dispatch/journey/visitors.rb +57 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +7 -1
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +125 -106
- data/lib/action_dispatch/middleware/debug_exceptions.rb +37 -8
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +13 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +18 -23
- data/lib/action_dispatch/middleware/executor.rb +19 -4
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +14 -12
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +87 -77
- data/lib/action_dispatch/middleware/request_id.rb +16 -10
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +30 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
- data/lib/action_dispatch/middleware/ssl.rb +53 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +23 -3
- data/lib/action_dispatch/request/session.rb +24 -21
- data/lib/action_dispatch/request/utils.rb +11 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +85 -60
- data/lib/action_dispatch/routing/mapper.rb +1031 -851
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +47 -39
- data/lib/action_dispatch/routing/route_set.rb +79 -56
- data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +16 -23
- data/lib/action_dispatch/system_testing/driver.rb +2 -0
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +34 -23
- 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 +52 -25
- data/lib/action_dispatch/testing/assertions/routing.rb +168 -87
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +233 -223
- data/lib/action_dispatch/testing/request_encoder.rb +11 -9
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +11 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +36 -32
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +36 -32
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -31
|
@@ -1,63 +1,65 @@
|
|
|
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
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
|
-
# underlying request.
|
|
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.
|
|
61
63
|
#
|
|
62
64
|
# The HTTP_REFERER header will be set to the previous url.
|
|
63
65
|
def follow_redirect!(headers: {}, **args)
|
|
@@ -79,13 +81,13 @@ module ActionDispatch
|
|
|
79
81
|
end
|
|
80
82
|
end
|
|
81
83
|
|
|
82
|
-
# An instance of this class represents a set of requests and responses
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
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.
|
|
86
88
|
#
|
|
87
89
|
# Typically, you will instantiate a new session using Runner#open_session,
|
|
88
|
-
# rather than instantiating a
|
|
90
|
+
# rather than instantiating a Session directly.
|
|
89
91
|
class Session
|
|
90
92
|
DEFAULT_HOST = "www.example.com"
|
|
91
93
|
|
|
@@ -107,8 +109,8 @@ module ActionDispatch
|
|
|
107
109
|
# The Accept header to send.
|
|
108
110
|
attr_accessor :accept
|
|
109
111
|
|
|
110
|
-
# A map of the cookies returned by the last response, and which will be
|
|
111
|
-
#
|
|
112
|
+
# A map of the cookies returned by the last response, and which will be sent
|
|
113
|
+
# with the next request.
|
|
112
114
|
def cookies
|
|
113
115
|
_mock_session.cookie_jar
|
|
114
116
|
end
|
|
@@ -127,7 +129,7 @@ module ActionDispatch
|
|
|
127
129
|
|
|
128
130
|
include ActionDispatch::Routing::UrlFor
|
|
129
131
|
|
|
130
|
-
# Create and initialize a new
|
|
132
|
+
# Create and initialize a new Session instance.
|
|
131
133
|
def initialize(app)
|
|
132
134
|
super()
|
|
133
135
|
@app = app
|
|
@@ -147,11 +149,10 @@ module ActionDispatch
|
|
|
147
149
|
end
|
|
148
150
|
end
|
|
149
151
|
|
|
150
|
-
# Resets the instance. This can be used to reset the state information
|
|
151
|
-
#
|
|
152
|
-
# 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.
|
|
153
154
|
#
|
|
154
|
-
#
|
|
155
|
+
# session.reset!
|
|
155
156
|
def reset!
|
|
156
157
|
@https = false
|
|
157
158
|
@controller = @request = @response = nil
|
|
@@ -166,63 +167,61 @@ module ActionDispatch
|
|
|
166
167
|
"*/*;q=0.5"
|
|
167
168
|
|
|
168
169
|
unless defined? @named_routes_configured
|
|
169
|
-
# the helpers are made protected by default--we make them public for
|
|
170
|
-
#
|
|
170
|
+
# the helpers are made protected by default--we make them public for easier
|
|
171
|
+
# access during testing and troubleshooting.
|
|
171
172
|
@named_routes_configured = true
|
|
172
173
|
end
|
|
173
174
|
end
|
|
174
175
|
|
|
175
176
|
# Specify whether or not the session should mimic a secure HTTPS request.
|
|
176
177
|
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
178
|
+
# session.https!
|
|
179
|
+
# session.https!(false)
|
|
179
180
|
def https!(flag = true)
|
|
180
181
|
@https = flag
|
|
181
182
|
end
|
|
182
183
|
|
|
183
|
-
# Returns
|
|
184
|
+
# Returns `true` if the session is mimicking a secure HTTPS request.
|
|
184
185
|
#
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
186
|
+
# if session.https?
|
|
187
|
+
# ...
|
|
188
|
+
# end
|
|
188
189
|
def https?
|
|
189
190
|
@https
|
|
190
191
|
end
|
|
191
192
|
|
|
192
193
|
# Performs the actual request.
|
|
193
194
|
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
#
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
# Supports +:json+ by default and will set the appropriate request headers.
|
|
212
|
-
# 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.
|
|
211
|
+
#
|
|
213
212
|
#
|
|
214
213
|
# This method is rarely used directly. Use RequestHelpers#get,
|
|
215
|
-
# RequestHelpers#post, or other standard HTTP methods in integration
|
|
216
|
-
#
|
|
217
|
-
#
|
|
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.
|
|
218
217
|
#
|
|
219
218
|
# This method returns the response status, after performing the request.
|
|
220
|
-
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest
|
|
221
|
-
# then that object's
|
|
222
|
-
# 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.
|
|
223
222
|
#
|
|
224
223
|
# Example:
|
|
225
|
-
#
|
|
224
|
+
# process :get, '/author', params: { since: 201501011400 }
|
|
226
225
|
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
|
227
226
|
request_encoder = RequestEncoder.encoder(as)
|
|
228
227
|
headers ||= {}
|
|
@@ -283,9 +282,19 @@ module ActionDispatch
|
|
|
283
282
|
|
|
284
283
|
session = Rack::Test::Session.new(_mock_session)
|
|
285
284
|
|
|
286
|
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
|
287
|
-
#
|
|
288
|
-
|
|
285
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly Make sure requested
|
|
286
|
+
# path is always a full URI.
|
|
287
|
+
uri = build_full_uri(path, request_env)
|
|
288
|
+
|
|
289
|
+
if method == :get && String === request_env[:params]
|
|
290
|
+
# rack-test will needlessly parse and rebuild a :params
|
|
291
|
+
# querystring, using Rack's query parser. At best that's a
|
|
292
|
+
# waste of time; at worst it can change the value.
|
|
293
|
+
|
|
294
|
+
uri << "?" << request_env.delete(:params)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
session.request(uri, request_env)
|
|
289
298
|
|
|
290
299
|
@request_count += 1
|
|
291
300
|
@request = ActionDispatch::Request.new(session.last_request.env)
|
|
@@ -302,7 +311,7 @@ module ActionDispatch
|
|
|
302
311
|
|
|
303
312
|
# Set the host name to use in the next request.
|
|
304
313
|
#
|
|
305
|
-
#
|
|
314
|
+
# session.host! "www.example.com"
|
|
306
315
|
alias :host! :host=
|
|
307
316
|
|
|
308
317
|
private
|
|
@@ -344,16 +353,16 @@ module ActionDispatch
|
|
|
344
353
|
@integration_session ||= create_session(app)
|
|
345
354
|
end
|
|
346
355
|
|
|
347
|
-
# Reset the current session. This is useful for testing multiple sessions
|
|
348
|
-
#
|
|
356
|
+
# Reset the current session. This is useful for testing multiple sessions in a
|
|
357
|
+
# single test case.
|
|
349
358
|
def reset!
|
|
350
359
|
@integration_session = create_session(app)
|
|
351
360
|
end
|
|
352
361
|
|
|
353
362
|
def create_session(app)
|
|
354
363
|
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
|
|
355
|
-
# If the app is a Rails app, make url_helpers available on the session.
|
|
356
|
-
#
|
|
364
|
+
# If the app is a Rails app, make url_helpers available on the session. This
|
|
365
|
+
# makes app.url_for and app.foo_path available in the console.
|
|
357
366
|
if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
|
|
358
367
|
include app.routes.url_helpers
|
|
359
368
|
include app.routes.mounted_helpers
|
|
@@ -383,16 +392,15 @@ module ActionDispatch
|
|
|
383
392
|
RUBY
|
|
384
393
|
end
|
|
385
394
|
|
|
386
|
-
# Open a new session instance. If a block is given, the new session is
|
|
387
|
-
#
|
|
395
|
+
# Open a new session instance. If a block is given, the new session is yielded
|
|
396
|
+
# to the block before being returned.
|
|
388
397
|
#
|
|
389
|
-
#
|
|
390
|
-
#
|
|
391
|
-
#
|
|
398
|
+
# session = open_session do |sess|
|
|
399
|
+
# sess.extend(CustomAssertions)
|
|
400
|
+
# end
|
|
392
401
|
#
|
|
393
|
-
# By default, a single session is automatically created for you, but you
|
|
394
|
-
#
|
|
395
|
-
# simultaneously.
|
|
402
|
+
# By default, a single session is automatically created for you, but you can use
|
|
403
|
+
# this method to open multiple sessions that ought to be tested simultaneously.
|
|
396
404
|
def open_session
|
|
397
405
|
dup.tap do |session|
|
|
398
406
|
session.reset!
|
|
@@ -409,8 +417,8 @@ module ActionDispatch
|
|
|
409
417
|
root_session ? root_session.assertions = assertions : super
|
|
410
418
|
end
|
|
411
419
|
|
|
412
|
-
# Copy the instance variables from the current session instance into the
|
|
413
|
-
#
|
|
420
|
+
# Copy the instance variables from the current session instance into the test
|
|
421
|
+
# instance.
|
|
414
422
|
def copy_session_variables! # :nodoc:
|
|
415
423
|
@controller = @integration_session.controller
|
|
416
424
|
@response = @integration_session.response
|
|
@@ -431,212 +439,212 @@ module ActionDispatch
|
|
|
431
439
|
end
|
|
432
440
|
|
|
433
441
|
# Delegate unhandled messages to the current session instance.
|
|
434
|
-
def method_missing(method,
|
|
442
|
+
def method_missing(method, ...)
|
|
435
443
|
if integration_session.respond_to?(method)
|
|
436
|
-
integration_session.public_send(method,
|
|
444
|
+
integration_session.public_send(method, ...).tap do
|
|
437
445
|
copy_session_variables!
|
|
438
446
|
end
|
|
439
447
|
else
|
|
440
448
|
super
|
|
441
449
|
end
|
|
442
450
|
end
|
|
443
|
-
ruby2_keywords(:method_missing)
|
|
444
451
|
end
|
|
445
452
|
end
|
|
446
453
|
|
|
447
|
-
# An integration test spans multiple controllers and actions,
|
|
448
|
-
#
|
|
449
|
-
#
|
|
450
|
-
#
|
|
454
|
+
# An integration test spans multiple controllers and actions, tying them all
|
|
455
|
+
# together to ensure they work together as expected. It tests more completely
|
|
456
|
+
# than either unit or functional tests do, exercising the entire stack, from the
|
|
457
|
+
# dispatcher to the database.
|
|
451
458
|
#
|
|
452
|
-
# At its simplest, you simply extend
|
|
453
|
-
#
|
|
459
|
+
# At its simplest, you simply extend `IntegrationTest` and write your tests
|
|
460
|
+
# using the Integration::RequestHelpers#get and/or
|
|
454
461
|
# Integration::RequestHelpers#post methods:
|
|
455
462
|
#
|
|
456
|
-
#
|
|
463
|
+
# require "test_helper"
|
|
457
464
|
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
465
|
+
# class ExampleTest < ActionDispatch::IntegrationTest
|
|
466
|
+
# fixtures :people
|
|
460
467
|
#
|
|
461
|
-
#
|
|
462
|
-
#
|
|
463
|
-
#
|
|
464
|
-
#
|
|
468
|
+
# def test_login
|
|
469
|
+
# # get the login page
|
|
470
|
+
# get "/login"
|
|
471
|
+
# assert_equal 200, status
|
|
465
472
|
#
|
|
466
|
-
#
|
|
467
|
-
#
|
|
468
|
-
#
|
|
469
|
-
#
|
|
470
|
-
#
|
|
471
|
-
#
|
|
473
|
+
# # post the login and follow through to the home page
|
|
474
|
+
# post "/login", params: { username: people(:jamis).username,
|
|
475
|
+
# password: people(:jamis).password }
|
|
476
|
+
# follow_redirect!
|
|
477
|
+
# assert_equal 200, status
|
|
478
|
+
# assert_equal "/home", path
|
|
479
|
+
# end
|
|
472
480
|
# end
|
|
473
|
-
# end
|
|
474
|
-
#
|
|
475
|
-
# However, you can also have multiple session instances open per test, and
|
|
476
|
-
# even extend those instances with assertions and methods to create a very
|
|
477
|
-
# powerful testing DSL that is specific for your application. You can even
|
|
478
|
-
# reference any named routes you happen to have defined.
|
|
479
|
-
#
|
|
480
|
-
# require "test_helper"
|
|
481
481
|
#
|
|
482
|
-
#
|
|
483
|
-
#
|
|
482
|
+
# However, you can also have multiple session instances open per test, and even
|
|
483
|
+
# extend those instances with assertions and methods to create a very powerful
|
|
484
|
+
# testing DSL that is specific for your application. You can even reference any
|
|
485
|
+
# named routes you happen to have defined.
|
|
484
486
|
#
|
|
485
|
-
#
|
|
486
|
-
# jamis, david = login(:jamis), login(:david)
|
|
487
|
-
# room = rooms(:office)
|
|
487
|
+
# require "test_helper"
|
|
488
488
|
#
|
|
489
|
-
#
|
|
490
|
-
#
|
|
489
|
+
# class AdvancedTest < ActionDispatch::IntegrationTest
|
|
490
|
+
# fixtures :people, :rooms
|
|
491
491
|
#
|
|
492
|
-
#
|
|
493
|
-
#
|
|
494
|
-
#
|
|
492
|
+
# def test_login_and_speak
|
|
493
|
+
# jamis, david = login(:jamis), login(:david)
|
|
494
|
+
# room = rooms(:office)
|
|
495
495
|
#
|
|
496
|
-
#
|
|
496
|
+
# jamis.enter(room)
|
|
497
|
+
# jamis.speak(room, "anybody home?")
|
|
497
498
|
#
|
|
498
|
-
#
|
|
499
|
-
#
|
|
500
|
-
#
|
|
501
|
-
# get(room_url(id: room.id))
|
|
502
|
-
# assert(...)
|
|
503
|
-
# ...
|
|
504
|
-
# end
|
|
499
|
+
# david.enter(room)
|
|
500
|
+
# david.speak(room, "hello!")
|
|
501
|
+
# end
|
|
505
502
|
#
|
|
506
|
-
#
|
|
507
|
-
#
|
|
508
|
-
#
|
|
509
|
-
#
|
|
503
|
+
# private
|
|
504
|
+
#
|
|
505
|
+
# module CustomAssertions
|
|
506
|
+
# def enter(room)
|
|
507
|
+
# # reference a named route, for maximum internal consistency!
|
|
508
|
+
# get(room_url(id: room.id))
|
|
509
|
+
# assert(...)
|
|
510
|
+
# ...
|
|
511
|
+
# end
|
|
512
|
+
#
|
|
513
|
+
# def speak(room, message)
|
|
514
|
+
# post "/say/#{room.id}", xhr: true, params: { message: message }
|
|
515
|
+
# assert(...)
|
|
516
|
+
# ...
|
|
517
|
+
# end
|
|
510
518
|
# end
|
|
511
|
-
# end
|
|
512
519
|
#
|
|
513
|
-
#
|
|
514
|
-
#
|
|
515
|
-
#
|
|
516
|
-
#
|
|
517
|
-
#
|
|
518
|
-
#
|
|
519
|
-
#
|
|
520
|
+
# def login(who)
|
|
521
|
+
# open_session do |sess|
|
|
522
|
+
# sess.extend(CustomAssertions)
|
|
523
|
+
# who = people(who)
|
|
524
|
+
# sess.post "/login", params: { username: who.username,
|
|
525
|
+
# password: who.password }
|
|
526
|
+
# assert(...)
|
|
527
|
+
# end
|
|
520
528
|
# end
|
|
521
|
-
#
|
|
522
|
-
# end
|
|
529
|
+
# end
|
|
523
530
|
#
|
|
524
531
|
# Another longer example would be:
|
|
525
532
|
#
|
|
526
533
|
# A simple integration test that exercises multiple controllers:
|
|
527
534
|
#
|
|
528
|
-
#
|
|
535
|
+
# require "test_helper"
|
|
529
536
|
#
|
|
530
|
-
#
|
|
531
|
-
#
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
#
|
|
537
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
538
|
+
# test "login and browse site" do
|
|
539
|
+
# # login via https
|
|
540
|
+
# https!
|
|
541
|
+
# get "/login"
|
|
542
|
+
# assert_response :success
|
|
536
543
|
#
|
|
537
|
-
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
540
|
-
#
|
|
544
|
+
# post "/login", params: { username: users(:david).username, password: users(:david).password }
|
|
545
|
+
# follow_redirect!
|
|
546
|
+
# assert_equal '/welcome', path
|
|
547
|
+
# assert_equal 'Welcome david!', flash[:notice]
|
|
541
548
|
#
|
|
542
|
-
#
|
|
543
|
-
#
|
|
544
|
-
#
|
|
545
|
-
#
|
|
549
|
+
# https!(false)
|
|
550
|
+
# get "/articles/all"
|
|
551
|
+
# assert_response :success
|
|
552
|
+
# assert_dom 'h1', 'Articles'
|
|
553
|
+
# end
|
|
546
554
|
# end
|
|
547
|
-
# end
|
|
548
555
|
#
|
|
549
556
|
# As you can see the integration test involves multiple controllers and
|
|
550
557
|
# exercises the entire stack from database to dispatcher. In addition you can
|
|
551
|
-
# have multiple session instances open simultaneously in a test and extend
|
|
552
|
-
#
|
|
553
|
-
#
|
|
558
|
+
# have multiple session instances open simultaneously in a test and extend those
|
|
559
|
+
# instances with assertion methods to create a very powerful testing DSL
|
|
560
|
+
# (domain-specific language) just for your application.
|
|
554
561
|
#
|
|
555
562
|
# Here's an example of multiple sessions and custom DSL in an integration test
|
|
556
563
|
#
|
|
557
|
-
#
|
|
564
|
+
# require "test_helper"
|
|
558
565
|
#
|
|
559
|
-
#
|
|
560
|
-
#
|
|
561
|
-
#
|
|
562
|
-
#
|
|
563
|
-
#
|
|
564
|
-
#
|
|
566
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
567
|
+
# test "login and browse site" do
|
|
568
|
+
# # User david logs in
|
|
569
|
+
# david = login(:david)
|
|
570
|
+
# # User guest logs in
|
|
571
|
+
# guest = login(:guest)
|
|
565
572
|
#
|
|
566
|
-
#
|
|
567
|
-
#
|
|
568
|
-
#
|
|
573
|
+
# # Both are now available in different sessions
|
|
574
|
+
# assert_equal 'Welcome david!', david.flash[:notice]
|
|
575
|
+
# assert_equal 'Welcome guest!', guest.flash[:notice]
|
|
569
576
|
#
|
|
570
|
-
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
573
|
-
#
|
|
577
|
+
# # User david can browse site
|
|
578
|
+
# david.browses_site
|
|
579
|
+
# # User guest can browse site as well
|
|
580
|
+
# guest.browses_site
|
|
574
581
|
#
|
|
575
|
-
#
|
|
576
|
-
#
|
|
582
|
+
# # Continue with other assertions
|
|
583
|
+
# end
|
|
577
584
|
#
|
|
578
|
-
#
|
|
585
|
+
# private
|
|
579
586
|
#
|
|
580
|
-
#
|
|
581
|
-
#
|
|
582
|
-
#
|
|
583
|
-
#
|
|
584
|
-
#
|
|
587
|
+
# module CustomDsl
|
|
588
|
+
# def browses_site
|
|
589
|
+
# get "/products/all"
|
|
590
|
+
# assert_response :success
|
|
591
|
+
# assert_dom 'h1', 'Products'
|
|
592
|
+
# end
|
|
585
593
|
# end
|
|
586
|
-
# end
|
|
587
594
|
#
|
|
588
|
-
#
|
|
589
|
-
#
|
|
590
|
-
#
|
|
591
|
-
#
|
|
592
|
-
#
|
|
593
|
-
#
|
|
594
|
-
#
|
|
595
|
-
#
|
|
595
|
+
# def login(user)
|
|
596
|
+
# open_session do |sess|
|
|
597
|
+
# sess.extend(CustomDsl)
|
|
598
|
+
# u = users(user)
|
|
599
|
+
# sess.https!
|
|
600
|
+
# sess.post "/login", params: { username: u.username, password: u.password }
|
|
601
|
+
# assert_equal '/welcome', sess.path
|
|
602
|
+
# sess.https!(false)
|
|
603
|
+
# end
|
|
596
604
|
# end
|
|
597
|
-
#
|
|
598
|
-
# end
|
|
605
|
+
# end
|
|
599
606
|
#
|
|
600
|
-
# See the
|
|
601
|
-
# use
|
|
607
|
+
# See the [request helpers documentation](rdoc-ref:ActionDispatch::Integration::RequestHelpers)
|
|
608
|
+
# for help on how to use `get`, etc.
|
|
602
609
|
#
|
|
603
|
-
#
|
|
610
|
+
# ### Changing the request encoding
|
|
604
611
|
#
|
|
605
|
-
# You can also test your JSON API easily by setting what the request should
|
|
606
|
-
#
|
|
612
|
+
# You can also test your JSON API easily by setting what the request should be
|
|
613
|
+
# encoded as:
|
|
607
614
|
#
|
|
608
|
-
#
|
|
615
|
+
# require "test_helper"
|
|
609
616
|
#
|
|
610
|
-
#
|
|
611
|
-
#
|
|
612
|
-
#
|
|
613
|
-
#
|
|
614
|
-
#
|
|
617
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
|
618
|
+
# test "creates articles" do
|
|
619
|
+
# assert_difference -> { Article.count } do
|
|
620
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
|
621
|
+
# end
|
|
615
622
|
#
|
|
616
|
-
#
|
|
617
|
-
#
|
|
623
|
+
# assert_response :success
|
|
624
|
+
# assert_equal({ "id" => Article.last.id, "title" => "Ahoy!" }, response.parsed_body)
|
|
625
|
+
# end
|
|
618
626
|
# end
|
|
619
|
-
# end
|
|
620
627
|
#
|
|
621
|
-
# The
|
|
628
|
+
# The `as` option passes an "application/json" Accept header (thereby setting
|
|
622
629
|
# the request format to JSON unless overridden), sets the content type to
|
|
623
630
|
# "application/json" and encodes the parameters as JSON.
|
|
624
631
|
#
|
|
625
|
-
# Calling TestResponse#parsed_body on the response parses the response body
|
|
626
|
-
# last response MIME type.
|
|
632
|
+
# Calling TestResponse#parsed_body on the response parses the response body
|
|
633
|
+
# based on the last response MIME type.
|
|
627
634
|
#
|
|
628
|
-
# Out of the box, only
|
|
629
|
-
#
|
|
635
|
+
# Out of the box, only `:json` is supported. But for any custom MIME types
|
|
636
|
+
# you've registered, you can add your own encoders with:
|
|
630
637
|
#
|
|
631
|
-
#
|
|
632
|
-
#
|
|
633
|
-
#
|
|
638
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
|
639
|
+
# param_encoder: -> params { params.to_wibble },
|
|
640
|
+
# response_parser: -> body { body }
|
|
634
641
|
#
|
|
635
|
-
# Where
|
|
636
|
-
#
|
|
642
|
+
# Where `param_encoder` defines how the params should be encoded and
|
|
643
|
+
# `response_parser` defines how the response body should be parsed through
|
|
637
644
|
# TestResponse#parsed_body.
|
|
638
645
|
#
|
|
639
|
-
# Consult the
|
|
646
|
+
# Consult the [Rails Testing Guide](https://guides.rubyonrails.org/testing.html)
|
|
647
|
+
# for more.
|
|
640
648
|
|
|
641
649
|
class IntegrationTest < ActiveSupport::TestCase
|
|
642
650
|
include TestProcess::FixtureFile
|
|
@@ -653,10 +661,12 @@ module ActionDispatch
|
|
|
653
661
|
|
|
654
662
|
include Integration::Runner
|
|
655
663
|
include ActionController::TemplateAssertions
|
|
664
|
+
include TestHelpers::PageDumpHelper
|
|
656
665
|
|
|
657
666
|
included do
|
|
658
667
|
include ActionDispatch::Routing::UrlFor
|
|
659
668
|
include UrlOptions # don't let UrlFor override the url_options method
|
|
669
|
+
include ActionDispatch::Assertions::RoutingAssertions::WithIntegrationRouting
|
|
660
670
|
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
|
|
661
671
|
@@app = nil
|
|
662
672
|
end
|