actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,264 +1,237 @@
1
- require 'rack/session/abstract/id'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_support/core_ext/object/to_query'
4
- require 'active_support/core_ext/class/attribute'
5
- require 'active_support/core_ext/module/anonymous'
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/session/abstract/id"
4
+ require "active_support/core_ext/hash/conversions"
5
+ require "active_support/core_ext/object/to_query"
6
+ require "active_support/core_ext/module/anonymous"
7
+ require "active_support/core_ext/module/redefine_method"
8
+ require "active_support/core_ext/hash/keys"
9
+ require "active_support/testing/constant_lookup"
10
+ require "action_controller/template_assertions"
11
+ require "rails-dom-testing"
6
12
 
7
13
  module ActionController
8
- module TemplateAssertions
9
- extend ActiveSupport::Concern
14
+ class Metal
15
+ include Testing::Functional
16
+ end
10
17
 
11
- included do
12
- setup :setup_subscriptions
13
- teardown :teardown_subscriptions
18
+ module Live
19
+ # Disable controller / rendering threads in tests. User tests can access
20
+ # the database on the main thread, so they could open a txn, then the
21
+ # controller thread will open a new connection and try to access data
22
+ # that's only visible to the main thread's txn. This is the problem in #23483.
23
+ silence_redefinition_of_method :new_controller_thread
24
+ def new_controller_thread # :nodoc:
25
+ yield
14
26
  end
27
+ end
15
28
 
16
- def setup_subscriptions
17
- @partials = Hash.new(0)
18
- @templates = Hash.new(0)
19
- @layouts = Hash.new(0)
20
-
21
- ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
22
- path = payload[:layout]
23
- if path
24
- @layouts[path] += 1
25
- if path =~ /^layouts\/(.*)/
26
- @layouts[$1] += 1
27
- end
28
- end
29
- end
29
+ # ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
30
+ # Please use ActionDispatch::IntegrationTest going forward.
31
+ class TestRequest < ActionDispatch::TestRequest #:nodoc:
32
+ DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
33
+ DEFAULT_ENV.delete "PATH_INFO"
30
34
 
31
- ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
32
- path = payload[:virtual_path]
33
- next unless path
34
- partial = path =~ /^.*\/_[^\/]*$/
35
- if partial
36
- @partials[path] += 1
37
- @partials[path.split("/").last] += 1
38
- @templates[path] += 1
39
- else
40
- @templates[path] += 1
41
- end
42
- end
35
+ def self.new_session
36
+ TestSession.new
43
37
  end
44
38
 
45
- def teardown_subscriptions
46
- ActiveSupport::Notifications.unsubscribe("render_template.action_view")
47
- ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
39
+ attr_reader :controller_class
40
+
41
+ # Create a new test request with default `env` values.
42
+ def self.create(controller_class)
43
+ env = {}
44
+ env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
45
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
46
+ new(default_env.merge(env), new_session, controller_class)
48
47
  end
49
48
 
50
- def process(*args)
51
- @partials = Hash.new(0)
52
- @templates = Hash.new(0)
53
- @layouts = Hash.new(0)
54
- super
49
+ def self.default_env
50
+ DEFAULT_ENV
55
51
  end
52
+ private_class_method :default_env
56
53
 
