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