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