57
- # Asserts that the request was rendered with the appropriate template file or partials.
58
- #
59
- # ==== Examples
60
- #
61
- # # assert that the "new" view template was rendered
62
- # assert_template "new"
63
- #
64
- # # assert that the layout 'admin' was rendered
65
- # assert_template :layout => 'admin'
66
- # assert_template :layout => 'layouts/admin'
67
- # assert_template :layout => :admin
68
- #
69
- # # assert that no layout was rendered
70
- # assert_template :layout => nil
71
- # assert_template :layout => false
72
- #
73
- # # assert that the "_customer" partial was rendered twice
74
- # assert_template :partial => '_customer', :count => 2
75
- #
76
- # # assert that no partials were rendered
77
- # assert_template :partial => false
78
- #
79
- # In a view test case, you can also assert that specific locals are passed
80
- # to partials:
81
- #
82
- # # assert that the "_customer" partial was rendered with a specific object
83
- # assert_template :partial => '_customer', :locals => { :customer => @customer }
84
- #
85
- def assert_template(options = {}, message = nil)
86
- validate_request!
87
- # Force body to be read in case the template is being streamed
88
- response.body
89
-
90
- case options
91
- when NilClass, String, Symbol
92
- options = options.to_s if Symbol === options
93
- rendered = @templates
94
- msg = build_message(message,
95
- "expecting <?> but rendering with <?>",
96
- options, rendered.keys.join(', '))
97
- assert_block(msg) do
98
- if options
99
- rendered.any? { |t,num| t.match(options) }
100
- else
101
- @templates.blank?
102
- end
103
- end
104
- when Hash
105
- if options.key?(:layout)
106
- expected_layout = options[:layout]
107
- msg = build_message(message,
108
- "expecting layout <?> but action rendered <?>",
109
- expected_layout, @layouts.keys)
110
-
111
- case expected_layout
112
- when String, Symbol
113
- assert(@layouts.keys.include?(expected_layout.to_s), msg)
114
- when Regexp
115
- assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
116
- when nil, false
117
- assert(@layouts.empty?, msg)
118
- end
119
- end
54
+ def initialize(env, session, controller_class)
55
+ super(env)
120
56
 
121
- if expected_partial = options[:partial]
122
- if expected_locals = options[:locals]
123
- if defined?(@locals)
124
- actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
125
- expected_locals.each_pair do |k,v|
126
- assert_equal(v, actual_locals[k])
127
- end
128
- else
129
- warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
130
- end
131
- elsif expected_count = options[:count]
132
- actual_count = @partials[expected_partial]
133
- msg = build_message(message,
134
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
135
- expected_partial, expected_count, actual_count)
136
- assert(actual_count == expected_count.to_i, msg)
137
- else
138
- msg = build_message(message,
139
- "expecting partial <?> but action rendered <?>",
140
- options[:partial], @partials.keys)
141
- assert(@partials.include?(expected_partial), msg)
142
- end
143
- elsif options.key?(:partial)
144
- assert @partials.empty?,
145
- "Expected no partials to be rendered"
146
- end
147
- end
57
+ self.session = session
58
+ self.session_options = TestSession::DEFAULT_OPTIONS.dup
59
+ @controller_class = controller_class
60
+ @custom_param_parsers = {
61
+ xml: lambda { |raw_post| Hash.from_xml(raw_post)["hash"] }
62
+ }
148
63
  end
149
- end
150
-
151
- class TestRequest < ActionDispatch::TestRequest #:nodoc:
152
- def initialize(env = {})
153
- super
154
64
 
155
- self.session = TestSession.new
156
- self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
65
+ def query_string=(string)
66
+ set_header Rack::QUERY_STRING, string
157
67
  end
158
68
 
159
- class Result < ::Array #:nodoc:
160
- def to_s() join '/' end
161
- def self.new_escaped(strings)
162
- new strings.collect {|str| uri_parser.unescape str}
163
- end
69
+ def content_type=(type)
70
+ set_header "CONTENT_TYPE", type
164
71
  end
165
72
 
166
- def assign_parameters(routes, controller_path, action, parameters = {})
167
- parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
168
- extra_keys = routes.extra_keys(parameters)
169
- non_path_parameters = get? ? query_parameters : request_parameters
170
- parameters.each do |key, value|
171
- if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
172
- value = value.map{ |v| v.duplicable? ? v.dup : v }
173
- elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
174
- value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
175
- elsif value.frozen? && value.duplicable?
176
- value = value.dup
177
- end
73
+ def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
74
+ non_path_parameters = {}
75
+ path_parameters = {}
178
76
 
179
- if extra_keys.include?(key.to_sym)
77
+ parameters.each do |key, value|
78
+ if query_string_keys.include?(key)
180
79
  non_path_parameters[key] = value
181
80
  else
182
81
  if value.is_a?(Array)
