actionpack 4.2.10 → 7.2.0.rc1

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.

Files changed (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +425 -497
  65. data/lib/action_controller.rb +44 -22
  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 +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,152 +1,101 @@
1
- require 'stringio'
2
- require 'uri'
3
- require 'active_support/core_ext/kernel/singleton_class'
4
- require 'active_support/core_ext/object/try'
5
- require 'rack/test'
6
- require 'minitest'
1
+ # frozen_string_literal: true
7
2
 
8
- module ActionDispatch
9
- module Integration #:nodoc:
10
- module RequestHelpers
11
- # Performs a GET request with the given parameters.
12
- #
13
- # - +path+: The URI (as a String) on which you want to perform a GET
14
- # request.
15
- # - +parameters+: The HTTP parameters that you want to pass. This may
16
- # be +nil+,
17
- # a Hash, or a String that is appropriately encoded
18
- # (<tt>application/x-www-form-urlencoded</tt> or
19
- # <tt>multipart/form-data</tt>).
20
- # - +headers_or_env+: Additional headers to pass, as a Hash. The headers will be
21
- # merged into the Rack env hash.
22
- #
23
- # This method returns a Response object, which one can use to
24
- # inspect the details of the response. Furthermore, if this method was
25
- # called from an ActionDispatch::IntegrationTest object, then that
26
- # object's <tt>@response</tt> instance variable will point to the same
27
- # response object.
28
- #
29
- # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
30
- # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
31
- def get(path, parameters = nil, headers_or_env = nil)
32
- process :get, path, parameters, headers_or_env
33
- end
3
+ # :markup: markdown
34
4
 
35
- # Performs a POST request with the given parameters. See +#get+ for more
36
- # details.
37
- def post(path, parameters = nil, headers_or_env = nil)
38
- process :post, path, parameters, headers_or_env
39
- end
5
+ require "stringio"
6
+ require "uri"
7
+ require "rack/test"
8
+ require "active_support/test_case"
40
9
 
41
- # Performs a PATCH request with the given parameters. See +#get+ for more
42
- # details.
43
- def patch(path, parameters = nil, headers_or_env = nil)
44
- process :patch, path, parameters, headers_or_env
45
- end
10
+ require "action_dispatch/testing/request_encoder"
11
+ require "action_dispatch/testing/test_helpers/page_dump_helper"
46
12
 
47
- # Performs a PUT request with the given parameters. See +#get+ for more
48
- # details.
49
- def put(path, parameters = nil, headers_or_env = nil)
50
- process :put, path, parameters, headers_or_env
13
+ module ActionDispatch
14
+ module Integration # :nodoc:
15
+ module RequestHelpers
16
+ # Performs a GET request with the given parameters. See
17
+ # ActionDispatch::Integration::Session#process for more details.
18
+ def get(path, **args)
19
+ process(:get, path, **args)
51
20
  end
52
21
 
53
- # Performs a DELETE request with the given parameters. See +#get+ for
54
- # more details.
55
- def delete(path, parameters = nil, headers_or_env = nil)
56
- process :delete, path, parameters, headers_or_env
22
+ # Performs a POST request with the given parameters. See
23
+ # ActionDispatch::Integration::Session#process for more details.
24
+ def post(path, **args)
25
+ process(:post, path, **args)
57
26
  end
58
27
 
59
- # Performs a HEAD request with the given parameters. See +#get+ for more
60
- # details.
61
- def head(path, parameters = nil, headers_or_env = nil)
62
- process :head, path, parameters, headers_or_env
28
+ # Performs a PATCH request with the given parameters. See
29
+ # ActionDispatch::Integration::Session#process for more details.
30
+ def patch(path, **args)
31
+ process(:patch, path, **args)
63
32
  end
64
33
 
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
- # Follow a single redirect response. If the last response was not a
80
- # redirect, an exception will be raised. Otherwise, the redirect is
81
- # performed on the location header.
82
- def follow_redirect!
83
- raise "not a redirect! #{status} #{status_message}" unless redirect?
84
- get(response.location)
85
- status
34
+ # Performs a PUT request with the given parameters. See
35
+ # ActionDispatch::Integration::Session#process for more details.
36
+ def put(path, **args)
37
+ process(:put, path, **args)
86
38
  end
87
39
 
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
40
+ # Performs a DELETE request with the given parameters. See
41
+ # ActionDispatch::Integration::Session#process for more details.
42
+ def delete(path, **args)
43
+ process(:delete, path, **args)
96
44
  end
97
45
 
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)
46
+ # Performs a HEAD request with the given parameters. See
47
+ # ActionDispatch::Integration::Session#process for more details.
48
+ def head(path, **args)
49
+ process(:head, path, **args)
102
50
  end
