actionpack 4.2.11.1 → 6.1.3.2
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 +4 -4
- data/CHANGELOG.md +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,128 +1,75 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "uri"
|
5
|
+
require "rack/test"
|
6
|
+
require "minitest"
|
7
|
+
|
8
|
+
require "action_dispatch/testing/request_encoder"
|
7
9
|
|
8
10
|
module ActionDispatch
|
9
11
|
module Integration #:nodoc:
|
10
12
|
module RequestHelpers
|
11
|
-
# Performs a GET request with the given parameters.
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
# - +parameters+: The HTTP parameters that you want to pass. This may
|
16
|
-
# be +nil+,
|
17
|
-
# a Hash, or a String that is appropriately encoded
|
18
|
-
# (<tt>application/x-www-form-urlencoded</tt> or
|
19
|
-
# <tt>multipart/form-data</tt>).
|
20
|
-
# - +headers_or_env+: Additional headers to pass, as a Hash. The headers will be
|
21
|
-
# merged into the Rack env hash.
|
22
|
-
#
|
23
|
-
# This method returns a Response object, which one can use to
|
24
|
-
# inspect the details of the response. Furthermore, if this method was
|
25
|
-
# called from an ActionDispatch::IntegrationTest object, then that
|
26
|
-
# object's <tt>@response</tt> instance variable will point to the same
|
27
|
-
# response object.
|
28
|
-
#
|
29
|
-
# You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
|
30
|
-
# +#post+, +#patch+, +#put+, +#delete+, and +#head+.
|
31
|
-
def get(path, parameters = nil, headers_or_env = nil)
|
32
|
-
process :get, path, parameters, headers_or_env
|
13
|
+
# Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
|
14
|
+
# for more details.
|
15
|
+
def get(path, **args)
|
16
|
+
process(:get, path, **args)
|
33
17
|
end
|
34
18
|
|
35
|
-
# Performs a POST request with the given parameters. See
|
36
|
-
# details.
|
37
|
-
def post(path,
|
38
|
-
process
|
19
|
+
# Performs a POST request with the given parameters. See ActionDispatch::Integration::Session#process
|
20
|
+
# for more details.
|
21
|
+
def post(path, **args)
|
22
|
+
process(:post, path, **args)
|
39
23
|
end
|
40
24
|
|
41
|
-
# Performs a PATCH request with the given parameters. See
|
42
|
-
# details.
|
43
|
-
def patch(path,
|
44
|
-
process
|
25
|
+
# Performs a PATCH request with the given parameters. See ActionDispatch::Integration::Session#process
|
26
|
+
# for more details.
|
27
|
+
def patch(path, **args)
|
28
|
+
process(:patch, path, **args)
|
45
29
|
end
|
46
30
|
|
47
|
-
# Performs a PUT request with the given parameters. See
|
48
|
-
# details.
|
49
|
-
def put(path,
|
50
|
-
process
|
31
|
+
# Performs a PUT request with the given parameters. See ActionDispatch::Integration::Session#process
|
32
|
+
# for more details.
|
33
|
+
def put(path, **args)
|
34
|
+
process(:put, path, **args)
|
51
35
|
end
|
52
36
|
|
53
|
-
# Performs a DELETE request with the given parameters. See
|
54
|
-
# more details.
|
55
|
-
def delete(path,
|
56
|
-
process
|
37
|
+
# Performs a DELETE request with the given parameters. See ActionDispatch::Integration::Session#process
|
38
|
+
# for more details.
|
39
|
+
def delete(path, **args)
|
40
|
+
process(:delete, path, **args)
|
57
41
|
end
|
58
42
|
|
59
|
-
# Performs a HEAD request with the given parameters. See
|
60
|
-
# details.
|
61
|
-
def head(path,
|
62
|
-
process
|
43
|
+
# Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process
|
44
|
+
# for more details.
|
45
|
+
def head(path, **args)
|
46
|
+
process(:head, path, **args)
|
63
47
|
end
|
64
48
|
|
65
|
-
# Performs an
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
|
70
|
-
# string; the headers are a hash.
|
71
|
-
def xml_http_request(request_method, path, parameters = nil, headers_or_env = nil)
|
72
|
-
headers_or_env ||= {}
|
73
|
-
headers_or_env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
74
|
-
headers_or_env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
|
75
|
-
process(request_method, path, parameters, headers_or_env)
|
49
|
+
# Performs an OPTIONS request with the given parameters. See ActionDispatch::Integration::Session#process
|
50
|
+
# for more details.
|
51
|
+
def options(path, **args)
|
52
|
+
process(:options, path, **args)
|
76
53
|
end
|
77
|
-
alias xhr :xml_http_request
|
78
54
|
|
79
55
|
# Follow a single redirect response. If the last response was not a
|
80
56
|
# redirect, an exception will be raised. Otherwise, the redirect is
|
81
|
-
# performed on the location header.
|
82
|
-
|
57
|
+
# performed on the location header. If the redirection is a 307 or 308 redirect,
|
58
|
+
# the same HTTP verb will be used when redirecting, otherwise a GET request
|
59
|
+
# will be performed. Any arguments are passed to the
|
60
|
+
# underlying request.
|
61
|
+
def follow_redirect!(**args)
|
83
62
|
raise "not a redirect! #{status} #{status_message}" unless redirect?
|
84
|
-
get(response.location)
|
85
|
-
status
|
86
|
-
end
|
87
|
-
|
88
|
-
# Performs a request using the specified method, following any subsequent
|
89
|
-
# redirect. Note that the redirects are followed until the response is
|
90
|
-
# not a redirect--this means you may run into an infinite loop if your
|
91
|
-
# redirect loops back to itself.
|
92
|
-
def request_via_redirect(http_method, path, parameters = nil, headers_or_env = nil)
|
93
|
-
process(http_method, path, parameters, headers_or_env)
|
94
|
-
follow_redirect! while redirect?
|
95
|
-
status
|
96
|
-
end
|
97
|
-
|
98
|
-
# Performs a GET request, following any subsequent redirect.
|
99
|
-
# See +request_via_redirect+ for more information.
|
100
|
-
def get_via_redirect(path, parameters = nil, headers_or_env = nil)
|
101
|
-
request_via_redirect(:get, path, parameters, headers_or_env)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Performs a POST request, following any subsequent redirect.
|
105
|
-
# See +request_via_redirect+ for more information.
|
106
|
-
def post_via_redirect(path, parameters = nil, headers_or_env = nil)
|
107
|
-
request_via_redirect(:post, path, parameters, headers_or_env)
|
108
|
-
end
|
109
63
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# Performs a PUT request, following any subsequent redirect.
|
117
|
-
# See +request_via_redirect+ for more information.
|
118
|
-
def put_via_redirect(path, parameters = nil, headers_or_env = nil)
|
119
|
-
request_via_redirect(:put, path, parameters, headers_or_env)
|
120
|
-
end
|
64
|
+
method =
|
65
|
+
if [307, 308].include?(response.status)
|
66
|
+
request.method.downcase
|
67
|
+
else
|
68
|
+
:get
|
69
|
+
end
|
121
70
|
|
122
|
-
|
123
|
-
|
124
|
-
def delete_via_redirect(path, parameters = nil, headers_or_env = nil)
|
125
|
-
request_via_redirect(:delete, path, parameters, headers_or_env)
|
71
|
+
public_send(method, response.location, **args)
|
72
|
+
status
|
126
73
|
end
|
127
74
|
end
|
128
75
|
|
@@ -140,13 +87,8 @@ module ActionDispatch
|
|
140
87
|
include Minitest::Assertions
|
141
88
|
include TestProcess, RequestHelpers, Assertions
|
142
89
|
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
%w( path ).each do |method|
|
148
|
-
delegate method, :to => :request, :allow_nil => true
|
149
|
-
end
|
90
|
+
delegate :status, :status_message, :headers, :body, :redirect?, to: :response, allow_nil: true
|
91
|
+
delegate :path, to: :request, allow_nil: true
|
150
92
|
|
151
93
|
# The hostname used in the last request.
|
152
94
|
def host
|
@@ -185,27 +127,18 @@ module ActionDispatch
|
|
185
127
|
super()
|
186
128
|
@app = app
|
187
129
|
|
188
|
-
# If the app is a Rails app, make url_helpers available on the session
|
189
|
-
# This makes app.url_for and app.foo_path available in the console
|
190
|
-
if app.respond_to?(:routes)
|
191
|
-
singleton_class.class_eval do
|
192
|
-
include app.routes.url_helpers
|
193
|
-
include app.routes.mounted_helpers
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
130
|
reset!
|
198
131
|
end
|
199
132
|
|
200
133
|
def url_options
|
201
134
|
@url_options ||= default_url_options.dup.tap do |url_options|
|
202
|
-
url_options.reverse_merge!(controller.url_options) if controller
|
135
|
+
url_options.reverse_merge!(controller.url_options) if controller.respond_to?(:url_options)
|
203
136
|
|
204
137
|
if @app.respond_to?(:routes)
|
205
138
|
url_options.reverse_merge!(@app.routes.default_url_options)
|
206
139
|
end
|
207
140
|
|
208
|
-
url_options.reverse_merge!(:
|
141
|
+
url_options.reverse_merge!(host: host, protocol: https? ? "https" : "http")
|
209
142
|
end
|
210
143
|
end
|
211
144
|
|
@@ -223,8 +156,8 @@ module ActionDispatch
|
|
223
156
|
|
224
157
|
self.host = DEFAULT_HOST
|
225
158
|
self.remote_addr = "127.0.0.1"
|
226
|
-
self.accept = "text/xml,application/xml,application/xhtml+xml,"
|
227
|
-
"text/html;q=0.9,text/plain;q=0.8,image/png,"
|
159
|
+
self.accept = "text/xml,application/xml,application/xhtml+xml," \
|
160
|
+
"text/html;q=0.9,text/plain;q=0.8,image/png," \
|
228
161
|
"*/*;q=0.5"
|
229
162
|
|
230
163
|
unless defined? @named_routes_configured
|
@@ -251,102 +184,196 @@ module ActionDispatch
|
|
251
184
|
@https
|
252
185
|
end
|
253
186
|
|
254
|
-
#
|
187
|
+
# Performs the actual request.
|
255
188
|
#
|
256
|
-
#
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
189
|
+
# - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
|
190
|
+
# as a symbol.
|
191
|
+
# - +path+: The URI (as a String) on which you want to perform the
|
192
|
+
# request.
|
193
|
+
# - +params+: The HTTP parameters that you want to pass. This may
|
194
|
+
# be +nil+,
|
195
|
+
# a Hash, or a String that is appropriately encoded
|
196
|
+
# (<tt>application/x-www-form-urlencoded</tt> or
|
197
|
+
# <tt>multipart/form-data</tt>).
|
198
|
+
# - +headers+: Additional headers to pass, as a Hash. The headers will be
|
199
|
+
# merged into the Rack env hash.
|
200
|
+
# - +env+: Additional env to pass, as a Hash. The headers will be
|
201
|
+
# merged into the Rack env hash.
|
202
|
+
# - +xhr+: Set to +true+ if you want to make and Ajax request.
|
203
|
+
# Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
|
204
|
+
# The headers will be merged into the Rack env hash.
|
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.
|
208
|
+
#
|
209
|
+
# This method is rarely used directly. Use +#get+, +#post+, or other standard
|
210
|
+
# HTTP methods in integration tests. +#process+ is only required when using a
|
211
|
+
# request method that doesn't have a method defined in the integration tests.
|
212
|
+
#
|
213
|
+
# This method returns the response status, after performing the request.
|
214
|
+
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
|
215
|
+
# then that object's <tt>@response</tt> instance variable will point to a Response object
|
216
|
+
# which one can use to inspect the details of the response.
|
217
|
+
#
|
218
|
+
# Example:
|
219
|
+
# process :get, '/author', params: { since: 201501011400 }
|
220
|
+
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
221
|
+
request_encoder = RequestEncoder.encoder(as)
|
222
|
+
headers ||= {}
|
223
|
+
|
224
|
+
if method == :get && as == :json && params
|
225
|
+
headers["X-Http-Method-Override"] = "GET"
|
226
|
+
method = :post
|
262
227
|
end
|
263
228
|
|
264
|
-
|
265
|
-
|
266
|
-
if path =~ %r{://}
|
267
|
-
location = URI.parse(path)
|
229
|
+
if %r{://}.match?(path)
|
230
|
+
path = build_expanded_path(path) do |location|
|
268
231
|
https! URI::HTTPS === location if location.scheme
|
269
|
-
|
270
|
-
|
232
|
+
|
233
|
+
if url_host = location.host
|
234
|
+
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
235
|
+
url_host += ":#{location.port}" if default != location.port
|
236
|
+
host! url_host
|
237
|
+
end
|
271
238
|
end
|
239
|
+
end
|
240
|
+
|
241
|
+
hostname, port = host.split(":")
|
242
|
+
|
243
|
+
request_env = {
|
244
|
+
:method => method,
|
245
|
+
:params => request_encoder.encode_params(params),
|
246
|
+
|
247
|
+
"SERVER_NAME" => hostname,
|
248
|
+
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
249
|
+
"HTTPS" => https? ? "on" : "off",
|
250
|
+
"rack.url_scheme" => https? ? "https" : "http",
|
251
|
+
|
252
|
+
"REQUEST_URI" => path,
|
253
|
+
"HTTP_HOST" => host,
|
254
|
+
"REMOTE_ADDR" => remote_addr,
|
255
|
+
"CONTENT_TYPE" => request_encoder.content_type,
|
256
|
+
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
257
|
+
}
|
272
258
|
|
273
|
-
|
259
|
+
wrapped_headers = Http::Headers.from_hash({})
|
260
|
+
wrapped_headers.merge!(headers) if headers
|
274
261
|
|
275
|
-
|
276
|
-
|
277
|
-
|
262
|
+
if xhr
|
263
|
+
wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
|
264
|
+
wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
265
|
+
end
|
278
266
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
267
|
+
# This modifies the passed request_env directly.
|
268
|
+
if wrapped_headers.present?
|
269
|
+
Http::Headers.from_hash(request_env).merge!(wrapped_headers)
|
270
|
+
end
|
271
|
+
if env.present?
|
272
|
+
Http::Headers.from_hash(request_env).merge!(env)
|
273
|
+
end
|
283
274
|
|
284
|
-
|
285
|
-
"HTTP_HOST" => host,
|
286
|
-
"REMOTE_ADDR" => remote_addr,
|
287
|
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
288
|
-
"HTTP_ACCEPT" => accept
|
289
|
-
}
|
290
|
-
# this modifies the passed env directly
|
291
|
-
Http::Headers.new(env).merge!(headers_or_env || {})
|
275
|
+
session = Rack::Test::Session.new(_mock_session)
|
292
276
|
|
293
|
-
|
277
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
278
|
+
# Make sure requested path is always a full URI.
|
279
|
+
session.request(build_full_uri(path, request_env), request_env)
|
294
280
|
|
295
|
-
|
296
|
-
|
297
|
-
|
281
|
+
@request_count += 1
|
282
|
+
@request = ActionDispatch::Request.new(session.last_request.env)
|
283
|
+
response = _mock_session.last_response
|
284
|
+
@response = ActionDispatch::TestResponse.from_response(response)
|
285
|
+
@response.request = @request
|
286
|
+
@html_document = nil
|
287
|
+
@url_options = nil
|
298
288
|
|
299
|
-
|
300
|
-
@request = ActionDispatch::Request.new(session.last_request.env)
|
301
|
-
response = _mock_session.last_response
|
302
|
-
@response = ActionDispatch::TestResponse.from_response(response)
|
303
|
-
@html_document = nil
|
304
|
-
@html_scanner_document = nil
|
305
|
-
@url_options = nil
|
289
|
+
@controller = @request.controller_instance
|
306
290
|
|
307
|
-
|
291
|
+
response.status
|
292
|
+
end
|
308
293
|
|
309
|
-
|
294
|
+
# Set the host name to use in the next request.
|
295
|
+
#
|
296
|
+
# session.host! "www.example.com"
|
297
|
+
alias :host! :host=
|
298
|
+
|
299
|
+
private
|
300
|
+
def _mock_session
|
301
|
+
@_mock_session ||= Rack::MockSession.new(@app, host)
|
310
302
|
end
|
311
303
|
|
312
304
|
def build_full_uri(path, env)
|
313
305
|
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
|
314
306
|
end
|
307
|
+
|
308
|
+
def build_expanded_path(path)
|
309
|
+
location = URI.parse(path)
|
310
|
+
yield location if block_given?
|
311
|
+
path = location.path
|
312
|
+
location.query ? "#{path}?#{location.query}" : path
|
313
|
+
end
|
315
314
|
end
|
316
315
|
|
317
316
|
module Runner
|
318
317
|
include ActionDispatch::Assertions
|
319
318
|
|
320
|
-
|
321
|
-
|
319
|
+
APP_SESSIONS = {}
|
320
|
+
|
321
|
+
attr_reader :app
|
322
|
+
attr_accessor :root_session # :nodoc:
|
323
|
+
|
324
|
+
def initialize(*args, &blk)
|
325
|
+
super(*args, &blk)
|
326
|
+
@integration_session = nil
|
327
|
+
end
|
328
|
+
|
329
|
+
def before_setup # :nodoc:
|
330
|
+
@app = nil
|
331
|
+
super
|
332
|
+
end
|
333
|
+
|
334
|
+
def integration_session
|
335
|
+
@integration_session ||= create_session(app)
|
322
336
|
end
|
323
337
|
|
324
338
|
# Reset the current session. This is useful for testing multiple sessions
|
325
339
|
# in a single test case.
|
326
340
|
def reset!
|
327
|
-
@integration_session =
|
341
|
+
@integration_session = create_session(app)
|
342
|
+
end
|
343
|
+
|
344
|
+
def create_session(app)
|
345
|
+
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
|
346
|
+
# If the app is a Rails app, make url_helpers available on the session.
|
347
|
+
# This makes app.url_for and app.foo_path available in the console.
|
348
|
+
if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
|
349
|
+
include app.routes.url_helpers
|
350
|
+
include app.routes.mounted_helpers
|
351
|
+
end
|
352
|
+
}
|
353
|
+
klass.new(app)
|
328
354
|
end
|
329
355
|
|
330
356
|
def remove! # :nodoc:
|
331
357
|
@integration_session = nil
|
332
358
|
end
|
333
359
|
|
334
|
-
%w(get post patch put head delete cookies assigns
|
335
|
-
|
336
|
-
|
337
|
-
|
360
|
+
%w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
|
361
|
+
# reset the html_document variable, except for cookies/assigns calls
|
362
|
+
unless method == "cookies" || method == "assigns"
|
363
|
+
reset_html_document = "@html_document = nil"
|
364
|
+
end
|
338
365
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
end
|
366
|
+
definition = RUBY_VERSION >= "2.7" ? "..." : "*args"
|
367
|
+
|
368
|
+
module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
369
|
+
def #{method}(#{definition})
|
370
|
+
#{reset_html_document}
|
345
371
|
|
346
|
-
|
372
|
+
result = integration_session.#{method}(#{definition})
|
347
373
|
copy_session_variables!
|
374
|
+
result
|
348
375
|
end
|
349
|
-
|
376
|
+
RUBY
|
350
377
|
end
|
351
378
|
|
352
379
|
# Open a new session instance. If a block is given, the new session is
|
@@ -362,49 +389,51 @@ module ActionDispatch
|
|
362
389
|
def open_session
|
363
390
|
dup.tap do |session|
|
364
391
|
session.reset!
|
392
|
+
session.root_session = self.root_session || self
|
365
393
|
yield session if block_given?
|
366
394
|
end
|
367
395
|
end
|
368
396
|
|
397
|
+
def assertions # :nodoc:
|
398
|
+
root_session ? root_session.assertions : super
|
399
|
+
end
|
400
|
+
|
401
|
+
def assertions=(assertions) # :nodoc:
|
402
|
+
root_session ? root_session.assertions = assertions : super
|
403
|
+
end
|
404
|
+
|
369
405
|
# Copy the instance variables from the current session instance into the
|
370
406
|
# test instance.
|
371
407
|
def copy_session_variables! #:nodoc:
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
end
|
408
|
+
@controller = @integration_session.controller
|
409
|
+
@response = @integration_session.response
|
410
|
+
@request = @integration_session.request
|
376
411
|
end
|
377
412
|
|
378
413
|
def default_url_options
|
379
|
-
reset! unless integration_session
|
380
414
|
integration_session.default_url_options
|
381
415
|
end
|
382
416
|
|
383
417
|
def default_url_options=(options)
|
384
|
-
reset! unless integration_session
|
385
418
|
integration_session.default_url_options = options
|
386
419
|
end
|
387
420
|
|
388
|
-
|
389
|
-
|
421
|
+
private
|
422
|
+
def respond_to_missing?(method, _)
|
423
|
+
integration_session.respond_to?(method) || super
|
390
424
|
end
|
391
425
|
|
392
426
|
# Delegate unhandled messages to the current session instance.
|
393
|
-
def method_missing(
|
394
|
-
|
395
|
-
|
396
|
-
integration_session.__send__(sym, *args, &block).tap do
|
427
|
+
def method_missing(method, *args, &block)
|
428
|
+
if integration_session.respond_to?(method)
|
429
|
+
integration_session.public_send(method, *args, &block).tap do
|
397
430
|
copy_session_variables!
|
398
431
|
end
|
399
432
|
else
|
400
433
|
super
|
401
434
|
end
|
402
435
|
end
|
403
|
-
|
404
|
-
private
|
405
|
-
def integration_session
|
406
|
-
@integration_session ||= nil
|
407
|
-
end
|
436
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
408
437
|
end
|
409
438
|
end
|
410
439
|
|
@@ -427,8 +456,8 @@ module ActionDispatch
|
|
427
456
|
# assert_equal 200, status
|
428
457
|
#
|
429
458
|
# # post the login and follow through to the home page
|
430
|
-
# post "/login", username: people(:jamis).username,
|
431
|
-
# password: people(:jamis).password
|
459
|
+
# post "/login", params: { username: people(:jamis).username,
|
460
|
+
# password: people(:jamis).password }
|
432
461
|
# follow_redirect!
|
433
462
|
# assert_equal 200, status
|
434
463
|
# assert_equal "/home", path
|
@@ -467,7 +496,7 @@ module ActionDispatch
|
|
467
496
|
# end
|
468
497
|
#
|
469
498
|
# def speak(room, message)
|
470
|
-
#
|
499
|
+
# post "/say/#{room.id}", xhr: true, params: { message: message }
|
471
500
|
# assert(...)
|
472
501
|
# ...
|
473
502
|
# end
|
@@ -477,38 +506,180 @@ module ActionDispatch
|
|
477
506
|
# open_session do |sess|
|
478
507
|
# sess.extend(CustomAssertions)
|
479
508
|
# who = people(who)
|
480
|
-
# sess.post "/login", username: who.username,
|
481
|
-
# password: who.password
|
509
|
+
# sess.post "/login", params: { username: who.username,
|
510
|
+
# password: who.password }
|
482
511
|
# assert(...)
|
483
512
|
# end
|
484
513
|
# end
|
485
514
|
# end
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
515
|
+
#
|
516
|
+
# Another longer example would be:
|
517
|
+
#
|
518
|
+
# A simple integration test that exercises multiple controllers:
|
519
|
+
#
|
520
|
+
# require "test_helper"
|
521
|
+
#
|
522
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
523
|
+
# test "login and browse site" do
|
524
|
+
# # login via https
|
525
|
+
# https!
|
526
|
+
# get "/login"
|
527
|
+
# assert_response :success
|
528
|
+
#
|
529
|
+
# post "/login", params: { username: users(:david).username, password: users(:david).password }
|
530
|
+
# follow_redirect!
|
531
|
+
# assert_equal '/welcome', path
|
532
|
+
# assert_equal 'Welcome david!', flash[:notice]
|
533
|
+
#
|
534
|
+
# https!(false)
|
535
|
+
# get "/articles/all"
|
536
|
+
# assert_response :success
|
537
|
+
# assert_select 'h1', 'Articles'
|
538
|
+
# end
|
539
|
+
# end
|
540
|
+
#
|
541
|
+
# As you can see the integration test involves multiple controllers and
|
542
|
+
# exercises the entire stack from database to dispatcher. In addition you can
|
543
|
+
# have multiple session instances open simultaneously in a test and extend
|
544
|
+
# those instances with assertion methods to create a very powerful testing
|
545
|
+
# DSL (domain-specific language) just for your application.
|
546
|
+
#
|
547
|
+
# Here's an example of multiple sessions and custom DSL in an integration test
|
548
|
+
#
|
549
|
+
# require "test_helper"
|
550
|
+
#
|
551
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
552
|
+
# test "login and browse site" do
|
553
|
+
# # User david logs in
|
554
|
+
# david = login(:david)
|
555
|
+
# # User guest logs in
|
556
|
+
# guest = login(:guest)
|
557
|
+
#
|
558
|
+
# # Both are now available in different sessions
|
559
|
+
# assert_equal 'Welcome david!', david.flash[:notice]
|
560
|
+
# assert_equal 'Welcome guest!', guest.flash[:notice]
|
561
|
+
#
|
562
|
+
# # User david can browse site
|
563
|
+
# david.browses_site
|
564
|
+
# # User guest can browse site as well
|
565
|
+
# guest.browses_site
|
566
|
+
#
|
567
|
+
# # Continue with other assertions
|
568
|
+
# end
|
569
|
+
#
|
570
|
+
# private
|
571
|
+
#
|
572
|
+
# module CustomDsl
|
573
|
+
# def browses_site
|
574
|
+
# get "/products/all"
|
575
|
+
# assert_response :success
|
576
|
+
# assert_select 'h1', 'Products'
|
577
|
+
# end
|
578
|
+
# end
|
579
|
+
#
|
580
|
+
# def login(user)
|
581
|
+
# open_session do |sess|
|
582
|
+
# sess.extend(CustomDsl)
|
583
|
+
# u = users(user)
|
584
|
+
# sess.https!
|
585
|
+
# sess.post "/login", params: { username: u.username, password: u.password }
|
586
|
+
# assert_equal '/welcome', sess.path
|
587
|
+
# sess.https!(false)
|
588
|
+
# end
|
589
|
+
# end
|
590
|
+
# end
|
591
|
+
#
|
592
|
+
# See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
|
593
|
+
# use +get+, etc.
|
594
|
+
#
|
595
|
+
# === Changing the request encoding
|
596
|
+
#
|
597
|
+
# You can also test your JSON API easily by setting what the request should
|
598
|
+
# be encoded as:
|
599
|
+
#
|
600
|
+
# require "test_helper"
|
601
|
+
#
|
602
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
603
|
+
# test "creates articles" do
|
604
|
+
# assert_difference -> { Article.count } do
|
605
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
606
|
+
# end
|
607
|
+
#
|
608
|
+
# assert_response :success
|
609
|
+
# assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
|
610
|
+
# end
|
611
|
+
# end
|
612
|
+
#
|
613
|
+
# The +as+ option passes an "application/json" Accept header (thereby setting
|
614
|
+
# the request format to JSON unless overridden), sets the content type to
|
615
|
+
# "application/json" and encodes the parameters as JSON.
|
616
|
+
#
|
617
|
+
# Calling +parsed_body+ on the response parses the response body based on the
|
618
|
+
# last response MIME type.
|
619
|
+
#
|
620
|
+
# Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
|
621
|
+
# types you've registered, you can add your own encoders with:
|
622
|
+
#
|
623
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
624
|
+
# param_encoder: -> params { params.to_wibble },
|
625
|
+
# response_parser: -> body { body }
|
626
|
+
#
|
627
|
+
# Where +param_encoder+ defines how the params should be encoded and
|
628
|
+
# +response_parser+ defines how the response body should be parsed through
|
629
|
+
# +parsed_body+.
|
630
|
+
#
|
631
|
+
# Consult the Rails Testing Guide for more.
|
490
632
|
|
491
|
-
|
633
|
+
class IntegrationTest < ActiveSupport::TestCase
|
634
|
+
include TestProcess::FixtureFile
|
492
635
|
|
493
|
-
|
494
|
-
|
636
|
+
module UrlOptions
|
637
|
+
extend ActiveSupport::Concern
|
638
|
+
def url_options
|
639
|
+
integration_session.url_options
|
640
|
+
end
|
495
641
|
end
|
496
642
|
|
497
|
-
|
498
|
-
|
499
|
-
end
|
643
|
+
module Behavior
|
644
|
+
extend ActiveSupport::Concern
|
500
645
|
|
501
|
-
|
502
|
-
|
503
|
-
end
|
646
|
+
include Integration::Runner
|
647
|
+
include ActionController::TemplateAssertions
|
504
648
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
649
|
+
included do
|
650
|
+
include ActionDispatch::Routing::UrlFor
|
651
|
+
include UrlOptions # don't let UrlFor override the url_options method
|
652
|
+
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
|
653
|
+
@@app = nil
|
654
|
+
end
|
655
|
+
|
656
|
+
module ClassMethods
|
657
|
+
def app
|
658
|
+
if defined?(@@app) && @@app
|
659
|
+
@@app
|
660
|
+
else
|
661
|
+
ActionDispatch.test_app
|
662
|
+
end
|
663
|
+
end
|
509
664
|
|
510
|
-
|
511
|
-
|
665
|
+
def app=(app)
|
666
|
+
@@app = app
|
667
|
+
end
|
668
|
+
|
669
|
+
def register_encoder(*args, **options)
|
670
|
+
RequestEncoder.register_encoder(*args, **options)
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
def app
|
675
|
+
super || self.class.app
|
676
|
+
end
|
677
|
+
|
678
|
+
def document_root_element
|
679
|
+
html_document.root
|
680
|
+
end
|
512
681
|
end
|
682
|
+
|
683
|
+
include Behavior
|
513
684
|
end
|
514
685
|
end
|