183
- value = Result.new(value.map(&:to_param))
82
+ value = value.map(&:to_param)
184
83
  else
185
84
  value = value.to_param
186
85
  end
187
86
 
188
- path_parameters[key.to_s] = value
87
+ path_parameters[key] = value
189
88
  end
190
89
  end
191
90
 
192
- # Clear the combined params hash in case it was already referenced.
193
- @env.delete("action_dispatch.request.parameters")
91
+ if get?
92
+ if query_string.blank?
93
+ self.query_string = non_path_parameters.to_query
94
+ end
95
+ else
96
+ if ENCODER.should_multipart?(non_path_parameters)
97
+ self.content_type = ENCODER.content_type
98
+ data = ENCODER.build_multipart non_path_parameters
99
+ else
100
+ fetch_header("CONTENT_TYPE") do |k|
101
+ set_header k, "application/x-www-form-urlencoded"
102
+ end
194
103
 
195
- params = self.request_parameters.dup
196
- %w(controller action only_path).each do |k|
197
- params.delete(k)
198
- params.delete(k.to_sym)
104
+ case content_mime_type.to_sym
105
+ when nil
106
+ raise "Unknown Content-Type: #{content_type}"
107
+ when :json
108
+ data = ActiveSupport::JSON.encode(non_path_parameters)
109
+ when :xml
110
+ data = non_path_parameters.to_xml
111
+ when :url_encoded_form
112
+ data = non_path_parameters.to_query
113
+ else
114
+ @custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
115
+ data = non_path_parameters.to_query
116
+ end
117
+ end
118
+
119
+ data_stream = StringIO.new(data)
120
+ set_header "CONTENT_LENGTH", data_stream.length.to_s
121
+ set_header "rack.input", data_stream
199
122
  end
200
- data = params.to_query
201
123
 
202
- @env['CONTENT_LENGTH'] = data.length.to_s
203
- @env['rack.input'] = StringIO.new(data)
204
- end
124
+ fetch_header("PATH_INFO") do |k|
125
+ set_header k, generated_path
126
+ end
127
+ path_parameters[:controller] = controller_path
128
+ path_parameters[:action] = action
205
129
 
206
- def recycle!
207
- @formats = nil
208
- @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
209
- @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
210
- @symbolized_path_params = nil
211
- @method = @request_method = nil
212
- @fullpath = @ip = @remote_ip = @protocol = nil
213
- @env['action_dispatch.request.query_parameters'] = {}
214
- @set_cookies ||= {}
215
- @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
216
- deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
217
- @set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
218
- cookie_jar.update(rack_cookies)
219
- cookie_jar.update(cookies)
220
- cookie_jar.update(@set_cookies)
221
- cookie_jar.recycle!
130
+ self.path_parameters = path_parameters
222
131
  end
132
+
133
+ ENCODER = Class.new do
134
+ include Rack::Test::Utils
135
+
136
+ def should_multipart?(params)
137
+ # FIXME: lifted from Rack-Test. We should push this separation upstream.
138
+ multipart = false
139
+ query = lambda { |value|
140
+ case value
141
+ when Array
142
+ value.each(&query)
143
+ when Hash
144
+ value.values.each(&query)
145
+ when Rack::Test::UploadedFile
146
+ multipart = true
147
+ end
148
+ }
149
+ params.values.each(&query)
150
+ multipart
151
+ end
152
+
153
+ public :build_multipart
154
+
155
+ def content_type
156
+ "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
157
+ end
158
+ end.new
159
+
160
+ private
161
+
162
+ def params_parsers
163
+ super.merge @custom_param_parsers
164
+ end
223
165
  end
224
166
 
225
- class TestResponse < ActionDispatch::TestResponse
226
- def recycle!
227
- @status = 200
228
- @header = {}
229
- @writer = lambda { |x| @body << x }
230
- @block = nil
231
- @length = 0
232
- @body = []
233
- @charset = @content_type = nil
234
- @request = @template = nil
235
- end
167
+ class LiveTestResponse < Live::Response
168
+ # Was the response successful?
169
+ alias_method :success?, :successful?
170
+
171
+ # Was the URL not found?
172
+ alias_method :missing?, :not_found?
173
+
174
+ # Was there a server-side error?
175
+ alias_method :error?, :server_error?
236
176
  end