103
51
 
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)
52
+ # Performs an OPTIONS request with the given parameters. See
53
+ # ActionDispatch::Integration::Session#process for more details.
54
+ def options(path, **args)
55
+ process(:options, path, **args)
108
56
  end
109
57
 
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
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)
66
+ raise "not a redirect! #{status} #{status_message}" unless redirect?
115
67
 
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
68
+ method =
69
+ if [307, 308].include?(response.status)
70
+ request.method.downcase
71
+ else
72
+ :get
73
+ end
121
74
 
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)
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)
80
+ status
126
81
  end
127
82
  end
128
83
 
129
- # An instance of this class represents a set of requests and responses
130
- # performed sequentially by a test process. Because you can instantiate
131
- # multiple sessions and run them side-by-side, you can also mimic (to some
132
- # 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.
133
88
  #
134
- # Typically, you will instantiate a new session using
135
- # IntegrationTest#open_session, rather than instantiating
136
- # Integration::Session directly.
89
+ # Typically, you will instantiate a new session using Runner#open_session,
90
+ # rather than instantiating a Session directly.
137
91
  class Session
138
92
  DEFAULT_HOST = "www.example.com"
139
93
 
140
94
  include Minitest::Assertions
141
95
  include TestProcess, RequestHelpers, Assertions
142
96
 
143
- %w( status status_message headers body redirect? ).each do |method|
144
- delegate method, :to => :response, :allow_nil => true
145
- end
146
-
147
- %w( path ).each do |method|
148
- delegate method, :to => :request, :allow_nil => true
149
- end
97
+ delegate :status, :status_message, :headers, :body, :redirect?, to: :response, allow_nil: true
98
+ delegate :path, to: :request, allow_nil: true
150
99
 
151
100
  # The hostname used in the last request.
152
101
  def host
@@ -160,8 +109,8 @@ module ActionDispatch
160
109
  # The Accept header to send.
161
110
  attr_accessor :accept
162
111
 
163
- # A map of the cookies returned by the last response, and which will be
164
- # 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.
165
114
  def cookies
166
115
  _mock_session.cookie_jar
167
116
  end
@@ -185,35 +134,25 @@ module ActionDispatch
185
134
  super()
186
135
  @app = app
187
136
 
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
137
  reset!
198
138
  end
199
139
 
200
140
  def url_options
201
141
  @url_options ||= default_url_options.dup.tap do |url_options|
202
- url_options.reverse_merge!(controller.url_options) if controller
142
+ url_options.reverse_merge!(controller.url_options) if controller.respond_to?(:url_options)
203
143
 
204
144
  if @app.respond_to?(:routes)
205
145
  url_options.reverse_merge!(@app.routes.default_url_options)
206
146
  end
207
147
 
208
- url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
148
+ url_options.reverse_merge!(host: host, protocol: https? ? "https" : "http")
209
149
  end
210
150
  end
211
151
 
212
- # Resets the instance. This can be used to reset the state information
213
- # in an existing session instance, so it can be used from a clean-slate
214
- # 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.
215
154
  #
216
- # session.reset!
155
+ # session.reset!
217
156
  def reset!
218
157
  @https = false
