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