actionpack 4.2.11.3 → 5.0.7.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 +890 -384
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +54 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +14 -11
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +14 -34
- data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +11 -11
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +71 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +9 -9
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +83 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +527 -134
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/railtie.rb +11 -7
- data/lib/action_controller/renderer.rb +113 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +311 -374
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +16 -12
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +45 -14
- data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +19 -9
- data/lib/action_dispatch/http/parameters.rb +70 -40
- data/lib/action_dispatch/http/request.rb +144 -89
- data/lib/action_dispatch/http/response.rb +215 -102
- data/lib/action_dispatch/http/upload.rb +6 -2
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +47 -30
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser.rb +2 -0
- data/lib/action_dispatch/journey/parser_extras.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +88 -26
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +8 -10
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +89 -44
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +188 -134
- data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
- data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +124 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- 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 +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +515 -348
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +5 -4
- data/lib/action_dispatch/routing/route_set.rb +148 -240
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +377 -149
- data/lib/action_dispatch/testing/request_encoder.rb +53 -0
- data/lib/action_dispatch/testing/test_process.rb +24 -20
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +12 -4
- data/lib/action_dispatch.rb +4 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +32 -34
- 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/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
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
|
@@ -2,77 +2,66 @@ require 'stringio'
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
require 'active_support/core_ext/kernel/singleton_class'
|
|
4
4
|
require 'active_support/core_ext/object/try'
|
|
5
|
+
require 'active_support/core_ext/string/strip'
|
|
5
6
|
require 'rack/test'
|
|
6
7
|
require 'minitest'
|
|
7
8
|
|
|
9
|
+
require 'action_dispatch/testing/request_encoder'
|
|
10
|
+
|
|
8
11
|
module ActionDispatch
|
|
9
12
|
module Integration #:nodoc:
|
|
10
13
|
module RequestHelpers
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# - +path+: The URI (as a String) on which you want to perform a GET
|
|
14
|
-
# request.
|
|
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
|
|
14
|
+
def get(path, *args)
|
|
15
|
+
process_with_kwargs(:get, path, *args)
|
|
33
16
|
end
|
|
34
17
|
|
|
35
|
-
# Performs a POST request with the given parameters. See +#
|
|
18
|
+
# Performs a POST request with the given parameters. See +#process+ for more
|
|
36
19
|
# details.
|
|
37
|
-
def post(path,
|
|
38
|
-
|
|
20
|
+
def post(path, *args)
|
|
21
|
+
process_with_kwargs(:post, path, *args)
|
|
39
22
|
end
|
|
40
23
|
|
|
41
|
-
# Performs a PATCH request with the given parameters. See +#
|
|
24
|
+
# Performs a PATCH request with the given parameters. See +#process+ for more
|
|
42
25
|
# details.
|
|
43
|
-
def patch(path,
|
|
44
|
-
|
|
26
|
+
def patch(path, *args)
|
|
27
|
+
process_with_kwargs(:patch, path, *args)
|
|
45
28
|
end
|
|
46
29
|
|
|
47
|
-
# Performs a PUT request with the given parameters. See +#
|
|
30
|
+
# Performs a PUT request with the given parameters. See +#process+ for more
|
|
48
31
|
# details.
|
|
49
|
-
def put(path,
|
|
50
|
-
|
|
32
|
+
def put(path, *args)
|
|
33
|
+
process_with_kwargs(:put, path, *args)
|
|
51
34
|
end
|
|
52
35
|
|
|
53
|
-
# Performs a DELETE request with the given parameters. See +#
|
|
36
|
+
# Performs a DELETE request with the given parameters. See +#process+ for
|
|
54
37
|
# more details.
|
|
55
|
-
def delete(path,
|
|
56
|
-
|
|
38
|
+
def delete(path, *args)
|
|
39
|
+
process_with_kwargs(:delete, path, *args)
|
|
57
40
|
end
|
|
58
41
|
|
|
59
|
-
# Performs a HEAD request with the given parameters. See +#
|
|
42
|
+
# Performs a HEAD request with the given parameters. See +#process+ for more
|
|
60
43
|
# details.
|
|
61
|
-
def head(path,
|
|
62
|
-
|
|
44
|
+
def head(path, *args)
|
|
45
|
+
process_with_kwargs(:head, path, *args)
|
|
63
46
|
end
|
|
64
47
|
|
|
65
48
|
# Performs an XMLHttpRequest request with the given parameters, mirroring
|
|
66
|
-
#
|
|
49
|
+
# an AJAX request made from JavaScript.
|
|
67
50
|
#
|
|
68
51
|
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
|
|
69
52
|
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
|
|
70
53
|
# string; the headers are a hash.
|
|
54
|
+
#
|
|
55
|
+
# Example:
|
|
56
|
+
#
|
|
57
|
+
# xhr :get, '/feed', since: 201501011400
|
|
71
58
|
def xml_http_request(request_method, path, parameters = nil, headers_or_env = nil)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
59
|
+
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
|
60
|
+
`xhr` and `xml_http_request` are deprecated and will be removed in Rails 5.1.
|
|
61
|
+
Switch to e.g. `post comments_path, params: { comment: { body: 'Honey bunny' } }, xhr: true`.
|
|
62
|
+
MSG
|
|
63
|
+
|
|
64
|
+
process(request_method, path, params: parameters, headers: headers_or_env, xhr: true)
|
|
76
65
|
end
|
|
77
66
|
alias xhr :xml_http_request
|
|
78
67
|
|
|
@@ -89,40 +78,53 @@ module ActionDispatch
|
|
|
89
78
|
# redirect. Note that the redirects are followed until the response is
|
|
90
79
|
# not a redirect--this means you may run into an infinite loop if your
|
|
91
80
|
# redirect loops back to itself.
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
#
|
|
82
|
+
# Example:
|
|
83
|
+
#
|
|
84
|
+
# request_via_redirect :post, '/welcome',
|
|
85
|
+
# params: { ref_id: 14 },
|
|
86
|
+
# headers: { "X-Test-Header" => "testvalue" }
|
|
87
|
+
def request_via_redirect(http_method, path, *args)
|
|
88
|
+
ActiveSupport::Deprecation.warn('`request_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
89
|
+
process_with_kwargs(http_method, path, *args)
|
|
90
|
+
|
|
94
91
|
follow_redirect! while redirect?
|
|
95
92
|
status
|
|
96
93
|
end
|
|
97
94
|
|
|
98
95
|
# Performs a GET request, following any subsequent redirect.
|
|
99
96
|
# See +request_via_redirect+ for more information.
|
|
100
|
-
def get_via_redirect(path,
|
|
101
|
-
|
|
97
|
+
def get_via_redirect(path, *args)
|
|
98
|
+
ActiveSupport::Deprecation.warn('`get_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
99
|
+
request_via_redirect(:get, path, *args)
|
|
102
100
|
end
|
|
103
101
|
|
|
104
102
|
# Performs a POST request, following any subsequent redirect.
|
|
105
103
|
# See +request_via_redirect+ for more information.
|
|
106
|
-
def post_via_redirect(path,
|
|
107
|
-
|
|
104
|
+
def post_via_redirect(path, *args)
|
|
105
|
+
ActiveSupport::Deprecation.warn('`post_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
106
|
+
request_via_redirect(:post, path, *args)
|
|
108
107
|
end
|
|
109
108
|
|
|
110
109
|
# Performs a PATCH request, following any subsequent redirect.
|
|
111
110
|
# See +request_via_redirect+ for more information.
|
|
112
|
-
def patch_via_redirect(path,
|
|
113
|
-
|
|
111
|
+
def patch_via_redirect(path, *args)
|
|
112
|
+
ActiveSupport::Deprecation.warn('`patch_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
113
|
+
request_via_redirect(:patch, path, *args)
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
# Performs a PUT request, following any subsequent redirect.
|
|
117
117
|
# See +request_via_redirect+ for more information.
|
|
118
|
-
def put_via_redirect(path,
|
|
119
|
-
|
|
118
|
+
def put_via_redirect(path, *args)
|
|
119
|
+
ActiveSupport::Deprecation.warn('`put_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
120
|
+
request_via_redirect(:put, path, *args)
|
|
120
121
|
end
|
|
121
122
|
|
|
122
123
|
# Performs a DELETE request, following any subsequent redirect.
|
|
123
124
|
# See +request_via_redirect+ for more information.
|
|
124
|
-
def delete_via_redirect(path,
|
|
125
|
-
|
|
125
|
+
def delete_via_redirect(path, *args)
|
|
126
|
+
ActiveSupport::Deprecation.warn('`delete_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
|
|
127
|
+
request_via_redirect(:delete, path, *args)
|
|
126
128
|
end
|
|
127
129
|
end
|
|
128
130
|
|
|
@@ -185,15 +187,6 @@ module ActionDispatch
|
|
|
185
187
|
super()
|
|
186
188
|
@app = app
|
|
187
189
|
|
|
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
190
|
reset!
|
|
198
191
|
end
|
|
199
192
|
|
|
@@ -251,6 +244,108 @@ module ActionDispatch
|
|
|
251
244
|
@https
|
|
252
245
|
end
|
|
253
246
|
|
|
247
|
+
# Performs the actual request.
|
|
248
|
+
#
|
|
249
|
+
# - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
|
|
250
|
+
# as a symbol.
|
|
251
|
+
# - +path+: The URI (as a String) on which you want to perform the
|
|
252
|
+
# request.
|
|
253
|
+
# - +params+: The HTTP parameters that you want to pass. This may
|
|
254
|
+
# be +nil+,
|
|
255
|
+
# a Hash, or a String that is appropriately encoded
|
|
256
|
+
# (<tt>application/x-www-form-urlencoded</tt> or
|
|
257
|
+
# <tt>multipart/form-data</tt>).
|
|
258
|
+
# - +headers+: Additional headers to pass, as a Hash. The headers will be
|
|
259
|
+
# merged into the Rack env hash.
|
|
260
|
+
# - +env+: Additional env to pass, as a Hash. The headers will be
|
|
261
|
+
# merged into the Rack env hash.
|
|
262
|
+
#
|
|
263
|
+
# This method is rarely used directly. Use +#get+, +#post+, or other standard
|
|
264
|
+
# HTTP methods in integration tests. +#process+ is only required when using a
|
|
265
|
+
# request method that doesn't have a method defined in the integrations tests.
|
|
266
|
+
#
|
|
267
|
+
# This method returns a Response object, which one can use to
|
|
268
|
+
# inspect the details of the response. Furthermore, if this method was
|
|
269
|
+
# called from an ActionDispatch::IntegrationTest object, then that
|
|
270
|
+
# object's <tt>@response</tt> instance variable will point to the same
|
|
271
|
+
# response object.
|
|
272
|
+
#
|
|
273
|
+
# Example:
|
|
274
|
+
#
|
|
275
|
+
# process :get, '/author', params: { since: 201501011400 }
|
|
276
|
+
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
|
277
|
+
request_encoder = RequestEncoder.encoder(as)
|
|
278
|
+
headers ||= {}
|
|
279
|
+
|
|
280
|
+
if method == :get && as == :json && params
|
|
281
|
+
headers['X-Http-Method-Override'] = 'GET'
|
|
282
|
+
method = :post
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if path =~ %r{://}
|
|
286
|
+
location = URI.parse(path)
|
|
287
|
+
https! URI::HTTPS === location if location.scheme
|
|
288
|
+
if url_host = location.host
|
|
289
|
+
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
|
290
|
+
url_host += ":#{location.port}" if default != location.port
|
|
291
|
+
host! url_host
|
|
292
|
+
end
|
|
293
|
+
path = location.query ? "#{location.path}?#{location.query}" : location.path
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
hostname, port = host.split(':')
|
|
297
|
+
|
|
298
|
+
request_env = {
|
|
299
|
+
:method => method,
|
|
300
|
+
:params => request_encoder.encode_params(params),
|
|
301
|
+
|
|
302
|
+
"SERVER_NAME" => hostname,
|
|
303
|
+
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
|
304
|
+
"HTTPS" => https? ? "on" : "off",
|
|
305
|
+
"rack.url_scheme" => https? ? "https" : "http",
|
|
306
|
+
|
|
307
|
+
"REQUEST_URI" => path,
|
|
308
|
+
"HTTP_HOST" => host,
|
|
309
|
+
"REMOTE_ADDR" => remote_addr,
|
|
310
|
+
"CONTENT_TYPE" => request_encoder.content_type,
|
|
311
|
+
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
wrapped_headers = Http::Headers.from_hash({})
|
|
315
|
+
wrapped_headers.merge!(headers) if headers
|
|
316
|
+
|
|
317
|
+
if xhr
|
|
318
|
+
wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
|
|
319
|
+
wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# this modifies the passed request_env directly
|
|
323
|
+
if wrapped_headers.present?
|
|
324
|
+
Http::Headers.from_hash(request_env).merge!(wrapped_headers)
|
|
325
|
+
end
|
|
326
|
+
if env.present?
|
|
327
|
+
Http::Headers.from_hash(request_env).merge!(env)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
session = Rack::Test::Session.new(_mock_session)
|
|
331
|
+
|
|
332
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
|
333
|
+
# Make sure requested path is always a full uri
|
|
334
|
+
session.request(build_full_uri(path, request_env), request_env)
|
|
335
|
+
|
|
336
|
+
@request_count += 1
|
|
337
|
+
@request = ActionDispatch::Request.new(session.last_request.env)
|
|
338
|
+
response = _mock_session.last_response
|
|
339
|
+
@response = ActionDispatch::TestResponse.from_response(response)
|
|
340
|
+
@response.request = @request
|
|
341
|
+
@html_document = nil
|
|
342
|
+
@url_options = nil
|
|
343
|
+
|
|
344
|
+
@controller = @request.controller_instance
|
|
345
|
+
|
|
346
|
+
response.status
|
|
347
|
+
end
|
|
348
|
+
|
|
254
349
|
# Set the host name to use in the next request.
|
|
255
350
|
#
|
|
256
351
|
# session.host! "www.example.com"
|
|
@@ -261,52 +356,31 @@ module ActionDispatch
|
|
|
261
356
|
@_mock_session ||= Rack::MockSession.new(@app, host)
|
|
262
357
|
end
|
|
263
358
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
path = location.query ? "#{location.path}?#{location.query}" : location.path
|
|
359
|
+
def process_with_kwargs(http_method, path, *args)
|
|
360
|
+
if kwarg_request?(args)
|
|
361
|
+
process(http_method, path, *args)
|
|
362
|
+
else
|
|
363
|
+
non_kwarg_request_warning if args.any?
|
|
364
|
+
process(http_method, path, { params: args[0], headers: args[1] })
|
|
271
365
|
end
|
|
366
|
+
end
|
|
272
367
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
:params => parameters,
|
|
278
|
-
|
|
279
|
-
"SERVER_NAME" => hostname,
|
|
280
|
-
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
|
281
|
-
"HTTPS" => https? ? "on" : "off",
|
|
282
|
-
"rack.url_scheme" => https? ? "https" : "http",
|
|
283
|
-
|
|
284
|
-
"REQUEST_URI" => path,
|
|
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 || {})
|
|
292
|
-
|
|
293
|
-
session = Rack::Test::Session.new(_mock_session)
|
|
294
|
-
|
|
295
|
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
|
296
|
-
# Make sure requested path is always a full uri
|
|
297
|
-
session.request(build_full_uri(path, env), env)
|
|
368
|
+
REQUEST_KWARGS = %i(params headers env xhr as)
|
|
369
|
+
def kwarg_request?(args)
|
|
370
|
+
args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
|
|
371
|
+
end
|
|
298
372
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
@html_document = nil
|
|
304
|
-
@html_scanner_document = nil
|
|
305
|
-
@url_options = nil
|
|
373
|
+
def non_kwarg_request_warning
|
|
374
|
+
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
|
375
|
+
Using positional arguments in integration tests has been deprecated,
|
|
376
|
+
in favor of keyword arguments, and will be removed in Rails 5.1.
|
|
306
377
|
|
|
307
|
-
|
|
378
|
+
Deprecated style:
|
|
379
|
+
get "/profile", { id: 1 }, { "X-Extra-Header" => "123" }
|
|
308
380
|
|
|
309
|
-
|
|
381
|
+
New keyword style:
|
|
382
|
+
get "/profile", params: { id: 1 }, headers: { "X-Extra-Header" => "123" }
|
|
383
|
+
MSG
|
|
310
384
|
end
|
|
311
385
|
|
|
312
386
|
def build_full_uri(path, env)
|
|
@@ -317,14 +391,40 @@ module ActionDispatch
|
|
|
317
391
|
module Runner
|
|
318
392
|
include ActionDispatch::Assertions
|
|
319
393
|
|
|
320
|
-
|
|
321
|
-
|
|
394
|
+
APP_SESSIONS = {}
|
|
395
|
+
|
|
396
|
+
attr_reader :app
|
|
397
|
+
|
|
398
|
+
def initialize(*args, &blk)
|
|
399
|
+
super(*args, &blk)
|
|
400
|
+
@integration_session = nil
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def before_setup # :nodoc:
|
|
404
|
+
@app = nil
|
|
405
|
+
super
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def integration_session
|
|
409
|
+
@integration_session ||= create_session(app)
|
|
322
410
|
end
|
|
323
411
|
|
|
324
412
|
# Reset the current session. This is useful for testing multiple sessions
|
|
325
413
|
# in a single test case.
|
|
326
414
|
def reset!
|
|
327
|
-
@integration_session =
|
|
415
|
+
@integration_session = create_session(app)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def create_session(app)
|
|
419
|
+
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
|
|
420
|
+
# If the app is a Rails app, make url_helpers available on the session
|
|
421
|
+
# This makes app.url_for and app.foo_path available in the console
|
|
422
|
+
if app.respond_to?(:routes)
|
|
423
|
+
include app.routes.url_helpers
|
|
424
|
+
include app.routes.mounted_helpers
|
|
425
|
+
end
|
|
426
|
+
}
|
|
427
|
+
klass.new(app)
|
|
328
428
|
end
|
|
329
429
|
|
|
330
430
|
def remove! # :nodoc:
|
|
@@ -332,15 +432,11 @@ module ActionDispatch
|
|
|
332
432
|
end
|
|
333
433
|
|
|
334
434
|
%w(get post patch put head delete cookies assigns
|
|
335
|
-
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
|
|
435
|
+
xml_http_request xhr get_via_redirect post_via_redirect follow_redirect!).each do |method|
|
|
336
436
|
define_method(method) do |*args|
|
|
337
|
-
reset! unless integration_session
|
|
338
|
-
|
|
339
437
|
# reset the html_document variable, except for cookies/assigns calls
|
|
340
438
|
unless method == 'cookies' || method == 'assigns'
|
|
341
439
|
@html_document = nil
|
|
342
|
-
@html_scanner_document = nil
|
|
343
|
-
reset_template_assertion
|
|
344
440
|
end
|
|
345
441
|
|
|
346
442
|
integration_session.__send__(method, *args).tap do
|
|
@@ -361,7 +457,6 @@ module ActionDispatch
|
|
|
361
457
|
# simultaneously.
|
|
362
458
|
def open_session
|
|
363
459
|
dup.tap do |session|
|
|
364
|
-
session.reset!
|
|
365
460
|
yield session if block_given?
|
|
366
461
|
end
|
|
367
462
|
end
|
|
@@ -369,19 +464,16 @@ module ActionDispatch
|
|
|
369
464
|
# Copy the instance variables from the current session instance into the
|
|
370
465
|
# test instance.
|
|
371
466
|
def copy_session_variables! #:nodoc:
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
end
|
|
467
|
+
@controller = @integration_session.controller
|
|
468
|
+
@response = @integration_session.response
|
|
469
|
+
@request = @integration_session.request
|
|
376
470
|
end
|
|
377
471
|
|
|
378
472
|
def default_url_options
|
|
379
|
-
reset! unless integration_session
|
|
380
473
|
integration_session.default_url_options
|
|
381
474
|
end
|
|
382
475
|
|
|
383
476
|
def default_url_options=(options)
|
|
384
|
-
reset! unless integration_session
|
|
385
477
|
integration_session.default_url_options = options
|
|
386
478
|
end
|
|
387
479
|
|
|
@@ -391,7 +483,6 @@ module ActionDispatch
|
|
|
391
483
|
|
|
392
484
|
# Delegate unhandled messages to the current session instance.
|
|
393
485
|
def method_missing(sym, *args, &block)
|
|
394
|
-
reset! unless integration_session
|
|
395
486
|
if integration_session.respond_to?(sym)
|
|
396
487
|
integration_session.__send__(sym, *args, &block).tap do
|
|
397
488
|
copy_session_variables!
|
|
@@ -400,11 +491,6 @@ module ActionDispatch
|
|
|
400
491
|
super
|
|
401
492
|
end
|
|
402
493
|
end
|
|
403
|
-
|
|
404
|
-
private
|
|
405
|
-
def integration_session
|
|
406
|
-
@integration_session ||= nil
|
|
407
|
-
end
|
|
408
494
|
end
|
|
409
495
|
end
|
|
410
496
|
|
|
@@ -427,8 +513,8 @@ module ActionDispatch
|
|
|
427
513
|
# assert_equal 200, status
|
|
428
514
|
#
|
|
429
515
|
# # post the login and follow through to the home page
|
|
430
|
-
# post "/login", username: people(:jamis).username,
|
|
431
|
-
# password: people(:jamis).password
|
|
516
|
+
# post "/login", params: { username: people(:jamis).username,
|
|
517
|
+
# password: people(:jamis).password }
|
|
432
518
|
# follow_redirect!
|
|
433
519
|
# assert_equal 200, status
|
|
434
520
|
# assert_equal "/home", path
|
|
@@ -467,7 +553,7 @@ module ActionDispatch
|
|
|
467
553
|
# end
|
|
468
554
|
#
|
|
469
555
|
# def speak(room, message)
|
|
470
|
-
#
|
|
556
|
+
# post "/say/#{room.id}", xhr: true, params: { message: message }
|
|
471
557
|
# assert(...)
|
|
472
558
|
# ...
|
|
473
559
|
# end
|
|
@@ -477,38 +563,180 @@ module ActionDispatch
|
|
|
477
563
|
# open_session do |sess|
|
|
478
564
|
# sess.extend(CustomAssertions)
|
|
479
565
|
# who = people(who)
|
|
480
|
-
# sess.post "/login", username: who.username,
|
|
481
|
-
# password: who.password
|
|
566
|
+
# sess.post "/login", params: { username: who.username,
|
|
567
|
+
# password: who.password }
|
|
482
568
|
# assert(...)
|
|
483
569
|
# end
|
|
484
570
|
# end
|
|
485
571
|
# end
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
572
|
+
#
|
|
573
|
+
# Another longer example would be:
|
|
574
|
+
#
|
|
575
|
+
# A simple integration test that exercises multiple controllers:
|
|
576
|
+
#
|
|
577
|
+
# require 'test_helper'
|
|
578
|
+
#
|
|
579
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
580
|
+
# test "login and browse site" do
|
|
581
|
+
# # login via https
|
|
582
|
+
# https!
|
|
583
|
+
# get "/login"
|
|
584
|
+
# assert_response :success
|
|
585
|
+
#
|
|
586
|
+
# post "/login", params: { username: users(:david).username, password: users(:david).password }
|
|
587
|
+
# follow_redirect!
|
|
588
|
+
# assert_equal '/welcome', path
|
|
589
|
+
# assert_equal 'Welcome david!', flash[:notice]
|
|
590
|
+
#
|
|
591
|
+
# https!(false)
|
|
592
|
+
# get "/articles/all"
|
|
593
|
+
# assert_response :success
|
|
594
|
+
# assert_select 'h1', 'Articles'
|
|
595
|
+
# end
|
|
596
|
+
# end
|
|
597
|
+
#
|
|
598
|
+
# As you can see the integration test involves multiple controllers and
|
|
599
|
+
# exercises the entire stack from database to dispatcher. In addition you can
|
|
600
|
+
# have multiple session instances open simultaneously in a test and extend
|
|
601
|
+
# those instances with assertion methods to create a very powerful testing
|
|
602
|
+
# DSL (domain-specific language) just for your application.
|
|
603
|
+
#
|
|
604
|
+
# Here's an example of multiple sessions and custom DSL in an integration test
|
|
605
|
+
#
|
|
606
|
+
# require 'test_helper'
|
|
607
|
+
#
|
|
608
|
+
# class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
609
|
+
# test "login and browse site" do
|
|
610
|
+
# # User david logs in
|
|
611
|
+
# david = login(:david)
|
|
612
|
+
# # User guest logs in
|
|
613
|
+
# guest = login(:guest)
|
|
614
|
+
#
|
|
615
|
+
# # Both are now available in different sessions
|
|
616
|
+
# assert_equal 'Welcome david!', david.flash[:notice]
|
|
617
|
+
# assert_equal 'Welcome guest!', guest.flash[:notice]
|
|
618
|
+
#
|
|
619
|
+
# # User david can browse site
|
|
620
|
+
# david.browses_site
|
|
621
|
+
# # User guest can browse site as well
|
|
622
|
+
# guest.browses_site
|
|
623
|
+
#
|
|
624
|
+
# # Continue with other assertions
|
|
625
|
+
# end
|
|
626
|
+
#
|
|
627
|
+
# private
|
|
628
|
+
#
|
|
629
|
+
# module CustomDsl
|
|
630
|
+
# def browses_site
|
|
631
|
+
# get "/products/all"
|
|
632
|
+
# assert_response :success
|
|
633
|
+
# assert_select 'h1', 'Products'
|
|
634
|
+
# end
|
|
635
|
+
# end
|
|
636
|
+
#
|
|
637
|
+
# def login(user)
|
|
638
|
+
# open_session do |sess|
|
|
639
|
+
# sess.extend(CustomDsl)
|
|
640
|
+
# u = users(user)
|
|
641
|
+
# sess.https!
|
|
642
|
+
# sess.post "/login", params: { username: u.username, password: u.password }
|
|
643
|
+
# assert_equal '/welcome', sess.path
|
|
644
|
+
# sess.https!(false)
|
|
645
|
+
# end
|
|
646
|
+
# end
|
|
647
|
+
# end
|
|
648
|
+
#
|
|
649
|
+
# See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
|
|
650
|
+
# use +get+, etc.
|
|
651
|
+
#
|
|
652
|
+
# === Changing the request encoding
|
|
653
|
+
#
|
|
654
|
+
# You can also test your JSON API easily by setting what the request should
|
|
655
|
+
# be encoded as:
|
|
656
|
+
#
|
|
657
|
+
# require "test_helper"
|
|
658
|
+
#
|
|
659
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
|
660
|
+
# test "creates articles" do
|
|
661
|
+
# assert_difference -> { Article.count } do
|
|
662
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
|
663
|
+
# end
|
|
664
|
+
#
|
|
665
|
+
# assert_response :success
|
|
666
|
+
# assert_equal({ id: Arcticle.last.id, title: "Ahoy!" }, response.parsed_body)
|
|
667
|
+
# end
|
|
668
|
+
# end
|
|
669
|
+
#
|
|
670
|
+
# The +as+ option passes an "application/json" Accept header (thereby setting
|
|
671
|
+
# the request format to JSON unless overridden), sets the content type to
|
|
672
|
+
# "application/json" and encodes the parameters as JSON.
|
|
673
|
+
#
|
|
674
|
+
# Calling +parsed_body+ on the response parses the response body based on the
|
|
675
|
+
# last response MIME type.
|
|
676
|
+
#
|
|
677
|
+
# Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
|
|
678
|
+
# types you've registered, you can add your own encoders with:
|
|
679
|
+
#
|
|
680
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
|
681
|
+
# param_encoder: -> params { params.to_wibble },
|
|
682
|
+
# response_parser: -> body { body }
|
|
683
|
+
#
|
|
684
|
+
# Where +param_encoder+ defines how the params should be encoded and
|
|
685
|
+
# +response_parser+ defines how the response body should be parsed through
|
|
686
|
+
# +parsed_body+.
|
|
687
|
+
#
|
|
688
|
+
# Consult the Rails Testing Guide for more.
|
|
490
689
|
|
|
491
|
-
|
|
690
|
+
class IntegrationTest < ActiveSupport::TestCase
|
|
691
|
+
include TestProcess::FixtureFile
|
|
492
692
|
|
|
493
|
-
|
|
494
|
-
|
|
693
|
+
module UrlOptions
|
|
694
|
+
extend ActiveSupport::Concern
|
|
695
|
+
def url_options
|
|
696
|
+
integration_session.url_options
|
|
697
|
+
end
|
|
495
698
|
end
|
|
496
699
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
end
|
|
700
|
+
module Behavior
|
|
701
|
+
extend ActiveSupport::Concern
|
|
500
702
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
end
|
|
703
|
+
include Integration::Runner
|
|
704
|
+
include ActionController::TemplateAssertions
|
|
504
705
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
706
|
+
included do
|
|
707
|
+
include ActionDispatch::Routing::UrlFor
|
|
708
|
+
include UrlOptions # don't let UrlFor override the url_options method
|
|
709
|
+
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
|
|
710
|
+
@@app = nil
|
|
711
|
+
end
|
|
509
712
|
|
|
510
|
-
|
|
511
|
-
|
|
713
|
+
module ClassMethods
|
|
714
|
+
def app
|
|
715
|
+
if defined?(@@app) && @@app
|
|
716
|
+
@@app
|
|
717
|
+
else
|
|
718
|
+
ActionDispatch.test_app
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
def app=(app)
|
|
723
|
+
@@app = app
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def register_encoder(*args)
|
|
727
|
+
RequestEncoder.register_encoder(*args)
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def app
|
|
732
|
+
super || self.class.app
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
def document_root_element
|
|
736
|
+
html_document.root
|
|
737
|
+
end
|
|
512
738
|
end
|
|
739
|
+
|
|
740
|
+
include Behavior
|
|
513
741
|
end
|
|
514
742
|
end
|