237
177
 
178
+ # Methods #destroy and #load! are overridden to avoid calling methods on the
179
+ # @store object, which does not exist for the TestSession class.
238
180
  class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
239
- DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
181
+ DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
240
182
 
241
183
  def initialize(session = {})
242
184
  super(nil, nil)
243
- replace(session.stringify_keys)
185
+ @id = SecureRandom.hex(16)
186
+ @data = stringify_keys(session)
244
187
  @loaded = true
245
188
  end
246
189
 
247
190
  def exists?
248
191
  true
249
192
  end
193
+
194
+ def keys
195
+ @data.keys
196
+ end
197
+
198
+ def values
199
+ @data.values
200
+ end
201
+
202
+ def destroy
203
+ clear
204
+ end
205
+
206
+ def fetch(key, *args, &block)
207
+ @data.fetch(key.to_s, *args, &block)
208
+ end
209
+
210
+ private
211
+
212
+ def load!
213
+ @id
214
+ end
250
215
  end
251
216
 
252
217
  # Superclass for ActionController functional tests. Functional tests allow you to
253
- # test a single controller action per test method. This should not be confused with
254
- # integration tests (see ActionDispatch::IntegrationTest), which are more like
255
- # "stories" that can involve multiple controllers and multiple actions (i.e. multiple
256
- # different HTTP requests).
218
+ # test a single controller action per test method.
219
+ #
220
+ # == Use integration style controller tests over functional style controller tests.
221
+ #
222
+ # Rails discourages the use of functional tests in favor of integration tests
223
+ # (use ActionDispatch::IntegrationTest).
224
+ #
225
+ # New Rails applications no longer generate functional style controller tests and they should
226
+ # only be used for backward compatibility. Integration style controller tests perform actual
227
+ # requests, whereas functional style controller tests merely simulate a request. Besides,
228
+ # integration tests are as fast as functional tests and provide lot of helpers such as +as+,
229
+ # +parsed_body+ for effective testing of controller actions including even API endpoints.
257
230
  #
258
231
  # == Basic example
259
232
  #
260
233
  # Functional tests are written as follows:
261
- # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
234
+ # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
262
235
  # an HTTP request.
263
236
  # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
264
237
  # the controller's HTTP response, the database contents, etc.
@@ -268,17 +241,24 @@ module ActionController
268
241
  # class BooksControllerTest < ActionController::TestCase
269
242
  # def test_create
270
243
  # # Simulate a POST response with the given HTTP parameters.
271
- # post(:create, :book => { :title => "Love Hina" })
244
+ # post(:create, params: { book: { title: "Love Hina" }})
272
245
  #
273
- # # Assert that the controller tried to redirect us to
246
+ # # Asserts that the controller tried to redirect us to
274
247
  # # the created book's URI.
275
248
  # assert_response :found
276
249
  #
277
- # # Assert that the controller really put the book in the database.
278
- # assert_not_nil Book.find_by_title("Love Hina")
250
+ # # Asserts that the controller really put the book in the database.
251
+ # assert_not_nil Book.find_by(title: "Love Hina")
279
252
  # end
280
253
  # end
281
254
  #
255
+ # You can also send a real document in the simulated HTTP request.
256
+ #
257
+ # def test_create
258
+ # json = {book: { title: "Love Hina" }}.to_json
259
+ # post :create, body: json
260
+ # end
261
+ #
282
262
  # == Special instance variables
283
263
  #
284
264
  # ActionController::TestCase will also automatically provide the following instance
@@ -291,7 +271,7 @@ module ActionController
291
271
  # request. You can modify this object before sending the HTTP request. For example,
292
272
  # you might want to set some session properties before sending a GET request.
293
273
  # <b>@response</b>::
294
- # An ActionController::TestResponse object, representing the response
274
+ # An ActionDispatch::TestResponse object, representing the response
295
275
  # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
