actionpack 7.0.8.7 → 7.2.2.1

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.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -537
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +119 -106
  7. data/lib/abstract_controller/caching/fragments.rb +51 -52
  8. data/lib/abstract_controller/caching.rb +2 -0
  9. data/lib/abstract_controller/callbacks.rb +94 -67
  10. data/lib/abstract_controller/collector.rb +6 -6
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +2 -0
  13. data/lib/abstract_controller/helpers.rb +121 -91
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +14 -13
  17. data/lib/abstract_controller/translation.rb +12 -30
  18. data/lib/abstract_controller/url_for.rb +9 -5
  19. data/lib/abstract_controller.rb +8 -0
  20. data/lib/action_controller/api/api_rendering.rb +2 -0
  21. data/lib/action_controller/api.rb +78 -73
  22. data/lib/action_controller/base.rb +199 -141
  23. data/lib/action_controller/caching.rb +16 -11
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +21 -16
  26. data/lib/action_controller/log_subscriber.rb +19 -5
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  29. data/lib/action_controller/metal/conditional_get.rb +187 -174
  30. data/lib/action_controller/metal/content_security_policy.rb +26 -25
  31. data/lib/action_controller/metal/cookies.rb +4 -2
  32. data/lib/action_controller/metal/data_streaming.rb +65 -54
  33. data/lib/action_controller/metal/default_headers.rb +6 -2
  34. data/lib/action_controller/metal/etag_with_flash.rb +4 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
  36. data/lib/action_controller/metal/exceptions.rb +19 -9
  37. data/lib/action_controller/metal/flash.rb +12 -10
  38. data/lib/action_controller/metal/head.rb +20 -16
  39. data/lib/action_controller/metal/helpers.rb +64 -67
  40. data/lib/action_controller/metal/http_authentication.rb +212 -199
  41. data/lib/action_controller/metal/implicit_render.rb +21 -17
  42. data/lib/action_controller/metal/instrumentation.rb +22 -12
  43. data/lib/action_controller/metal/live.rb +125 -92
  44. data/lib/action_controller/metal/logging.rb +6 -4
  45. data/lib/action_controller/metal/mime_responds.rb +151 -142
  46. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  47. data/lib/action_controller/metal/params_wrapper.rb +58 -58
  48. data/lib/action_controller/metal/permissions_policy.rb +14 -13
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +110 -84
  51. data/lib/action_controller/metal/renderers.rb +50 -49
  52. data/lib/action_controller/metal/rendering.rb +103 -82
  53. data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
  54. data/lib/action_controller/metal/rescue.rb +12 -8
  55. data/lib/action_controller/metal/streaming.rb +174 -132
  56. data/lib/action_controller/metal/strong_parameters.rb +598 -473
  57. data/lib/action_controller/metal/testing.rb +2 -0
  58. data/lib/action_controller/metal/url_for.rb +23 -14
  59. data/lib/action_controller/metal.rb +145 -61
  60. data/lib/action_controller/railtie.rb +25 -9
  61. data/lib/action_controller/railties/helpers.rb +2 -0
  62. data/lib/action_controller/renderer.rb +105 -66
  63. data/lib/action_controller/template_assertions.rb +4 -2
  64. data/lib/action_controller/test_case.rb +157 -128
  65. data/lib/action_controller.rb +17 -3
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +28 -29
  69. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +48 -45
  71. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  72. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  73. data/lib/action_dispatch/http/headers.rb +23 -21
  74. data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
  75. data/lib/action_dispatch/http/mime_type.rb +60 -30
  76. data/lib/action_dispatch/http/mime_types.rb +5 -1
  77. data/lib/action_dispatch/http/parameters.rb +12 -10
  78. data/lib/action_dispatch/http/permissions_policy.rb +32 -27
  79. data/lib/action_dispatch/http/rack_cache.rb +4 -0
  80. data/lib/action_dispatch/http/request.rb +132 -79
  81. data/lib/action_dispatch/http/response.rb +136 -103
  82. data/lib/action_dispatch/http/upload.rb +19 -15
  83. data/lib/action_dispatch/http/url.rb +75 -73
  84. data/lib/action_dispatch/journey/formatter.rb +19 -6
  85. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
  88. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  90. data/lib/action_dispatch/journey/parser.rb +4 -3
  91. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  92. data/lib/action_dispatch/journey/path/pattern.rb +18 -15
  93. data/lib/action_dispatch/journey/route.rb +12 -9
  94. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  95. data/lib/action_dispatch/journey/router.rb +13 -10
  96. data/lib/action_dispatch/journey/routes.rb +6 -4
  97. data/lib/action_dispatch/journey/scanner.rb +4 -2
  98. data/lib/action_dispatch/journey/visitors.rb +2 -0
  99. data/lib/action_dispatch/journey.rb +2 -0
  100. data/lib/action_dispatch/log_subscriber.rb +25 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
  102. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  103. data/lib/action_dispatch/middleware/callbacks.rb +4 -0
  104. data/lib/action_dispatch/middleware/cookies.rb +192 -194
  105. data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
  106. data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
  107. data/lib/action_dispatch/middleware/debug_view.rb +9 -2
  108. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
  109. data/lib/action_dispatch/middleware/executor.rb +9 -1
  110. data/lib/action_dispatch/middleware/flash.rb +65 -46
  111. data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
  112. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
  113. data/lib/action_dispatch/middleware/reloader.rb +9 -5
  114. data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
  115. data/lib/action_dispatch/middleware/request_id.rb +15 -8
  116. data/lib/action_dispatch/middleware/server_timing.rb +8 -6
  117. data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
  118. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
  119. data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
  120. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
  121. data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
  122. data/lib/action_dispatch/middleware/ssl.rb +60 -45
  123. data/lib/action_dispatch/middleware/stack.rb +15 -9
  124. data/lib/action_dispatch/middleware/static.rb +40 -34
  125. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  126. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  129. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  130. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
  139. data/lib/action_dispatch/railtie.rb +12 -4
  140. data/lib/action_dispatch/request/session.rb +39 -27
  141. data/lib/action_dispatch/request/utils.rb +10 -3
  142. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  143. data/lib/action_dispatch/routing/inspector.rb +59 -9
  144. data/lib/action_dispatch/routing/mapper.rb +686 -639
  145. data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
  146. data/lib/action_dispatch/routing/redirection.rb +52 -38
  147. data/lib/action_dispatch/routing/route_set.rb +106 -62
  148. data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
  149. data/lib/action_dispatch/routing/url_for.rb +131 -122
  150. data/lib/action_dispatch/routing.rb +152 -150
  151. data/lib/action_dispatch/system_test_case.rb +91 -81
  152. data/lib/action_dispatch/system_testing/browser.rb +27 -19
  153. data/lib/action_dispatch/system_testing/driver.rb +16 -22
  154. data/lib/action_dispatch/system_testing/server.rb +2 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  158. data/lib/action_dispatch/testing/assertions/response.rb +36 -26
  159. data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
  160. data/lib/action_dispatch/testing/assertions.rb +5 -1
  161. data/lib/action_dispatch/testing/integration.rb +240 -229
  162. data/lib/action_dispatch/testing/request_encoder.rb +6 -1
  163. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  164. data/lib/action_dispatch/testing/test_process.rb +14 -9
  165. data/lib/action_dispatch/testing/test_request.rb +4 -2
  166. data/lib/action_dispatch/testing/test_response.rb +34 -19
  167. data/lib/action_dispatch.rb +52 -21
  168. data/lib/action_pack/gem_version.rb +6 -4
  169. data/lib/action_pack/version.rb +3 -1
  170. data/lib/action_pack.rb +18 -17
  171. metadata +86 -27