219
158
  @controller = @request = @response = nil
@@ -223,292 +162,533 @@ module ActionDispatch
223
162
 
224
163
  self.host = DEFAULT_HOST
225
164
  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," +
165
+ self.accept = "text/xml,application/xml,application/xhtml+xml," \
166
+ "text/html;q=0.9,text/plain;q=0.8,image/png," \
228
167
  "*/*;q=0.5"
229
168
 
230
169
  unless defined? @named_routes_configured
231
- # the helpers are made protected by default--we make them public for
232
- # 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.
233
172
  @named_routes_configured = true
234
173
  end
235
174
  end
236
175
 
237
176
  # Specify whether or not the session should mimic a secure HTTPS request.
238
177
  #
239
- # session.https!
240
- # session.https!(false)
178
+ # session.https!
179
+ # session.https!(false)
241
180
  def https!(flag = true)
242
181
  @https = flag
243
182
  end
244
183
 
245
- # Returns +true+ if the session is mimicking a secure HTTPS request.
184
+ # Returns `true` if the session is mimicking a secure HTTPS request.
246
185
  #
247
- # if session.https?
248
- # ...
249
- # end
186
+ # if session.https?
187
+ # ...
188
+ # end
250
189
  def https?
251
190
  @https
252
191
  end
253
192
 
254
- # Set the host name to use in the next request.
193
+ # Performs the actual request.
255
194
  #
256
- # session.host! "www.example.com"
257
- alias :host! :host=
258
-
259
- private
260
- def _mock_session
261
- @_mock_session ||= Rack::MockSession.new(@app, host)
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
+ #
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.
217
+ #
218
+ # This method returns the response status, after performing the request.
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.
222
+ #
223
+ # Example:
224
+ # process :get, '/author', params: { since: 201501011400 }
225
+ def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
226
+ request_encoder = RequestEncoder.encoder(as)
227
+ headers ||= {}
228
+
229
+ if method == :get && as == :json && params
230
+ headers["X-Http-Method-Override"] = "GET"
231
+ method = :post
262
232
  end
263
233
 