296
276
  # after calling +post+. If the various assert methods are not sufficient, then you
297
277
  # may use this object to inspect the HTTP response in detail.
@@ -314,22 +294,16 @@ module ActionController
314
294
  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
315
295
  # can be used against. These collections are:
316
296
  #
317
- # * assigns: Instance variables assigned in the action that are available for the view.
318
297
  # * session: Objects being saved in the session.
319
298
  # * flash: The flash objects currently in the session.
320
299
  # * cookies: \Cookies being sent to the user on this request.
321
300
  #
322
301
  # These collections can be used just like any other hash:
323
302
  #
324
- # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
325
303
  # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
326
304
  # assert flash.empty? # makes sure that there's nothing in the flash
327
305
  #
328
- # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
329
- # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
330
- # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
331
- #
332
- # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
306
+ # On top of the collections, you have the complete URL that a given action redirected to available in <tt>redirect_to_url</tt>.
333
307
  #
334
308
  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
335
309
  # action call which can then be asserted against.
@@ -349,29 +323,28 @@ module ActionController
349
323
  # == \Testing named routes
350
324
  #
351
325
  # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
352
- # Example:
353
326
  #
354
- # assert_redirected_to page_url(:title => 'foo')
327
+ # assert_redirected_to page_url(title: 'foo')
355
328
  class TestCase < ActiveSupport::TestCase
356
329
  module Behavior
357
330
  extend ActiveSupport::Concern
358
331
  include ActionDispatch::TestProcess
332
+ include ActiveSupport::Testing::ConstantLookup
333
+ include Rails::Dom::Testing::Assertions
359
334
 
360
335
  attr_reader :response, :request
361
336
 
362
337
  module ClassMethods
363
-
364
338
  # Sets the controller class name. Useful if the name can't be inferred from test class.
365
- # Normalizes +controller_class+ before using. Examples:
339
+ # Normalizes +controller_class+ before using.
366
340
  #
367
341
  # tests WidgetController
368
342
  # tests :widget
369
343
  # tests 'widget'
370
- #
371
344
  def tests(controller_class)
372
345
  case controller_class
373
346
  when String, Symbol
374
- self.controller_class = "#{controller_class.to_s.underscore}_controller".camelize.constantize
347
+ self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
375
348
  when Class
376
349
  self.controller_class = controller_class
377
350
  else
@@ -380,12 +353,11 @@ module ActionController
380
353
  end
381
354
 
382
355
  def controller_class=(new_class)
383
- prepare_controller_class(new_class) if new_class
384
356
  self._controller_class = new_class
385
357
  end
386
358
 
387
359
  def controller_class
388
- if current_controller_class = self._controller_class
360
+ if current_controller_class = _controller_class
389
361
  current_controller_class
390
362
  else
391
363
  self.controller_class = determine_default_controller_class(name)
@@ -393,179 +365,262 @@ module ActionController
393
365
  end
394
366
 
395
367
  def determine_default_controller_class(name)
396
- name.sub(/Test$/, '').safe_constantize
397
- end
398
-
399
- def prepare_controller_class(new_class)
400
- new_class.send :include, ActionController::TestCase::RaiseActionExceptions
368
+ determine_constant_from_test_name(name) do |constant|
369
+ Class === constant && constant < ActionController::Metal
370
+ end
401
371
  end
372
+ end
402
373
 
374
+ # Simulate a GET request with the given parameters.
375
+ #
376
+ # - +action+: The controller action to call.
377
+ # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
378
+ # - +body+: The request body with a string that is appropriately encoded
379
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
380
+ # - +session+: A hash of parameters to store in the session. This may be +nil+.
381
+ # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
382
+ #
383
+ # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
384
+ # +post+, +patch+, +put+, +delete+, and +head+.
385
+ # Example sending parameters, session and setting a flash message:
386
+ #
387
+ # get :show,
388
+ # params: { id: 7 },
389
+ # session: { user_id: 1 },
390
+ # flash: { notice: 'This is flash message' }
391
+ #
392
+ # Note that the request method is not verified. The different methods are
393
+ # available to make the tests more expressive.
394
+ def get(action, **args)
395
+ res = process(action, method: "GET", **args)
396
+ cookies.update res.cookies
397
+ res
403
398
  end