@@ -1,64 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "stringio"
4
6
  require "uri"
5
7
  require "rack/test"
6
- require "minitest"
8
+ require "active_support/test_case"
7
9
 
8
10
  require "action_dispatch/testing/request_encoder"
11
+ require "action_dispatch/testing/test_helpers/page_dump_helper"
9
12
 
10
13
  module ActionDispatch
11
14
  module Integration # :nodoc:
12
15
  module RequestHelpers
13
- # Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
14
- # for more details.
16
+ # Performs a GET request with the given parameters. See
17
+ # ActionDispatch::Integration::Session#process for more details.
15
18
  def get(path, **args)
16
19
  process(:get, path, **args)
17
20
  end
18
21
 
19
- # Performs a POST request with the given parameters. See ActionDispatch::Integration::Session#process
20
- # for more details.
22
+ # Performs a POST request with the given parameters. See
23
+ # ActionDispatch::Integration::Session#process for more details.
21
24
  def post(path, **args)
22
25
  process(:post, path, **args)
23
26
  end
24
27
 
25
- # Performs a PATCH request with the given parameters. See ActionDispatch::Integration::Session#process
26
- # for more details.
28
+ # Performs a PATCH request with the given parameters. See
29
+ # ActionDispatch::Integration::Session#process for more details.
27
30
  def patch(path, **args)