264
- # Performs the actual request.
265
- def process(method, path, parameters = nil, headers_or_env = nil)
266
- if path =~ %r{://}
267
- location = URI.parse(path)
234
+ if path.include?("://")
235
+ path = build_expanded_path(path) do |location|
268
236
  https! URI::HTTPS === location if location.scheme
269
- host! "#{location.host}:#{location.port}" if location.host
270
- path = location.query ? "#{location.path}?#{location.query}" : location.path
237
+
238
+ if url_host = location.host
239
+ default = Rack::Request::DEFAULT_PORTS[location.scheme]
240
+ url_host += ":#{location.port}" if default != location.port
241
+ host! url_host
242
+ end
271
243
  end
244
+ end
245
+
246
+ hostname, port = host.split(":")
247
+
248
+ request_env = {
249
+ :method => method,
250
+ :params => request_encoder.encode_params(params),
251
+
252
+ "SERVER_NAME" => hostname,
253
+ "SERVER_PORT" => port || (https? ? "443" : "80"),
254
+ "HTTPS" => https? ? "on" : "off",
255
+ "rack.url_scheme" => https? ? "https" : "http",
256
+
257
+ "REQUEST_URI" => path,
258
+ "HTTP_HOST" => host,
259
+ "REMOTE_ADDR" => remote_addr,
260
+ "HTTP_ACCEPT" => request_encoder.accept_header || accept
261
+ }
272
262
 
273
- hostname, port = host.split(':')
263
+ if request_encoder.content_type
264
+ request_env["CONTENT_TYPE"] = request_encoder.content_type
265
+ end
274
266
 
275
- env = {
276
- :method => method,
277
- :params => parameters,
267
+ wrapped_headers = Http::Headers.from_hash({})
268
+ wrapped_headers.merge!(headers) if headers
278
269
 
279
- "SERVER_NAME" => hostname,
280
- "SERVER_PORT" => port || (https? ? "443" : "80"),
281
- "HTTPS" => https? ? "on" : "off",
282
- "rack.url_scheme" => https? ? "https" : "http",
270
+ if xhr
271
+ wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
272
+ wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
273
+ end
283
274
 
284
- "REQUEST_URI" => path,
285
- "HTTP_HOST" => host,
286
- "REMOTE_ADDR" => remote_addr,
287
- "CONTENT_TYPE" => "application/x-www-form-urlencoded",
288
- "HTTP_ACCEPT" => accept
289
- }
290
- # this modifies the passed env directly
291
- Http::Headers.new(env).merge!(headers_or_env || {})
275
+ # This modifies the passed request_env directly.
276
+ if wrapped_headers.present?
277
+ Http::Headers.from_hash(request_env).merge!(wrapped_headers)
278
+ end
279
+ if env.present?
280
+ Http::Headers.from_hash(request_env).merge!(env)
281
+ end
292
282
 
293
- session = Rack::Test::Session.new(_mock_session)
283
+ session = Rack::Test::Session.new(_mock_session)
294
284
 
295
- # NOTE: rack-test v0.5 doesn't build a default uri correctly
296
- # Make sure requested path is always a full uri
297
- session.request(build_full_uri(path, env), env)
285
+ # NOTE: rack-test v0.5 doesn't build a default uri correctly Make sure requested
286
+ # path is always a full URI.
287
+ session.request(build_full_uri(path, request_env), request_env)
298
288
 
299
- @request_count += 1
300
- @request = ActionDispatch::Request.new(session.last_request.env)
301
- response = _mock_session.last_response
302
- @response = ActionDispatch::TestResponse.from_response(response)
303
- @html_document = nil
304
- @html_scanner_document = nil
305
- @url_options = nil
289
+ @request_count += 1
290
+ @request = ActionDispatch::Request.new(session.last_request.env)
291
+ response = _mock_session.last_response
292
+ @response = ActionDispatch::TestResponse.from_response(response)
293
+ @response.request = @request
294
+ @html_document = nil
295
+ @url_options = nil
306
296
 
307
- @controller = session.last_request.env['action_controller.instance']
297
+ @controller = @request.controller_instance
298
+
299
+ response.status
300
+ end
301
+
302
+ # Set the host name to use in the next request.
303
+ #
304
+ # session.host! "www.example.com"
305
+ alias :host! :host=
308
306
 
309
- return response.status
307
+ private
308
+ def _mock_session
309
+ @_mock_session ||= Rack::MockSession.new(@app, host)
310
310
  end
311
311
 
312
312
  def build_full_uri(path, env)
313
313
  "#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
314
314
  end
315
+
316
+ def build_expanded_path(path)
317
+ location = URI.parse(path)
318
+ yield location if block_given?
319
+ path = location.path
320
+ location.query ? "#{path}?#{location.query}" : path
321
+ end
315
322
  end
316
323
 
317
324
  module Runner
318
325
  include ActionDispatch::Assertions
319
326
 
320
- def app
321
- @app ||= nil
327
+ APP_SESSIONS = {}
328
+
329
+ attr_reader :app
330
+ attr_accessor :root_session # :nodoc:
331
+
332
+ def initialize(*args, &blk)
333
+ super(*args, &blk)
334
+ @integration_session = nil
322
335
  end
323
336
 
324
- # Reset the current session. This is useful for testing multiple sessions
325
- # in a single test case.
337
+ def before_setup # :nodoc:
338
+ @app = nil
339
+ super
340
+ end
341
+
342
+ def integration_session
343
+ @integration_session ||= create_session(app)
344
+ end
345
+
346
+ # Reset the current session. This is useful for testing multiple sessions in a
347
+ # single test case.
326
348
  def reset!
327
- @integration_session = Integration::Session.new(app)
349
+ @integration_session = create_session(app)
350
+ end
351
+
352
+ def create_session(app)
353
+ klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
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.
356
+ if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
357
+ include app.routes.url_helpers
358
+ include app.routes.mounted_helpers
359
+ end
360
+ }
361
+ klass.new(app)
328
362
  end