404
399
 
405
- # Executes a request simulating GET HTTP method and set/volley the response
406
- def get(action, parameters = nil, session = nil, flash = nil)
407
- process(action, parameters, session, flash, "GET")
400
+ # Simulate a POST request with the given parameters and set/volley the response.
401
+ # See +get+ for more details.
402
+ def post(action, **args)
403
+ process(action, method: "POST", **args)
408
404
  end
409
405
 
410
- # Executes a request simulating POST HTTP method and set/volley the response
411
- def post(action, parameters = nil, session = nil, flash = nil)
412
- process(action, parameters, session, flash, "POST")
406
+ # Simulate a PATCH request with the given parameters and set/volley the response.
407
+ # See +get+ for more details.
408
+ def patch(action, **args)
409
+ process(action, method: "PATCH", **args)
413
410
  end
414
411
 
415
- # Executes a request simulating PUT HTTP method and set/volley the response
416
- def put(action, parameters = nil, session = nil, flash = nil)
417
- process(action, parameters, session, flash, "PUT")
412
+ # Simulate a PUT request with the given parameters and set/volley the response.
413
+ # See +get+ for more details.
414
+ def put(action, **args)
415
+ process(action, method: "PUT", **args)
418
416
  end
419
417
 
420
- # Executes a request simulating DELETE HTTP method and set/volley the response
421
- def delete(action, parameters = nil, session = nil, flash = nil)
422
- process(action, parameters, session, flash, "DELETE")
418
+ # Simulate a DELETE request with the given parameters and set/volley the response.
419
+ # See +get+ for more details.
420
+ def delete(action, **args)
421
+ process(action, method: "DELETE", **args)
423
422
  end
424
423
 
425
- # Executes a request simulating HEAD HTTP method and set/volley the response
426
- def head(action, parameters = nil, session = nil, flash = nil)
427
- process(action, parameters, session, flash, "HEAD")
424
+ # Simulate a HEAD request with the given parameters and set/volley the response.
425
+ # See +get+ for more details.
426
+ def head(action, **args)
427
+ process(action, method: "HEAD", **args)
428
428
  end
429
429
 
430
- def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
431
- @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
432
- @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
433
- __send__(request_method, action, parameters, session, flash).tap do
434
- @request.env.delete 'HTTP_X_REQUESTED_WITH'
435
- @request.env.delete 'HTTP_ACCEPT'
430
+ # Simulate an HTTP request to +action+ by specifying request method,
431
+ # parameters and set/volley the response.
432
+ #
433
+ # - +action+: The controller action to call.
434
+ # - +method+: Request method used to send the HTTP request. Possible values
435
+ # are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+. Can be a symbol.
436
+ # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
437
+ # - +body+: The request body with a string that is appropriately encoded
438
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
439
+ # - +session+: A hash of parameters to store in the session. This may be +nil+.
440
+ # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
441
+ # - +format+: Request format. Defaults to +nil+. Can be string or symbol.
442
+ # - +as+: Content type. Defaults to +nil+. Must be a symbol that corresponds
443
+ # to a mime type.
444
+ #
445
+ # Example calling +create+ action and sending two params:
446
+ #
447
+ # process :create,
448
+ # method: 'POST',
449
+ # params: {
450
+ # user: { name: 'Gaurish Sharma', email: 'user@example.com' }
451
+ # },
452
+ # session: { user_id: 1 },
453
+ # flash: { notice: 'This is flash message' }
454
+ #
455
+ # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
456
+ # prefer using #get, #post, #patch, #put, #delete and #head methods
457
+ # respectively which will make tests more expressive.
458
+ #
459
+ # Note that the request method is not verified.
460
+ def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
461
+ check_required_ivars
462
+
463
+ action = action.to_s.dup
464
+ http_method = method.to_s.upcase
465
+
466
+ @html_document = nil
467
+
468
+ cookies.update(@request.cookies)
469
+ cookies.update_cookies_from_jar
470
+ @request.set_header "HTTP_COOKIE", cookies.to_header
471
+ @request.delete_header "action_dispatch.cookies"
472
+
473
+ @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class
474
+ @response = build_response @response_klass
475
+ @response.request = @request
476
+ @controller.recycle!
477
+
478
+ if body
479
+ @request.set_header "RAW_POST_DATA", body
436
480
  end