28
31
  process(:patch, path, **args)
29
32
  end
30
33
 
31
- # Performs a PUT request with the given parameters. See ActionDispatch::Integration::Session#process
32
- # for more details.
34
+ # Performs a PUT request with the given parameters. See
35
+ # ActionDispatch::Integration::Session#process for more details.
33
36
  def put(path, **args)
34
37
  process(:put, path, **args)
35
38
  end
36
39
 
37
- # Performs a DELETE request with the given parameters. See ActionDispatch::Integration::Session#process
38
- # for more details.
40
+ # Performs a DELETE request with the given parameters. See
41
+ # ActionDispatch::Integration::Session#process for more details.
39
42
  def delete(path, **args)
40
43
  process(:delete, path, **args)
41
44
  end
42
45
 
43
- # Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process
44
- # for more details.
46
+ # Performs a HEAD request with the given parameters. See
47
+ # ActionDispatch::Integration::Session#process for more details.
45
48
  def head(path, **args)
46
49
  process(:head, path, **args)
47
50
  end
48
51
 
49
- # Performs an OPTIONS request with the given parameters. See ActionDispatch::Integration::Session#process
50
- # for more details.
52
+ # Performs an OPTIONS request with the given parameters. See
53
+ # ActionDispatch::Integration::Session#process for more details.
51
54
  def options(path, **args)
52
55
  process(:options, path, **args)
53
56
  end
54
57
 
55
- # Follow a single redirect response. If the last response was not a
56
- # redirect, an exception will be raised. Otherwise, the redirect is
57
- # performed on the location header. If the redirection is a 307 or 308 redirect,
58
- # the same HTTP verb will be used when redirecting, otherwise a GET request
59
- # will be performed. Any arguments are passed to the
60
- # underlying request.
61
- def follow_redirect!(**args)
58
+ # Follow a single redirect response. If the last response was not a redirect, an
59
+ # exception will be raised. Otherwise, the redirect is performed on the location
60
+ # header. If the redirection is a 307 or 308 redirect, the same HTTP verb will
61
+ # be used when redirecting, otherwise a GET request will be performed. Any
62
+ # arguments are passed to the underlying request.
63
+ #
64
+ # The HTTP_REFERER header will be set to the previous url.
65
+ def follow_redirect!(headers: {}, **args)
62
66
  raise "not a redirect! #{status} #{status_message}" unless redirect?
63
67
 
64
68
  method =
@@ -68,19 +72,22 @@ module ActionDispatch
68
72
  :get
69
73
  end
70
74
 
71
- public_send(method, response.location, **args)
75
+ if [ :HTTP_REFERER, "HTTP_REFERER" ].none? { |key| headers.key? key }
76
+ headers["HTTP_REFERER"] = request.url
77
+ end
78
+
79
+ public_send(method, response.location, headers: headers, **args)
72
80
  status
73
81
  end
74
82
  end
75
83
 
76
- # An instance of this class represents a set of requests and responses
77
- # performed sequentially by a test process. Because you can instantiate
78
- # multiple sessions and run them side-by-side, you can also mimic (to some
79
- # limited extent) multiple simultaneous users interacting with your system.
84
+ # An instance of this class represents a set of requests and responses performed
85
+ # sequentially by a test process. Because you can instantiate multiple sessions
86
+ # and run them side-by-side, you can also mimic (to some limited extent)
87
+ # multiple simultaneous users interacting with your system.
80
88
  #
81
- # Typically, you will instantiate a new session using
82
- # IntegrationTest#open_session, rather than instantiating
83
- # Integration::Session directly.
89
+ # Typically, you will instantiate a new session using Runner#open_session,
90
+ # rather than instantiating a Session directly.
84
91
  class Session
85
92
  DEFAULT_HOST = "www.example.com"
86
93
 
@@ -102,8 +109,8 @@ module ActionDispatch
102
109
  # The Accept header to send.
103
110
  attr_accessor :accept
104
111
 
105
- # A map of the cookies returned by the last response, and which will be
106
- # sent with the next request.
112
+ # A map of the cookies returned by the last response, and which will be sent
113
+ # with the next request.
107
114
  def cookies
108
115
  _mock_session.cookie_jar
109
116
  end
@@ -142,11 +149,10 @@ module ActionDispatch
142
149
  end
143
150
  end
144
151
 