329
363
 
330
364
  def remove! # :nodoc:
331
365
  @integration_session = nil
332
366
  end
333
367
 
334
- %w(get post patch put head delete cookies assigns
335
- xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
336
- define_method(method) do |*args|
337
- reset! unless integration_session
368
+ %w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
369
+ # reset the html_document variable, except for cookies/assigns calls
370
+ unless method == "cookies" || method == "assigns"
371
+ reset_html_document = "@html_document = nil"
372
+ end
338
373
 
339
- # reset the html_document variable, except for cookies/assigns calls
340
- unless method == 'cookies' || method == 'assigns'
341
- @html_document = nil
342
- @html_scanner_document = nil
343
- reset_template_assertion
344
- end
374
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
375
+ def #{method}(...)
376
+ #{reset_html_document}
345
377
 
346
- integration_session.__send__(method, *args).tap do
378
+ result = integration_session.#{method}(...)
347
379
  copy_session_variables!
380
+ result
348
381
  end
349
- end
382
+ RUBY
350
383
  end
351
384
 
352
- # Open a new session instance. If a block is given, the new session is
353
- # 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.
354
387
  #
355
- # session = open_session do |sess|
356
- # sess.extend(CustomAssertions)
357
- # end
388
+ # session = open_session do |sess|
389
+ # sess.extend(CustomAssertions)
390
+ # end
358
391
  #
359
- # By default, a single session is automatically created for you, but you
360
- # can use this method to open multiple sessions that ought to be tested
361
- # 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.
362
394
  def open_session
363
395
  dup.tap do |session|
364
396
  session.reset!
397
+ session.root_session = self.root_session || self
365
398
  yield session if block_given?
366
399
  end
367
400
  end
368
401
 
369
- # Copy the instance variables from the current session instance into the
370
- # test instance.
371
- def copy_session_variables! #:nodoc:
372
- return unless integration_session
373
- %w(controller response request).each do |var|
374
- instance_variable_set("@#{var}", @integration_session.__send__(var))
375
- end
402
+ def assertions # :nodoc:
403
+ root_session ? root_session.assertions : super
404
+ end
405
+
406
+ def assertions=(assertions) # :nodoc:
407
+ root_session ? root_session.assertions = assertions : super
408
+ end
409
+
410
+ # Copy the instance variables from the current session instance into the test
411
+ # instance.
412
+ def copy_session_variables! # :nodoc:
413
+ @controller = @integration_session.controller
414
+ @response = @integration_session.response
415
+ @request = @integration_session.request
376
416
  end
377
417
 
378
418
  def default_url_options
379
- reset! unless integration_session
380
419
  integration_session.default_url_options
381
420
  end
382
421
 
383
422
  def default_url_options=(options)
384
- reset! unless integration_session
385
423
  integration_session.default_url_options = options
386
424
  end
387
425
 
388
- def respond_to?(method, include_private = false)
389
- integration_session.respond_to?(method, include_private) || super
426
+ private
427
+ def respond_to_missing?(method, _)
428
+ integration_session.respond_to?(method) || super
390
429
  end
391
430
 
392
431
  # Delegate unhandled messages to the current session instance.
393
- def method_missing(sym, *args, &block)
394
- reset! unless integration_session
395
- if integration_session.respond_to?(sym)
396
- integration_session.__send__(sym, *args, &block).tap do
432
+ def method_missing(method, ...)
433
+ if integration_session.respond_to?(method)
434
+ integration_session.public_send(method, ...).tap do
397
435
  copy_session_variables!
398
436
  end
399
437
  else
400
438
  super
401
439
  end
402
440
  end
403
-
404
- private
405
- def integration_session
406
- @integration_session ||= nil
407
- end
408
441
  end
409
442
  end
410
443
 
411
- # An integration test spans multiple controllers and actions,
412
- # tying them all together to ensure they work together as expected. It tests
413
- # more completely than either unit or functional tests do, exercising the
414
- # 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.
415
448
  #
416
- # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
417
- # 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:
418
452
  #
419
- # require "test_helper"
453
+ # require "test_helper"
420
454
  #
421
- # class ExampleTest < ActionDispatch::IntegrationTest
422
- # fixtures :people
455
+ # class ExampleTest < ActionDispatch::IntegrationTest
456
+ # fixtures :people
423
457
  #
424
- # def test_login
425
- # # get the login page
426
- # get "/login"
427
- # assert_equal 200, status
458
+ # def test_login
459
+ # # get the login page
460
+ # get "/login"
461
+ # assert_equal 200, status
428
462
  #
429
- # # post the login and follow through to the home page
430
- # post "/login", username: people(:jamis).username,
431
- # password: people(:jamis).password
432
- # follow_redirect!
433
- # assert_equal 200, status
434
- # 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
435
470
  # end
436
- # end
437
471
  #
438
- # However, you can also have multiple session instances open per test, and
439
- # even extend those instances with assertions and methods to create a very
440
- # powerful testing DSL that is specific for your application. You can even
441
- # reference any named routes you happen to have defined.
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.
442
476
  #
443
- # require "test_helper"
477
+ # require "test_helper"
444
478
  #
445
- # class AdvancedTest < ActionDispatch::IntegrationTest
446
- # fixtures :people, :rooms
479
+ # class AdvancedTest < ActionDispatch::IntegrationTest
480
+ # fixtures :people, :rooms
447
481
  #
448
- # def test_login_and_speak
449
- # jamis, david = login(:jamis), login(:david)
450
- # room = rooms(:office)
482
+ # def test_login_and_speak
483
+ # jamis, david = login(:jamis), login(:david)
484
+ # room = rooms(:office)
451
485
  #
452
- # jamis.enter(room)
453
- # jamis.speak(room, "anybody home?")
486
+ # jamis.enter(room)
487
+ # jamis.speak(room, "anybody home?")
454
488
  #
455
- # david.enter(room)
456
- # david.speak(room, "hello!")
457
- # end
489
+ # david.enter(room)
490
+ # david.speak(room, "hello!")
491
+ # end
458
492
  #
459
- # private
493
+ # private
460
494
  #
461
- # module CustomAssertions
462
- # def enter(room)
463
- # # reference a named route, for maximum internal consistency!
464
- # get(room_url(id: room.id))
465
- # assert(...)
466
- # ...
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
467
508
  # end
468
509
  #
469
- # def speak(room, message)
470
- # xml_http_request "/say/#{room.id}", message: message
471
- # assert(...)
472
- # ...
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
473
518
  # end
519
+ # end
520
+ #
521
+ # Another longer example would be:
522
+ #
523
+ # A simple integration test that exercises multiple controllers:
524
+ #
525
+ # require "test_helper"
526
+ #
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
533
+ #
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]
538
+ #
539
+ # https!(false)
540
+ # get "/articles/all"
541
+ # assert_response :success
542
+ # assert_select 'h1', 'Articles'
543
+ # end
544
+ # end
545
+ #
546
+ # As you can see the integration test involves multiple controllers and
547
+ # exercises the entire stack from database to dispatcher. In addition you can
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.
551
+ #
552
+ # Here's an example of multiple sessions and custom DSL in an integration test
553
+ #
554
+ # require "test_helper"
555
+ #
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)
562
+ #
563
+ # # Both are now available in different sessions
564
+ # assert_equal 'Welcome david!', david.flash[:notice]
565
+ # assert_equal 'Welcome guest!', guest.flash[:notice]
566
+ #
567
+ # # User david can browse site
568
+ # david.browses_site
569
+ # # User guest can browse site as well
570
+ # guest.browses_site
571
+ #
572
+ # # Continue with other assertions
474
573
  # end