437
- end
438
- alias xhr :xml_http_request
439
-
440
- def paramify_values(hash_or_array_or_value)
441
- case hash_or_array_or_value
442
- when Hash
443
- Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
444
- when Array
445
- hash_or_array_or_value.map {|i| paramify_values(i)}
446
- when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
447
- hash_or_array_or_value
448
- else
449
- hash_or_array_or_value.to_param
481
+
482
+ @request.set_header "REQUEST_METHOD", http_method
483
+
484
+ if as
485
+ @request.content_type = Mime[as].to_s
486
+ format ||= as
450
487
  end
451
- end
452
488
 
453
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
454
- # Ensure that numbers and symbols passed as params are converted to
455
- # proper params, as is the case when engaging rack.
456
- parameters = paramify_values(parameters) if html_format?(parameters)
489
+ parameters = (params || {}).symbolize_keys
457
490
 
458
- # Sanity check for required instance variables so we can give an
459
- # understandable error message.
460
- %w(@routes @controller @request @response).each do |iv_name|
461
- if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
462
- raise "#{iv_name} is nil: make sure you set it in your test's setup method."
491
+ if format
492
+ parameters[:format] = format
493
+ end
494
+
495
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
496
+ generated_path = generated_path(generated_extras)
497
+ query_string_keys = query_parameter_names(generated_extras)
498
+
499
+ @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
500
+
501
+ @request.session.update(session) if session
502
+ @request.flash.update(flash || {})
503
+
504
+ if xhr
505
+ @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
506
+ @request.fetch_header("HTTP_ACCEPT") do |k|
507
+ @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
463
508
  end
464
509
  end
465
510
 
466
- @request.recycle!
467
- @response.recycle!
468
- @controller.response_body = nil
469
- @controller.formats = nil
470
- @controller.params = nil
511
+ @request.fetch_header("SCRIPT_NAME") do |k|
512
+ @request.set_header k, @controller.config.relative_url_root
513
+ end
471
514
 
472
- @html_document = nil
473
- @request.env['REQUEST_METHOD'] = http_method
515
+ begin
516
+ @controller.recycle!
517
+ @controller.dispatch(action, @request, @response)
518
+ ensure
519
+ @request = @controller.request
520
+ @response = @controller.response
521
+
522
+ if @request.have_cookie_jar?
523
+ unless @request.cookie_jar.committed?
524
+ @request.cookie_jar.write(@response)
525
+ cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
526
+ end
527
+ end
528
+ @response.prepare!
474
529
 
475
- parameters ||= {}
476
- controller_class_name = @controller.class.anonymous? ?
477
- "anonymous_controller" :
478
- @controller.class.controller_path
530
+ if flash_value = @request.flash.to_session_value
531
+ @request.session["flash"] = flash_value
532
+ else
533
+ @request.session.delete("flash")
534
+ end
479
535
 
480
- @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
536
+ if xhr
537
+ @request.delete_header "HTTP_X_REQUESTED_WITH"
538
+ @request.delete_header "HTTP_ACCEPT"
539
+ end
540
+ @request.query_string = ""
481
541
 
482
- @request.session = ActionController::TestSession.new(session) if session
483
- @request.session["flash"] = @request.flash.update(flash || {})
484
- @request.session["flash"].sweep
542
+ @response.sent!
543
+ end
485
544
 
486
- @controller.request = @request
487
- build_request_uri(action, parameters)
488
- @controller.class.class_eval { include Testing }
489
- @controller.recycle!
490
- @controller.process_with_new_base_test(@request, @response)
491
- @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
492
- @request.session.delete('flash') if @request.session['flash'].blank?
493
545
  @response