145
- # Resets the instance. This can be used to reset the state information
146
- # in an existing session instance, so it can be used from a clean-slate
147
- # condition.
152
+ # Resets the instance. This can be used to reset the state information in an
153
+ # existing session instance, so it can be used from a clean-slate condition.
148
154
  #
149
- # session.reset!
155
+ # session.reset!
150
156
  def reset!
151
157
  @https = false
152
158
  @controller = @request = @response = nil
@@ -161,62 +167,61 @@ module ActionDispatch
161
167
  "*/*;q=0.5"
162
168
 
163
169
  unless defined? @named_routes_configured
164
- # the helpers are made protected by default--we make them public for
165
- # easier access during testing and troubleshooting.
170
+ # the helpers are made protected by default--we make them public for easier
171
+ # access during testing and troubleshooting.
166
172
  @named_routes_configured = true
167
173
  end
168
174
  end
169
175
 
170
176
  # Specify whether or not the session should mimic a secure HTTPS request.
171
177
  #
172
- # session.https!
173
- # session.https!(false)
178
+ # session.https!
179
+ # session.https!(false)
174
180
  def https!(flag = true)
175
181
  @https = flag
176
182
  end
177
183
 
178
- # Returns +true+ if the session is mimicking a secure HTTPS request.
184
+ # Returns `true` if the session is mimicking a secure HTTPS request.
179
185
  #
180
- # if session.https?
181
- # ...
182
- # end
186
+ # if session.https?
187
+ # ...
188
+ # end
183
189
  def https?
184
190
  @https
185
191
  end
186
192
 
187
193
  # Performs the actual request.
188
194
  #
189
- # - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
190
- # as a symbol.
191
- # - +path+: The URI (as a String) on which you want to perform the
192
- # request.
193
- # - +params+: The HTTP parameters that you want to pass. This may
194
- # be +nil+,
195
- # a Hash, or a String that is appropriately encoded
196
- # (<tt>application/x-www-form-urlencoded</tt> or
197
- # <tt>multipart/form-data</tt>).
198
- # - +headers+: Additional headers to pass, as a Hash. The headers will be
199
- # merged into the Rack env hash.
200
- # - +env+: Additional env to pass, as a Hash. The headers will be
201
- # merged into the Rack env hash.
202
- # - +xhr+: Set to +true+ if you want to make an Ajax request.
203
- # Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
204
- # The headers will be merged into the Rack env hash.
205
- # - +as+: Used for encoding the request with different content type.
206
- # Supports +:json+ by default and will set the appropriate request headers.
207
- # The headers will be merged into the Rack env hash.
195
+ # * `method`: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
196
+ # as a symbol.
197
+ # * `path`: The URI (as a String) on which you want to perform the request.
198
+ # * `params`: The HTTP parameters that you want to pass. This may be `nil`, a
199
+ # Hash, or a String that is appropriately encoded
200
+ # (`application/x-www-form-urlencoded` or `multipart/form-data`).
201
+ # * `headers`: Additional headers to pass, as a Hash. The headers will be
202
+ # merged into the Rack env hash.
203
+ # * `env`: Additional env to pass, as a Hash. The headers will be merged into
204
+ # the Rack env hash.
205
+ # * `xhr`: Set to `true` if you want to make an Ajax request. Adds request
206
+ # headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH. The
207
+ # headers will be merged into the Rack env hash.
208
+ # * `as`: Used for encoding the request with different content type. Supports
209
+ # `:json` by default and will set the appropriate request headers. The
210
+ # headers will be merged into the Rack env hash.
208
211
  #
209
- # This method is rarely used directly. Use +#get+, +#post+, or other standard
210
- # HTTP methods in integration tests. +#process+ is only required when using a
211
- # request method that doesn't have a method defined in the integration tests.
212
+ #
213
+ # This method is rarely used directly. Use RequestHelpers#get,
214
+ # RequestHelpers#post, or other standard HTTP methods in integration tests.
215
+ # `#process` is only required when using a request method that doesn't have a
216
+ # method defined in the integration tests.
212
217
  #
213
218
  # This method returns the response status, after performing the request.
214
- # Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
215
- # then that object's <tt>@response</tt> instance variable will point to a Response object
216
- # which one can use to inspect the details of the response.
219
+ # Furthermore, if this method was called from an ActionDispatch::IntegrationTest
220
+ # object, then that object's `@response` instance variable will point to a
221
+ # Response object which one can use to inspect the details of the response.
217
222
  #
218
223
  # Example:
219
- # process :get, '/author', params: { since: 201501011400 }
224
+ # process :get, '/author', params: { since: 201501011400 }
220
225
  def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
221
226
  request_encoder = RequestEncoder.encoder(as)
222
227
  headers ||= {}
@@ -226,7 +231,7 @@ module ActionDispatch
226
231
  method = :post
227
232
  end
228
233
 
229
- if %r{://}.match?(path)
234
+ if path.include?("://")
230
235
  path = build_expanded_path(path) do |location|
231
236
  https! URI::HTTPS === location if location.scheme
232
237
 
@@ -252,10 +257,13 @@ module ActionDispatch
252
257
  "REQUEST_URI" => path,
253
258
  "HTTP_HOST" => host,
254
259
  "REMOTE_ADDR" => remote_addr,
255
- "CONTENT_TYPE" => request_encoder.content_type,
256
260
  "HTTP_ACCEPT" => request_encoder.accept_header || accept
257
261
  }
258
262
 
263
+ if request_encoder.content_type
264
+ request_env["CONTENT_TYPE"] = request_encoder.content_type
265
+ end
266
+
259
267
  wrapped_headers = Http::Headers.from_hash({})
260
268
  wrapped_headers.merge!(headers) if headers
261
269
 
@@ -274,8 +282,8 @@ module ActionDispatch
274
282
 
275
283
  session = Rack::Test::Session.new(_mock_session)
276
284
 
277
- # NOTE: rack-test v0.5 doesn't build a default uri correctly
278
- # Make sure requested path is always a full URI.
285
+ # NOTE: rack-test v0.5 doesn't build a default uri correctly Make sure requested
286
+ # path is always a full URI.
279
287
  session.request(build_full_uri(path, request_env), request_env)
280
288
 
281
289
  @request_count += 1
@@ -293,7 +301,7 @@ module ActionDispatch
293
301
 
294
302
  # Set the host name to use in the next request.
295
303
  #
296
- # session.host! "www.example.com"
304
+ # session.host! "www.example.com"
297
305
  alias :host! :host=
298
306
 
299
307
  private
@@ -335,16 +343,16 @@ module ActionDispatch
335
343
  @integration_session ||= create_session(app)
336
344
  end
337
345
 
338
- # Reset the current session. This is useful for testing multiple sessions
339
- # in a single test case.
346
+ # Reset the current session. This is useful for testing multiple sessions in a
347
+ # single test case.
340
348
  def reset!
341
349
  @integration_session = create_session(app)
342
350
  end
343
351
 
344
352
  def create_session(app)
345
353
  klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
346
- # If the app is a Rails app, make url_helpers available on the session.
347
- # This makes app.url_for and app.foo_path available in the console.
354
+ # If the app is a Rails app, make url_helpers available on the session. This
355
+ # makes app.url_for and app.foo_path available in the console.
348
356
  if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
349
357
  include app.routes.url_helpers
350
358
  include app.routes.mounted_helpers
@@ -374,16 +382,15 @@ module ActionDispatch
374
382
  RUBY
375
383
  end
376
384
 
377
- # Open a new session instance. If a block is given, the new session is
378
- # yielded to the block before being returned.
385
+ # Open a new session instance. If a block is given, the new session is yielded
386
+ # to the block before being returned.
379
387
  #
380
- # session = open_session do |sess|
381
- # sess.extend(CustomAssertions)
382
- # end
388
+ # session = open_session do |sess|
389
+ # sess.extend(CustomAssertions)
390
+ # end
383
391
  #
384
- # By default, a single session is automatically created for you, but you
385
- # can use this method to open multiple sessions that ought to be tested
386
- # simultaneously.
392
+ # By default, a single session is automatically created for you, but you can use
393
+ # this method to open multiple sessions that ought to be tested simultaneously.
387
394
  def open_session
388
395
  dup.tap do |session|
389
396
  session.reset!
@@ -400,8 +407,8 @@ module ActionDispatch
400
407
  root_session ? root_session.assertions = assertions : super
401
408
  end
402
409
 
403
- # Copy the instance variables from the current session instance into the
404
- # test instance.
410
+ # Copy the instance variables from the current session instance into the test
411
+ # instance.
405
412
  def copy_session_variables! # :nodoc:
406
413
  @controller = @integration_session.controller
407
414
  @response = @integration_session.response
@@ -422,211 +429,213 @@ module ActionDispatch
422
429
  end
423
430
 
424
431
  # Delegate unhandled messages to the current session instance.