475
574
  #
476
- # def login(who)
477
- # open_session do |sess|
478
- # sess.extend(CustomAssertions)
479
- # who = people(who)
480
- # sess.post "/login", username: who.username,
481
- # password: who.password
482
- # assert(...)
575
+ # private
576
+ #
577
+ # module CustomDsl
578
+ # def browses_site
579
+ # get "/products/all"
580
+ # assert_response :success
581
+ # assert_select 'h1', 'Products'
582
+ # end
583
+ # end
584
+ #
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
483
594
  # end
595
+ # end
596
+ #
597
+ # See the [request helpers documentation]
598
+ # (rdoc-ref:ActionDispatch::Integration::RequestHelpers) for help
599
+ # on how to use `get`, etc.
600
+ #
601
+ # ### Changing the request encoding
602
+ #
603
+ # You can also test your JSON API easily by setting what the request should be
604
+ # encoded as:
605
+ #
606
+ # require "test_helper"
607
+ #
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
613
+ #
614
+ # assert_response :success
615
+ # assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
484
616
  # end
485
- # end
486
- class IntegrationTest < ActiveSupport::TestCase
487
- include Integration::Runner
488
- include ActionController::TemplateAssertions
489
- include ActionDispatch::Routing::UrlFor
617
+ # end
618
+ #
619
+ # The `as` option passes an "application/json" Accept header (thereby setting
620
+ # the request format to JSON unless overridden), sets the content type to
621
+ # "application/json" and encodes the parameters as JSON.
622
+ #
623
+ # Calling TestResponse#parsed_body on the response parses the response body
624
+ # based on the last response MIME type.
625
+ #
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:
628
+ #
629
+ # ActionDispatch::IntegrationTest.register_encoder :wibble,
630
+ # param_encoder: -> params { params.to_wibble },
631
+ # response_parser: -> body { body }
632
+ #
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.
636
+ #
637
+ # Consult the [Rails Testing Guide](https://guides.rubyonrails.org/testing.html)
638
+ # for more.
490
639
 