494
546
  end
495
547
 
548
+ def controller_class_name
549
+ @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
550
+ end
551
+
552
+ def generated_path(generated_extras)
553
+ generated_extras[0]
554
+ end
555
+
556
+ def query_parameter_names(generated_extras)
557
+ generated_extras[1] + [:controller, :action]
558
+ end
559
+
496
560
  def setup_controller_request_and_response
497
- @request = TestRequest.new
498
- @response = TestResponse.new
561
+ @controller = nil unless defined? @controller
562
+
563
+ @response_klass = ActionDispatch::TestResponse
499
564
 
500
565
  if klass = self.class.controller_class
501
- @controller ||= klass.new rescue nil
566
+ if klass < ActionController::Live
567
+ @response_klass = LiveTestResponse
568
+ end
569
+ unless @controller
570
+ begin
571
+ @controller = klass.new
572
+ rescue
573
+ warn "could not construct controller #{klass}" if $VERBOSE
574
+ end
575
+ end
502
576
  end
503
577
 
504
- @request.env.delete('PATH_INFO')
578
+ @request = TestRequest.create(@controller.class)
579
+ @response = build_response @response_klass
580
+ @response.request = @request
505
581
 
506
- if defined?(@controller) && @controller
582
+ if @controller
507
583
  @controller.request = @request
508
584
  @controller.params = {}
509
585
  end
510
586
  end
511
587
 
588
+ def build_response(klass)
589
+ klass.create
590
+ end
591
+
512
592
  included do
513
593
  include ActionController::TemplateAssertions
514
594
  include ActionDispatch::Assertions
515
595
  class_attribute :_controller_class
516
596
  setup :setup_controller_request_and_response
597
+ ActiveSupport.run_load_hooks(:action_controller_test_case, self)
517
598
  end
518
599
 
519
- private
520
-
521
- def build_request_uri(action, parameters)
522
- unless @request.env["PATH_INFO"]
523
- options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
524
- options.update(
525
- :only_path => true,
526
- :action => action,
527
- :relative_url_root => nil,
528
- :_path_segments => @request.symbolized_path_parameters)
529
-
530
- url, query_string = @routes.url_for(options).split("?", 2)
531
-
532
- @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
533
- @request.env["PATH_INFO"] = url
534
- @request.env["QUERY_STRING"] = query_string || ""
600
+ private
601
+
602
+ def scrub_env!(env)
603
+ env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
604
+ env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
605
+ env.delete "action_dispatch.request.query_parameters"
606
+ env.delete "action_dispatch.request.request_parameters"
607
+ env["rack.input"] = StringIO.new
608
+ env.delete "CONTENT_LENGTH"
609
+ env.delete "RAW_POST_DATA"
610
+ env
535
611
  end
536
- end
537
612
 
538
- def html_format?(parameters)
539
- return true unless parameters.is_a?(Hash)
540
- format = Mime[parameters[:format]]
541
- format.nil? || format.html?
542
- end
543
- end
544
-
545
- # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
546
- # (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
547
- # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
548
- # than 0.0.0.0.
549
- #
550
- # The exception is stored in the exception accessor for further inspection.
551
- module RaiseActionExceptions
552
- def self.included(base)
553
- unless base.method_defined?(:exception) && base.method_defined?(:exception=)
554
- base.class_eval do
555
- attr_accessor :exception
556
- protected :exception, :exception=
557
- end
613
+ def document_root_element
614
+ html_document.root
558
615
  end
559
- end
560
616
 
561
- protected
562
- def rescue_action_without_handler(e)
563
- self.exception = e
564
-
565
- if request.remote_addr == "0.0.0.0"
566
- raise(e)
567
- else
568
- super(e)
617
+ def check_required_ivars
618
+ # Sanity check for required instance variables so we can give an
619
+ # understandable error message.
620
+ [:@routes, :@controller, :@request, :@response].each do |iv_name|
621
+ if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
622
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
623
+ end
569
624
  end
570
625
  end
571
626
  end