425
- def method_missing(method, *args, &block)
432
+ def method_missing(method, ...)
426
433
  if integration_session.respond_to?(method)
427
- integration_session.public_send(method, *args, &block).tap do
434
+ integration_session.public_send(method, ...).tap do
428
435
  copy_session_variables!
429
436
  end
430
437
  else
431
438
  super
432
439
  end
433
440
  end
434
- ruby2_keywords(:method_missing)
435
441
  end
436
442
  end
437
443
 
438
- # An integration test spans multiple controllers and actions,
439
- # tying them all together to ensure they work together as expected. It tests
440
- # more completely than either unit or functional tests do, exercising the
441
- # entire stack, from the dispatcher to the database.
444
+ # An integration test spans multiple controllers and actions, tying them all
445
+ # together to ensure they work together as expected. It tests more completely
446
+ # than either unit or functional tests do, exercising the entire stack, from the
447
+ # dispatcher to the database.
442
448
  #
443
- # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
444
- # using the get/post methods:
449
+ # At its simplest, you simply extend `IntegrationTest` and write your tests
450
+ # using the Integration::RequestHelpers#get and/or
451
+ # Integration::RequestHelpers#post methods:
445
452
  #
446
- # require "test_helper"
453
+ # require "test_helper"
447
454
  #
448
- # class ExampleTest < ActionDispatch::IntegrationTest
449
- # fixtures :people
455
+ # class ExampleTest < ActionDispatch::IntegrationTest
456
+ # fixtures :people
450
457
  #
451
- # def test_login
452
- # # get the login page
453
- # get "/login"
454
- # assert_equal 200, status
458
+ # def test_login
459
+ # # get the login page
460
+ # get "/login"
461
+ # assert_equal 200, status
455
462
  #
456
- # # post the login and follow through to the home page
457
- # post "/login", params: { username: people(:jamis).username,
458
- # password: people(:jamis).password }
459
- # follow_redirect!
460
- # assert_equal 200, status
461
- # assert_equal "/home", path
463
+ # # post the login and follow through to the home page
464
+ # post "/login", params: { username: people(:jamis).username,
465
+ # password: people(:jamis).password }
466
+ # follow_redirect!
467
+ # assert_equal 200, status
468
+ # assert_equal "/home", path
469
+ # end
462
470
  # end
463
- # end
464
- #
465
- # However, you can also have multiple session instances open per test, and
466
- # even extend those instances with assertions and methods to create a very
467
- # powerful testing DSL that is specific for your application. You can even
468
- # reference any named routes you happen to have defined.
469
471
  #
470
- # require "test_helper"
472
+ # However, you can also have multiple session instances open per test, and even
473
+ # extend those instances with assertions and methods to create a very powerful
474
+ # testing DSL that is specific for your application. You can even reference any
475
+ # named routes you happen to have defined.
471
476
  #
472
- # class AdvancedTest < ActionDispatch::IntegrationTest
473
- # fixtures :people, :rooms
477
+ # require "test_helper"
474
478
  #
475
- # def test_login_and_speak
476
- # jamis, david = login(:jamis), login(:david)
477
- # room = rooms(:office)
479
+ # class AdvancedTest < ActionDispatch::IntegrationTest
480
+ # fixtures :people, :rooms
478
481
  #
479
- # jamis.enter(room)
480
- # jamis.speak(room, "anybody home?")
482
+ # def test_login_and_speak
483
+ # jamis, david = login(:jamis), login(:david)
484
+ # room = rooms(:office)
481
485
  #
482
- # david.enter(room)
483
- # david.speak(room, "hello!")
484
- # end
486
+ # jamis.enter(room)
487
+ # jamis.speak(room, "anybody home?")
485
488
  #
486
- # private
489
+ # david.enter(room)
490
+ # david.speak(room, "hello!")
491
+ # end
487
492
  #
488
- # module CustomAssertions
489
- # def enter(room)
490
- # # reference a named route, for maximum internal consistency!
491
- # get(room_url(id: room.id))
492
- # assert(...)
493
- # ...
493
+ # private
494
+ #
495
+ # module CustomAssertions
496
+ # def enter(room)
497
+ # # reference a named route, for maximum internal consistency!
498
+ # get(room_url(id: room.id))
499
+ # assert(...)
500
+ # ...
501
+ # end
502
+ #
503
+ # def speak(room, message)
504
+ # post "/say/#{room.id}", xhr: true, params: { message: message }
505
+ # assert(...)
506
+ # ...
507
+ # end
494
508
  # end
