omg-actionpack 8.0.0.alpha1

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 (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  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 +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  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 +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  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 +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  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 +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  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 +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  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 +691 -0
  65. data/lib/action_controller.rb +80 -0
  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 +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  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 +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  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 +284 -0
  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 +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,691 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "rack/session/abstract/id"
6
+ require "active_support/core_ext/hash/conversions"
7
+ require "active_support/core_ext/object/to_query"
8
+ require "active_support/core_ext/module/anonymous"
9
+ require "active_support/core_ext/module/redefine_method"
10
+ require "active_support/core_ext/hash/keys"
11
+ require "active_support/testing/constant_lookup"
12
+ require "action_controller/template_assertions"
13
+ require "rails-dom-testing"
14
+
15
+ module ActionController
16
+ class Metal
17
+ include Testing::Functional
18
+ end
19
+
20
+ module Live
21
+ # Disable controller / rendering threads in tests. User tests can access the
22
+ # database on the main thread, so they could open a txn, then the controller
23
+ # thread will open a new connection and try to access data that's only visible
24
+ # to the main thread's txn. This is the problem in #23483.
25
+ alias_method :original_new_controller_thread, :new_controller_thread
26
+
27
+ silence_redefinition_of_method :new_controller_thread
28
+ def new_controller_thread # :nodoc:
29
+ yield
30
+ end
31
+
32
+ # Avoid a deadlock from the queue filling up
33
+ Buffer.queue_size = nil
34
+ end
35
+
36
+ # ActionController::TestCase will be deprecated and moved to a gem in the
37
+ # future. Please use ActionDispatch::IntegrationTest going forward.
38
+ class TestRequest < ActionDispatch::TestRequest # :nodoc:
39
+ DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
40
+ DEFAULT_ENV.delete "PATH_INFO"
41
+
42
+ def self.new_session
43
+ TestSession.new
44
+ end
45
+
46
+ attr_reader :controller_class
47
+
48
+ # Create a new test request with default `env` values.
49
+ def self.create(controller_class)
50
+ env = {}
51
+ env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
52
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
53
+ new(default_env.merge(env), new_session, controller_class)
54
+ end
55
+
56
+ def self.default_env
57
+ DEFAULT_ENV
58
+ end
59
+ private_class_method :default_env
60
+
61
+ def initialize(env, session, controller_class)
62
+ super(env)
63
+
64
+ self.session = session
65
+ self.session_options = TestSession::DEFAULT_OPTIONS.dup
66
+ @controller_class = controller_class
67
+ @custom_param_parsers = {
68
+ xml: lambda { |raw_post| Hash.from_xml(raw_post)["hash"] }
69
+ }
70
+ end
71
+
72
+ def query_string=(string)
73
+ set_header Rack::QUERY_STRING, string
74
+ end
75
+
76
+ def content_type=(type)
77
+ set_header "CONTENT_TYPE", type
78
+ end
79
+
80
+ def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
81
+ non_path_parameters = {}
82
+ path_parameters = {}
83
+
84
+ parameters.each do |key, value|
85
+ if query_string_keys.include?(key)
86
+ non_path_parameters[key] = value
87
+ else
88
+ if value.is_a?(Array)
89
+ value = value.map(&:to_param)
90
+ else
91
+ value = value.to_param
92
+ end
93
+
94
+ path_parameters[key.to_sym] = value
95
+ end
96
+ end
97
+
98
+ if get?
99
+ if query_string.blank?
100
+ self.query_string = non_path_parameters.to_query
101
+ end
102
+ else
103
+ if ENCODER.should_multipart?(non_path_parameters)
104
+ self.content_type = ENCODER.content_type
105
+ data = ENCODER.build_multipart non_path_parameters
106
+ else
107
+ fetch_header("CONTENT_TYPE") do |k|
108
+ set_header k, "application/x-www-form-urlencoded"
109
+ end
110
+
111
+ case content_mime_type.to_sym
112
+ when nil
113
+ raise "Unknown Content-Type: #{content_type}"
114
+ when :json
115
+ data = ActiveSupport::JSON.encode(non_path_parameters)
116
+ when :xml
117
+ data = non_path_parameters.to_xml
118
+ when :url_encoded_form
119
+ data = non_path_parameters.to_query
120
+ else
121
+ @custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
122
+ data = non_path_parameters.to_query
123
+ end
124
+ end
125
+
126
+ data_stream = StringIO.new(data)
127
+ set_header "CONTENT_LENGTH", data_stream.length.to_s
128
+ set_header "rack.input", data_stream
129
+ end
130
+
131
+ fetch_header("PATH_INFO") do |k|
132
+ set_header k, generated_path
133
+ end
134
+ fetch_header("ORIGINAL_FULLPATH") do |k|
135
+ set_header k, fullpath
136
+ end
137
+ path_parameters[:controller] = controller_path
138
+ path_parameters[:action] = action
139
+
140
+ self.path_parameters = path_parameters
141
+ end
142
+
143
+ ENCODER = Class.new do
144
+ include Rack::Test::Utils
145
+
146
+ def should_multipart?(params)
147
+ # FIXME: lifted from Rack-Test. We should push this separation upstream.
148
+ multipart = false
149
+ query = lambda { |value|
150
+ case value
151
+ when Array
152
+ value.each(&query)
153
+ when Hash
154
+ value.values.each(&query)
155
+ when Rack::Test::UploadedFile
156
+ multipart = true
157
+ end
158
+ }
159
+ params.values.each(&query)
160
+ multipart
161
+ end
162
+
163
+ public :build_multipart
164
+
165
+ def content_type
166
+ "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
167
+ end
168
+ end.new
169
+
170
+ private
171
+ def params_parsers
172
+ super.merge @custom_param_parsers
173
+ end
174
+ end
175
+
176
+ class LiveTestResponse < Live::Response
177
+ # Was the response successful?
178
+ alias_method :success?, :successful?
179
+
180
+ # Was the URL not found?
181
+ alias_method :missing?, :not_found?
182
+
183
+ # Was there a server-side error?
184
+ alias_method :error?, :server_error?
185
+ end
186
+
187
+ # Methods #destroy and #load! are overridden to avoid calling methods on the
188
+ # @store object, which does not exist for the TestSession class.
189
+ class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash # :nodoc:
190
+ DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
191
+
192
+ def initialize(session = {}, id = Rack::Session::SessionId.new(SecureRandom.hex(16)))
193
+ super(nil, nil)
194
+ @id = id
195
+ @data = stringify_keys(session)
196
+ @loaded = true
197
+ @initially_empty = @data.empty?
198
+ end
199
+
200
+ def exists?
201
+ true
202
+ end
203
+
204
+ def keys
205
+ @data.keys
206
+ end
207
+
208
+ def values
209
+ @data.values
210
+ end
211
+
212
+ def destroy
213
+ clear
214
+ end
215
+
216
+ def dig(*keys)
217
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
218
+ @data.dig(*keys)
219
+ end
220
+
221
+ def fetch(key, *args, &block)
222
+ @data.fetch(key.to_s, *args, &block)
223
+ end
224
+
225
+ def enabled?
226
+ true
227
+ end
228
+
229
+ def id_was
230
+ @id
231
+ end
232
+
233
+ private
234
+ def load!
235
+ @id
236
+ end
237
+ end
238
+
239
+ # # Action Controller Test Case
240
+ #
241
+ # Superclass for ActionController functional tests. Functional tests allow you
242
+ # to test a single controller action per test method.
243
+ #
244
+ # ## Use integration style controller tests over functional style controller tests.
245
+ #
246
+ # Rails discourages the use of functional tests in favor of integration tests
247
+ # (use ActionDispatch::IntegrationTest).
248
+ #
249
+ # New Rails applications no longer generate functional style controller tests
250
+ # and they should only be used for backward compatibility. Integration style
251
+ # controller tests perform actual requests, whereas functional style controller
252
+ # tests merely simulate a request. Besides, integration tests are as fast as
253
+ # functional tests and provide lot of helpers such as `as`, `parsed_body` for
254
+ # effective testing of controller actions including even API endpoints.
255
+ #
256
+ # ## Basic example
257
+ #
258
+ # Functional tests are written as follows:
259
+ # 1. First, one uses the `get`, `post`, `patch`, `put`, `delete`, or `head`
260
+ # method to simulate an HTTP request.
261
+ # 2. Then, one asserts whether the current state is as expected. "State" can be
262
+ # anything: the controller's HTTP response, the database contents, etc.
263
+ #
264
+ #
265
+ # For example:
266
+ #
267
+ # class BooksControllerTest < ActionController::TestCase
268
+ # def test_create
269
+ # # Simulate a POST response with the given HTTP parameters.
270
+ # post(:create, params: { book: { title: "Love Hina" }})
271
+ #
272
+ # # Asserts that the controller tried to redirect us to
273
+ # # the created book's URI.
274
+ # assert_response :found
275
+ #
276
+ # # Asserts that the controller really put the book in the database.
277
+ # assert_not_nil Book.find_by(title: "Love Hina")
278
+ # end
279
+ # end
280
+ #
281
+ # You can also send a real document in the simulated HTTP request.
282
+ #
283
+ # def test_create
284
+ # json = {book: { title: "Love Hina" }}.to_json
285
+ # post :create, body: json
286
+ # end
287
+ #
288
+ # ## Special instance variables
289
+ #
290
+ # ActionController::TestCase will also automatically provide the following
291
+ # instance variables for use in the tests:
292
+ #
293
+ # @controller
294
+ # : The controller instance that will be tested.
295
+ #
296
+ # @request
297
+ # : An ActionController::TestRequest, representing the current HTTP request.
298
+ # You can modify this object before sending the HTTP request. For example,
299
+ # you might want to set some session properties before sending a GET
300
+ # request.
301
+ #
302
+ # @response
303
+ # : An ActionDispatch::TestResponse object, representing the response of the
304
+ # last HTTP response. In the above example, `@response` becomes valid after
305
+ # calling `post`. If the various assert methods are not sufficient, then you
306
+ # may use this object to inspect the HTTP response in detail.
307
+ #
308
+ #
309
+ # ## Controller is automatically inferred
310
+ #
311
+ # ActionController::TestCase will automatically infer the controller under test
312
+ # from the test class name. If the controller cannot be inferred from the test
313
+ # class name, you can explicitly set it with `tests`.
314
+ #
315
+ # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
316
+ # tests WidgetController
317
+ # end
318
+ #
319
+ # ## Testing controller internals
320
+ #
321
+ # In addition to these specific assertions, you also have easy access to various
322
+ # collections that the regular test/unit assertions can be used against. These
323
+ # collections are:
324
+ #
325
+ # * session: Objects being saved in the session.
326
+ # * flash: The flash objects currently in the session.
327
+ # * cookies: Cookies being sent to the user on this request.
328
+ #
329
+ #
330
+ # These collections can be used just like any other hash:
331
+ #
332
+ # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
333
+ # assert flash.empty? # makes sure that there's nothing in the flash
334
+ #
335
+ # On top of the collections, you have the complete URL that a given action
336
+ # redirected to available in `redirect_to_url`.
337
+ #
338
+ # For redirects within the same controller, you can even call follow_redirect
339
+ # and the redirect will be followed, triggering another action call which can
340
+ # then be asserted against.
341
+ #
342
+ # ## Manipulating session and cookie variables
343
+ #
344
+ # Sometimes you need to set up the session and cookie variables for a test. To
345
+ # do this just assign a value to the session or cookie collection:
346
+ #
347
+ # session[:key] = "value"
348
+ # cookies[:key] = "value"
349
+ #
350
+ # To clear the cookies for a test just clear the cookie collection:
351
+ #
352
+ # cookies.clear
353
+ #
354
+ # ## Testing named routes
355
+ #
356
+ # If you're using named routes, they can be easily tested using the original
357
+ # named routes' methods straight in the test case.
358
+ #
359
+ # assert_redirected_to page_url(title: 'foo')
360
+ class TestCase < ActiveSupport::TestCase
361
+ singleton_class.attr_accessor :executor_around_each_request
362
+
363
+ module Behavior
364
+ extend ActiveSupport::Concern
365
+ include ActionDispatch::TestProcess
366
+ include ActiveSupport::Testing::ConstantLookup
367
+ include Rails::Dom::Testing::Assertions
368
+
369
+ attr_reader :response, :request
370
+
371
+ module ClassMethods
372
+ # Sets the controller class name. Useful if the name can't be inferred from test
373
+ # class. Normalizes `controller_class` before using.
374
+ #
375
+ # tests WidgetController
376
+ # tests :widget
377
+ # tests 'widget'
378
+ def tests(controller_class)
379
+ case controller_class
380
+ when String, Symbol
381
+ self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
382
+ when Class
383
+ self.controller_class = controller_class
384
+ else
385
+ raise ArgumentError, "controller class must be a String, Symbol, or Class"
386
+ end
387
+ end
388
+
389
+ def controller_class=(new_class)
390
+ self._controller_class = new_class
391
+ end
392
+
393
+ def controller_class
394
+ if current_controller_class = _controller_class
395
+ current_controller_class
396
+ else
397
+ self.controller_class = determine_default_controller_class(name)
398
+ end
399
+ end
400
+
401
+ def determine_default_controller_class(name)
402
+ determine_constant_from_test_name(name) do |constant|
403
+ Class === constant && constant < ActionController::Metal
404
+ end
405
+ end
406
+ end
407
+
408
+ # Simulate a GET request with the given parameters.
409
+ #
410
+ # * `action`: The controller action to call.
411
+ # * `params`: The hash with HTTP parameters that you want to pass. This may be
412
+ # `nil`.
413
+ # * `body`: The request body with a string that is appropriately encoded
414
+ # (`application/x-www-form-urlencoded` or `multipart/form-data`).
415
+ # * `session`: A hash of parameters to store in the session. This may be
416
+ # `nil`.
417
+ # * `flash`: A hash of parameters to store in the flash. This may be `nil`.
418
+ #
419
+ #
420
+ # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with `post`,
421
+ # `patch`, `put`, `delete`, and `head`. Example sending parameters, session, and
422
+ # setting a flash message:
423
+ #
424
+ # get :show,
425
+ # params: { id: 7 },
426
+ # session: { user_id: 1 },
427
+ # flash: { notice: 'This is flash message' }
428
+ #
429
+ # Note that the request method is not verified. The different methods are
430
+ # available to make the tests more expressive.
431
+ def get(action, **args)
432
+ res = process(action, method: "GET", **args)
433
+ cookies.update res.cookies
434
+ res
435
+ end
436
+
437
+ # Simulate a POST request with the given parameters and set/volley the response.
438
+ # See `get` for more details.
439
+ def post(action, **args)
440
+ process(action, method: "POST", **args)
441
+ end
442
+
443
+ # Simulate a PATCH request with the given parameters and set/volley the
444
+ # response. See `get` for more details.
445
+ def patch(action, **args)
446
+ process(action, method: "PATCH", **args)
447
+ end
448
+
449
+ # Simulate a PUT request with the given parameters and set/volley the response.
450
+ # See `get` for more details.
451
+ def put(action, **args)
452
+ process(action, method: "PUT", **args)
453
+ end
454
+
455
+ # Simulate a DELETE request with the given parameters and set/volley the
456
+ # response. See `get` for more details.
457
+ def delete(action, **args)
458
+ process(action, method: "DELETE", **args)
459
+ end
460
+
461
+ # Simulate a HEAD request with the given parameters and set/volley the response.
462
+ # See `get` for more details.
463
+ def head(action, **args)
464
+ process(action, method: "HEAD", **args)
465
+ end
466
+
467
+ # Simulate an HTTP request to `action` by specifying request method, parameters
468
+ # and set/volley the response.
469
+ #
470
+ # * `action`: The controller action to call.
471
+ # * `method`: Request method used to send the HTTP request. Possible values
472
+ # are `GET`, `POST`, `PATCH`, `PUT`, `DELETE`, `HEAD`. Defaults to `GET`.
473
+ # Can be a symbol.
474
+ # * `params`: The hash with HTTP parameters that you want to pass. This may be
475
+ # `nil`.
476
+ # * `body`: The request body with a string that is appropriately encoded
477
+ # (`application/x-www-form-urlencoded` or `multipart/form-data`).
478
+ # * `session`: A hash of parameters to store in the session. This may be
479
+ # `nil`.
480
+ # * `flash`: A hash of parameters to store in the flash. This may be `nil`.
481
+ # * `format`: Request format. Defaults to `nil`. Can be string or symbol.
482
+ # * `as`: Content type. Defaults to `nil`. Must be a symbol that corresponds
483
+ # to a mime type.
484
+ #
485
+ #
486
+ # Example calling `create` action and sending two params:
487
+ #
488
+ # process :create,
489
+ # method: 'POST',
490
+ # params: {
491
+ # user: { name: 'Gaurish Sharma', email: 'user@example.com' }
492
+ # },
493
+ # session: { user_id: 1 },
494
+ # flash: { notice: 'This is flash message' }
495
+ #
496
+ # To simulate `GET`, `POST`, `PATCH`, `PUT`, `DELETE`, and `HEAD` requests
497
+ # prefer using #get, #post, #patch, #put, #delete and #head methods respectively
498
+ # which will make tests more expressive.
499
+ #
500
+ # It's not recommended to make more than one request in the same test. Instance
501
+ # variables that are set in one request will not persist to the next request,
502
+ # but it's not guaranteed that all Rails internal state will be reset. Prefer
503
+ # ActionDispatch::IntegrationTest for making multiple requests in the same test.
504
+ #
505
+ # Note that the request method is not verified.
506
+ def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
507
+ check_required_ivars
508
+ @controller.clear_instance_variables_between_requests
509
+
510
+ action = +action.to_s
511
+ http_method = method.to_s.upcase
512
+
513
+ @html_document = nil
514
+
515
+ cookies.update(@request.cookies)
516
+ cookies.update_cookies_from_jar
517
+ @request.set_header "HTTP_COOKIE", cookies.to_header
518
+ @request.delete_header "action_dispatch.cookies"
519
+
520
+ @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class
521
+ @response = build_response @response_klass
522
+ @response.request = @request
523
+ @controller.recycle!
524
+
525
+ if body
526
+ @request.set_header "RAW_POST_DATA", body
527
+ end
528
+
529
+ @request.set_header "REQUEST_METHOD", http_method
530
+
531
+ if as
532
+ @request.content_type = Mime[as].to_s
533
+ format ||= as
534
+ end
535
+
536
+ parameters = (params || {}).symbolize_keys
537
+
538
+ if format
539
+ parameters[:format] = format
540
+ end
541
+
542
+ setup_request(controller_class_name, action, parameters, session, flash, xhr)
543
+ process_controller_response(action, cookies, xhr)
544
+ end
545
+
546
+ def controller_class_name
547
+ @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
548
+ end
549
+
550
+ def generated_path(generated_extras)
551
+ generated_extras[0]
552
+ end
553
+
554
+ def query_parameter_names(generated_extras)
555
+ generated_extras[1] + [:controller, :action]
556
+ end
557
+
558
+ def setup_controller_request_and_response
559
+ @controller = nil unless defined? @controller
560
+
561
+ @response_klass = ActionDispatch::TestResponse
562
+
563
+ if klass = self.class.controller_class
564
+ if klass < ActionController::Live
565
+ @response_klass = LiveTestResponse
566
+ end
567
+ unless @controller
568
+ begin
569
+ @controller = klass.new
570
+ rescue
571
+ warn "could not construct controller #{klass}" if $VERBOSE
572
+ end
573
+ end
574
+ end
575
+
576
+ @request = TestRequest.create(@controller.class)
577
+ @response = build_response @response_klass
578
+ @response.request = @request
579
+
580
+ if @controller
581
+ @controller.request = @request
582
+ @controller.params = {}
583
+ end
584
+ end
585
+
586
+ def build_response(klass)
587
+ klass.create
588
+ end
589
+
590
+ included do
591
+ include ActionController::TemplateAssertions
592
+ include ActionDispatch::Assertions
593
+ class_attribute :_controller_class
594
+ setup :setup_controller_request_and_response
595
+ ActiveSupport.run_load_hooks(:action_controller_test_case, self)
596
+ end
597
+
598
+ private
599
+ def setup_request(controller_class_name, action, parameters, session, flash, xhr)
600
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
601
+ generated_path = generated_path(generated_extras)
602
+ query_string_keys = query_parameter_names(generated_extras)
603
+
604
+ @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
605
+
606
+ @request.session.update(session) if session
607
+ @request.flash.update(flash || {})
608
+
609
+ if xhr
610
+ @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
611
+ @request.fetch_header("HTTP_ACCEPT") do |k|
612
+ @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
613
+ end
614
+ end
615
+
616
+ @request.fetch_header("SCRIPT_NAME") do |k|
617
+ @request.set_header k, @controller.config.relative_url_root
618
+ end
619
+ end
620
+
621
+ def wrap_execution(&block)
622
+ if ActionController::TestCase.executor_around_each_request && defined?(Rails.application) && Rails.application
623
+ Rails.application.executor.wrap(&block)
624
+ else
625
+ yield
626
+ end
627
+ end
628
+
629
+ def process_controller_response(action, cookies, xhr)
630
+ begin
631
+ @controller.recycle!
632
+
633
+ wrap_execution { @controller.dispatch(action, @request, @response) }
634
+ ensure
635
+ @request = @controller.request
636
+ @response = @controller.response
637
+
638
+ if @request.have_cookie_jar?
639
+ unless @request.cookie_jar.committed?
640
+ @request.cookie_jar.write(@response)
641
+ cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
642
+ end
643
+ end
644
+ @response.prepare!
645
+
646
+ if flash_value = @request.flash.to_session_value
647
+ @request.session["flash"] = flash_value
648
+ else
649
+ @request.session.delete("flash")
650
+ end
651
+
652
+ if xhr
653
+ @request.delete_header "HTTP_X_REQUESTED_WITH"
654
+ @request.delete_header "HTTP_ACCEPT"
655
+ end
656
+ @request.query_string = ""
657
+
658
+ @response.sent!
659
+ end
660
+
661
+ @response
662
+ end
663
+
664
+ def scrub_env!(env)
665
+ env.delete_if do |k, _|
666
+ k.start_with?("rack.request", "action_dispatch.request", "action_dispatch.rescue")
667
+ end
668
+ env["rack.input"] = StringIO.new
669
+ env.delete "CONTENT_LENGTH"
670
+ env.delete "RAW_POST_DATA"
671
+ env
672
+ end
673
+
674
+ def document_root_element
675
+ html_document.root
676
+ end
677
+
678
+ def check_required_ivars
679
+ # Check for required instance variables so we can give an understandable error
680
+ # message.
681
+ [:@routes, :@controller, :@request, :@response].each do |iv_name|
682
+ if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
683
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
684
+ end
685
+ end
686
+ end
687
+ end
688
+
689
+ include Behavior
690
+ end
691
+ end