491
- @@app = nil
640
+ class IntegrationTest < ActiveSupport::TestCase
641
+ include TestProcess::FixtureFile
492
642
 
493
- def self.app
494
- @@app || ActionDispatch.test_app
643
+ module UrlOptions
644
+ extend ActiveSupport::Concern
645
+ def url_options
646
+ integration_session.url_options
647
+ end
495
648
  end
496
649
 
497
- def self.app=(app)
498
- @@app = app
499
- end
650
+ module Behavior
651
+ extend ActiveSupport::Concern
500
652
 
501
- def app
502
- super || self.class.app
503
- end
653
+ include Integration::Runner
654
+ include ActionController::TemplateAssertions
655
+ include TestHelpers::PageDumpHelper
504
656
 
505
- def url_options
506
- reset! unless integration_session
507
- integration_session.url_options
508
- end
657
+ included do
658
+ include ActionDispatch::Routing::UrlFor
659
+ include UrlOptions # don't let UrlFor override the url_options method
660
+ include ActionDispatch::Assertions::RoutingAssertions::WithIntegrationRouting
661
+ ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
662
+ @@app = nil
663
+ end
664
+
665
+ module ClassMethods
666
+ def app
667
+ if defined?(@@app) && @@app
668
+ @@app
669
+ else
670
+ ActionDispatch.test_app
671
+ end
672
+ end
673
+
674
+ def app=(app)
675
+ @@app = app
676
+ end
509
677
 
510
- def document_root_element
511
- html_document.root
678
+ def register_encoder(*args, **options)
679
+ RequestEncoder.register_encoder(*args, **options)
680
+ end
681
+ end
682
+
683
+ def app
684
+ super || self.class.app
685
+ end
686
+
687
+ def document_root_element
688
+ html_document.root
689
+ end
512
690
  end
691
+
692
+ include Behavior
513
693
  end
514
694
  end