495
509
  #
496
- # def speak(room, message)
497
- # post "/say/#{room.id}", xhr: true, params: { message: message }
498
- # assert(...)
499
- # ...
510
+ # def login(who)
511
+ # open_session do |sess|
512
+ # sess.extend(CustomAssertions)
513
+ # who = people(who)
514
+ # sess.post "/login", params: { username: who.username,
515
+ # password: who.password }
516
+ # assert(...)
517
+ # end
500
518
  # end
501
- # end
502
- #
503
- # def login(who)
504
- # open_session do |sess|
505
- # sess.extend(CustomAssertions)
506
- # who = people(who)
507
- # sess.post "/login", params: { username: who.username,
508
- # password: who.password }
509
- # assert(...)
510
- # end
511
- # end
512
- # end
519
+ # end
513
520
  #
514
521
  # Another longer example would be:
515
522
  #
516
523
  # A simple integration test that exercises multiple controllers:
517
524
  #
518
- # require "test_helper"
525
+ # require "test_helper"
519
526
  #
520
- # class UserFlowsTest < ActionDispatch::IntegrationTest
521
- # test "login and browse site" do
522
- # # login via https
523
- # https!
524
- # get "/login"
525
- # assert_response :success
527
+ # class UserFlowsTest < ActionDispatch::IntegrationTest
528
+ # test "login and browse site" do
529
+ # # login via https
530
+ # https!
531
+ # get "/login"
532
+ # assert_response :success
526
533
  #
527
- # post "/login", params: { username: users(:david).username, password: users(:david).password }
528
- # follow_redirect!
529
- # assert_equal '/welcome', path
530
- # assert_equal 'Welcome david!', flash[:notice]
534
+ # post "/login", params: { username: users(:david).username, password: users(:david).password }
535
+ # follow_redirect!
536
+ # assert_equal '/welcome', path
537
+ # assert_equal 'Welcome david!', flash[:notice]
531
538
  #
532
- # https!(false)
533
- # get "/articles/all"
534
- # assert_response :success
535
- # assert_select 'h1', 'Articles'
539
+ # https!(false)
540
+ # get "/articles/all"
541
+ # assert_response :success
542
+ # assert_select 'h1', 'Articles'
543
+ # end
536
544
  # end
537
- # end
538
545
  #
539
546
  # As you can see the integration test involves multiple controllers and
540
547
  # exercises the entire stack from database to dispatcher. In addition you can
541
- # have multiple session instances open simultaneously in a test and extend
542
- # those instances with assertion methods to create a very powerful testing
543
- # DSL (domain-specific language) just for your application.
548
+ # have multiple session instances open simultaneously in a test and extend those
549
+ # instances with assertion methods to create a very powerful testing DSL
550
+ # (domain-specific language) just for your application.
544
551
  #
545
552
  # Here's an example of multiple sessions and custom DSL in an integration test
546
553
  #
547
- # require "test_helper"
554
+ # require "test_helper"
548
555
  #
549
- # class UserFlowsTest < ActionDispatch::IntegrationTest
550
- # test "login and browse site" do
551
- # # User david logs in
552
- # david = login(:david)
553
- # # User guest logs in
554
- # guest = login(:guest)
556
+ # class UserFlowsTest < ActionDispatch::IntegrationTest
557
+ # test "login and browse site" do
558
+ # # User david logs in
559
+ # david = login(:david)
560
+ # # User guest logs in
561
+ # guest = login(:guest)
555
562
  #
556
- # # Both are now available in different sessions
557
- # assert_equal 'Welcome david!', david.flash[:notice]
558
- # assert_equal 'Welcome guest!', guest.flash[:notice]
563
+ # # Both are now available in different sessions
564
+ # assert_equal 'Welcome david!', david.flash[:notice]
565
+ # assert_equal 'Welcome guest!', guest.flash[:notice]
559
566
  #
560
- # # User david can browse site
561
- # david.browses_site
562
- # # User guest can browse site as well
563
- # guest.browses_site
567
+ # # User david can browse site
568
+ # david.browses_site
569
+ # # User guest can browse site as well
570
+ # guest.browses_site
564
571
  #
565
- # # Continue with other assertions
566
- # end
572
+ # # Continue with other assertions
573
+ # end
567
574
  #
568
- # private
575
+ # private
569
576
  #
570
- # module CustomDsl
571
- # def browses_site
572
- # get "/products/all"
573
- # assert_response :success
574
- # assert_select 'h1', 'Products'
577
+ # module CustomDsl
578
+ # def browses_site
579
+ # get "/products/all"
580
+ # assert_response :success
581
+ # assert_select 'h1', 'Products'
582
+ # end
575
583
  # end
576
- # end
577
584
  #
578
- # def login(user)
579
- # open_session do |sess|
580
- # sess.extend(CustomDsl)
581
- # u = users(user)
582
- # sess.https!
583
- # sess.post "/login", params: { username: u.username, password: u.password }
584
- # assert_equal '/welcome', sess.path
585
- # sess.https!(false)
585
+ # def login(user)
586
+ # open_session do |sess|
587
+ # sess.extend(CustomDsl)
588
+ # u = users(user)
589
+ # sess.https!
590
+ # sess.post "/login", params: { username: u.username, password: u.password }
591
+ # assert_equal '/welcome', sess.path
592
+ # sess.https!(false)
593
+ # end
586
594
  # end
587
- # end
588
- # end
595
+ # end
589
596
  #
590
- # See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
591
- # use +get+, etc.
597
+ # See the [request helpers documentation]
598
+ # (rdoc-ref:ActionDispatch::Integration::RequestHelpers) for help
599
+ # on how to use `get`, etc.
592
600
  #
593
- # === Changing the request encoding
601
+ # ### Changing the request encoding
594
602
  #
595
- # You can also test your JSON API easily by setting what the request should
596
- # be encoded as:
603
+ # You can also test your JSON API easily by setting what the request should be
604
+ # encoded as:
597
605
  #
598
- # require "test_helper"
606
+ # require "test_helper"
599
607
  #
600
- # class ApiTest < ActionDispatch::IntegrationTest
601
- # test "creates articles" do
602
- # assert_difference -> { Article.count } do
603
- # post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
604
- # end
608
+ # class ApiTest < ActionDispatch::IntegrationTest
609
+ # test "creates articles" do
610
+ # assert_difference -> { Article.count } do
611
+ # post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
612
+ # end
605
613
  #
606
- # assert_response :success
607
- # assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
614
+ # assert_response :success
615
+ # assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
616
+ # end
608
617
  # end
609
- # end
610
618
  #
611
- # The +as+ option passes an "application/json" Accept header (thereby setting
619
+ # The `as` option passes an "application/json" Accept header (thereby setting
612
620
  # the request format to JSON unless overridden), sets the content type to
613
621
  # "application/json" and encodes the parameters as JSON.
614
622
  #
615
- # Calling +parsed_body+ on the response parses the response body based on the
616
- # last response MIME type.
623
+ # Calling TestResponse#parsed_body on the response parses the response body
624
+ # based on the last response MIME type.
617
625
  #
618
- # Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
619
- # types you've registered, you can add your own encoders with:
626
+ # Out of the box, only `:json` is supported. But for any custom MIME types
627
+ # you've registered, you can add your own encoders with:
620
628
  #
621
- # ActionDispatch::IntegrationTest.register_encoder :wibble,
622
- # param_encoder: -> params { params.to_wibble },
623
- # response_parser: -> body { body }
629
+ # ActionDispatch::IntegrationTest.register_encoder :wibble,
630
+ # param_encoder: -> params { params.to_wibble },
631
+ # response_parser: -> body { body }
624
632
  #
625
- # Where +param_encoder+ defines how the params should be encoded and
626
- # +response_parser+ defines how the response body should be parsed through
627
- # +parsed_body+.
633
+ # Where `param_encoder` defines how the params should be encoded and
634
+ # `response_parser` defines how the response body should be parsed through
635
+ # TestResponse#parsed_body.
628
636
  #
629
- # Consult the Rails Testing Guide for more.
637
+ # Consult the [Rails Testing Guide](https://guides.rubyonrails.org/testing.html)
638
+ # for more.
630
639
 
631
640
  class IntegrationTest < ActiveSupport::TestCase
632
641
  include TestProcess::FixtureFile
@@ -643,10 +652,12 @@ module ActionDispatch
643
652
 
644
653
  include Integration::Runner
645
654
  include ActionController::TemplateAssertions
655
+ include TestHelpers::PageDumpHelper
646
656
 
647
657
  included do
648
658
  include ActionDispatch::Routing::UrlFor
649
659
  include UrlOptions # don't let UrlFor override the url_options method
660
+ include ActionDispatch::Assertions::RoutingAssertions::WithIntegrationRouting
650
661
  ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
651
662
  @@app = nil
652
663
  end