actionpack 4.2.11.1 → 6.1.3.2

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,224 +1,81 @@
1
- require 'rack/session/abstract/id'
2
- require 'active_support/core_ext/object/to_query'
3
- require 'active_support/core_ext/module/anonymous'
4
- require 'active_support/core_ext/hash/keys'
5
- require 'active_support/deprecation'
6
-
7
- require 'rails-dom-testing'
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"
8
12
 
9
13
  module ActionController
10
- module TemplateAssertions
11
- extend ActiveSupport::Concern
14
+ class Metal
15
+ include Testing::Functional
16
+ end
12
17
 
13
- included do
14
- setup :setup_subscriptions
15
- 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
16
26
  end
27
+ end
17
28
 
18
- RENDER_TEMPLATE_INSTANCE_VARIABLES = %w{partials templates layouts files}.freeze
19
-
20
- def setup_subscriptions
21
- RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
22
- instance_variable_set("@_#{instance_variable}", Hash.new(0))
23
- end
24
-
25
- @_subscribers = []
26
-
27
- @_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
28
- path = payload[:layout]
29
- if path
30
- @_layouts[path] += 1
31
- if path =~ /^layouts\/(.*)/
32
- @_layouts[$1] += 1
33
- end
34
- end
35
- end
36
-
37
- @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
38
- if virtual_path = payload[:virtual_path]
39
- partial = virtual_path =~ /^.*\/_[^\/]*$/
40
-
41
- if partial
42
- @_partials[virtual_path] += 1
43
- @_partials[virtual_path.split("/").last] += 1
44
- end
29
+ # ActionController::TestCase will be deprecated and moved to a gem in the future.
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"
45
34
 
46
- @_templates[virtual_path] += 1
47
- else
48
- path = payload[:identifier]
49
- if path
50
- @_files[path] += 1
51
- @_files[path.split("/").last] += 1
52
- end
53
- end
54
- end
35
+ def self.new_session
36
+ TestSession.new
55
37
  end
56
38
 
57
- def teardown_subscriptions
58
- return unless defined?(@_subscribers)
39
+ attr_reader :controller_class
59
40
 
60
- @_subscribers.each do |subscriber|
61
- ActiveSupport::Notifications.unsubscribe(subscriber)
62
- end
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)
63
47
  end
64
48
 
65
- def process(*args)
66
- reset_template_assertion
67
- super
68
- end
69
-
70
- def reset_template_assertion
71
- RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
72
- ivar_name = "@_#{instance_variable}"
73
- if instance_variable_defined?(ivar_name)
74
- instance_variable_get(ivar_name).clear
75
- end
76
- end
49
+ def self.default_env
50
+ DEFAULT_ENV
77
51
  end
52
+ private_class_method :default_env
78
53
 
79
- # Asserts that the request was rendered with the appropriate template file or partials.
80
- #
81
- # # assert that the "new" view template was rendered
82
- # assert_template "new"
83
- #
84
- # # assert that the exact template "admin/posts/new" was rendered
85
- # assert_template %r{\Aadmin/posts/new\Z}
86
- #
87
- # # assert that the layout 'admin' was rendered
88
- # assert_template layout: 'admin'
89
- # assert_template layout: 'layouts/admin'
90
- # assert_template layout: :admin
91
- #
92
- # # assert that no layout was rendered
93
- # assert_template layout: nil
94
- # assert_template layout: false
95
- #
96
- # # assert that the "_customer" partial was rendered twice
97
- # assert_template partial: '_customer', count: 2
98
- #
99
- # # assert that no partials were rendered
100
- # assert_template partial: false
101
- #
102
- # # assert that a file was rendered
103
- # assert_template file: "README.rdoc"
104
- #
105
- # # assert that no file was rendered
106
- # assert_template file: nil
107
- # assert_template file: false
108
- #
109
- # In a view test case, you can also assert that specific locals are passed
110
- # to partials:
111
- #
112
- # # assert that the "_customer" partial was rendered with a specific object
113
- # assert_template partial: '_customer', locals: { customer: @customer }
114
- def assert_template(options = {}, message = nil)
115
- # Force body to be read in case the template is being streamed.
116
- response.body
117
-
118
- case options
119
- when NilClass, Regexp, String, Symbol
120
- options = options.to_s if Symbol === options
121
- rendered = @_templates
122
- msg = message || sprintf("expecting <%s> but rendering with <%s>",
123
- options.inspect, rendered.keys)
124
- matches_template =
125
- case options
126
- when String
127
- !options.empty? && rendered.any? do |t, num|
128
- options_splited = options.split(File::SEPARATOR)
129
- t_splited = t.split(File::SEPARATOR)
130
- t_splited.last(options_splited.size) == options_splited
131
- end
132
- when Regexp
133
- rendered.any? { |t,num| t.match(options) }
134
- when NilClass
135
- rendered.blank?
136
- end
137
- assert matches_template, msg
138
- when Hash
139
- options.assert_valid_keys(:layout, :partial, :locals, :count, :file)
140
-
141
- if options.key?(:layout)
142
- expected_layout = options[:layout]
143
- msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
144
- expected_layout, @_layouts.keys)
145
-
146
- case expected_layout
147
- when String, Symbol
148
- assert_includes @_layouts.keys, expected_layout.to_s, msg
149
- when Regexp
150
- assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
151
- when nil, false
152
- assert(@_layouts.empty?, msg)
153
- end
154
- end
155
-
156
- if options[:file]
157
- assert_includes @_files.keys, options[:file]
158
- elsif options.key?(:file)
159
- assert @_files.blank?, "expected no files but #{@_files.keys} was rendered"
160
- end
161
-
162
- if expected_partial = options[:partial]
163
- if expected_locals = options[:locals]
164
- if defined?(@_rendered_views)
165
- view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
54
+ def initialize(env, session, controller_class)
55
+ super(env)
166
56
 
167
- partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
168
- assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
169
-
170
- msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
171
- expected_locals,
172
- @_rendered_views.locals_for(view)]
173
- assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
174
- else
175
- warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
176
- end
177
- elsif expected_count = options[:count]
178
- actual_count = @_partials[expected_partial]
179
- msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
180
- expected_partial, expected_count, actual_count)
181
- assert(actual_count == expected_count.to_i, msg)
182
- else
183
- msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
184
- options[:partial], @_partials.keys)
185
- assert_includes @_partials, expected_partial, msg
186
- end
187
- elsif options.key?(:partial)
188
- assert @_partials.empty?,
189
- "Expected no partials to be rendered"
190
- end
191
- else
192
- raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
193
- 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
+ }
194
63
  end
195
- end
196
64
 
197
- class TestRequest < ActionDispatch::TestRequest #:nodoc:
198
- DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
199
- DEFAULT_ENV.delete 'PATH_INFO'
200
-
201
- def initialize(env = {})
202
- super
65
+ def query_string=(string)
66
+ set_header Rack::QUERY_STRING, string
67
+ end
203
68
 
204
- self.session = TestSession.new
205
- self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
69
+ def content_type=(type)
70
+ set_header "CONTENT_TYPE", type
206
71
  end
207
72
 
208
- def assign_parameters(routes, controller_path, action, parameters = {})
209
- parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
210
- extra_keys = routes.extra_keys(parameters)
211
- non_path_parameters = get? ? query_parameters : request_parameters
212
- parameters.each do |key, value|
213
- if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
214
- value = value.map{ |v| v.duplicable? ? v.dup : v }
215
- elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
216
- value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
217
- elsif value.frozen? && value.duplicable?
218
- value = value.dup
219
- end
73
+ def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
74
+ non_path_parameters = {}
75
+ path_parameters = {}
220
76
 
221
- if extra_keys.include?(key)
77
+ parameters.each do |key, value|
78
+ if query_string_keys.include?(key)
222
79
  non_path_parameters[key] = value
223
80
  else
224
81
  if value.is_a?(Array)
@@ -227,88 +84,104 @@ module ActionController
227
84
  value = value.to_param
228
85
  end
229
86
 
230
- path_parameters[key] = value
87
+ path_parameters[key.to_sym] = value
231
88
  end
232
89
  end
233
90
 
234
- # Clear the combined params hash in case it was already referenced.
235
- @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
103
+
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
236
118
 
237
- # Clear the filter cache variables so they're not stale
238
- @filtered_parameters = @filtered_env = @filtered_path = nil
119
+ data_stream = StringIO.new(data)
120
+ set_header "CONTENT_LENGTH", data_stream.length.to_s
121
+ set_header "rack.input", data_stream
122
+ end
239
123
 
240
- params = self.request_parameters.dup
241
- %w(controller action only_path).each do |k|
242
- params.delete(k)
243
- params.delete(k.to_sym)
124
+ fetch_header("PATH_INFO") do |k|
125
+ set_header k, generated_path
244
126
  end
245
- data = params.to_query
127
+ path_parameters[:controller] = controller_path
128
+ path_parameters[:action] = action
246
129
 
247
- @env['CONTENT_LENGTH'] = data.length.to_s
248
- @env['rack.input'] = StringIO.new(data)
130
+ self.path_parameters = path_parameters
249
131
  end
250
132
 
251
- def recycle!
252
- @formats = nil
253
- @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
254
- @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
255
- @method = @request_method = nil
256
- @fullpath = @ip = @remote_ip = @protocol = nil
257
- @env['action_dispatch.request.query_parameters'] = {}
258
- @set_cookies ||= {}
259
- @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
260
- deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
261
- @set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
262
- cookie_jar.update(rack_cookies)
263
- cookie_jar.update(cookies)
264
- cookie_jar.update(@set_cookies)
265
- cookie_jar.recycle!
266
- end
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
267
152
 
268
- private
153
+ public :build_multipart
269
154
 
270
- def default_env
271
- DEFAULT_ENV
272
- end
273
- end
155
+ def content_type
156
+ "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
157
+ end
158
+ end.new
274
159
 
275
- class TestResponse < ActionDispatch::TestResponse
276
- def recycle!
277
- initialize
278
- end
160
+ private
161
+ def params_parsers
162
+ super.merge @custom_param_parsers
163
+ end
279
164
  end
280
165
 
281
166
  class LiveTestResponse < Live::Response
282
- def recycle!
283
- @body = nil
284
- initialize
285
- end
286
-
287
- def body
288
- @body ||= super
289
- end
290
-
291
167
  # Was the response successful?
292
168
  alias_method :success?, :successful?
293
169
 
294
170
  # Was the URL not found?
295
171
  alias_method :missing?, :not_found?
296
172
 
297
- # Were we redirected?
298
- alias_method :redirect?, :redirection?
299
-
300
173
  # Was there a server-side error?
301
174
  alias_method :error?, :server_error?
302
175
  end
303
176
 
304
177
  # Methods #destroy and #load! are overridden to avoid calling methods on the
305
178
  # @store object, which does not exist for the TestSession class.
306
- class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
307
- DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
179
+ class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash #:nodoc:
180
+ DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
308
181
 
309
182
  def initialize(session = {})
310
183
  super(nil, nil)
311
- @id = SecureRandom.hex(16)
184
+ @id = Rack::Session::SessionId.new(SecureRandom.hex(16))
312
185
  @data = stringify_keys(session)
313
186
  @loaded = true
314
187
  end
@@ -329,22 +202,34 @@ module ActionController
329
202
  clear
330
203
  end
331
204
 
205
+ def dig(*keys)
206
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
207
+ @data.dig(*keys)
208
+ end
209
+
332
210
  def fetch(key, *args, &block)
333
211
  @data.fetch(key.to_s, *args, &block)
334
212
  end
335
213
 
336
214
  private
337
-
338
215
  def load!
339
216
  @id
340
217
  end
341
218
  end
342
219
 
343
220
  # Superclass for ActionController functional tests. Functional tests allow you to
344
- # test a single controller action per test method. This should not be confused with
345
- # integration tests (see ActionDispatch::IntegrationTest), which are more like
346
- # "stories" that can involve multiple controllers and multiple actions (i.e. multiple
347
- # different HTTP requests).
221
+ # test a single controller action per test method.
222
+ #
223
+ # == Use integration style controller tests over functional style controller tests.
224
+ #
225
+ # Rails discourages the use of functional tests in favor of integration tests
226
+ # (use ActionDispatch::IntegrationTest).
227
+ #
228
+ # New Rails applications no longer generate functional style controller tests and they should
229
+ # only be used for backward compatibility. Integration style controller tests perform actual
230
+ # requests, whereas functional style controller tests merely simulate a request. Besides,
231
+ # integration tests are as fast as functional tests and provide lot of helpers such as +as+,
232
+ # +parsed_body+ for effective testing of controller actions including even API endpoints.
348
233
  #
349
234
  # == Basic example
350
235
  #
@@ -359,13 +244,13 @@ module ActionController
359
244
  # class BooksControllerTest < ActionController::TestCase
360
245
  # def test_create
361
246
  # # Simulate a POST response with the given HTTP parameters.
362
- # post(:create, book: { title: "Love Hina" })
247
+ # post(:create, params: { book: { title: "Love Hina" }})
363
248
  #
364
- # # Assert that the controller tried to redirect us to
249
+ # # Asserts that the controller tried to redirect us to
365
250
  # # the created book's URI.
366
251
  # assert_response :found
367
252
  #
368
- # # Assert that the controller really put the book in the database.
253
+ # # Asserts that the controller really put the book in the database.
369
254
  # assert_not_nil Book.find_by(title: "Love Hina")
370
255
  # end
371
256
  # end
@@ -374,7 +259,7 @@ module ActionController
374
259
  #
375
260
  # def test_create
376
261
  # json = {book: { title: "Love Hina" }}.to_json
377
- # post :create, json
262
+ # post :create, body: json
378
263
  # end
379
264
  #
380
265
  # == Special instance variables
@@ -389,14 +274,11 @@ module ActionController
389
274
  # request. You can modify this object before sending the HTTP request. For example,
390
275
  # you might want to set some session properties before sending a GET request.
391
276
  # <b>@response</b>::
392
- # An ActionController::TestResponse object, representing the response
277
+ # An ActionDispatch::TestResponse object, representing the response
393
278
  # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
394
279
  # after calling +post+. If the various assert methods are not sufficient, then you
395
280
  # may use this object to inspect the HTTP response in detail.
396
281
  #
397
- # (Earlier versions of \Rails required each functional test to subclass
398
- # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
399
- #
400
282
  # == Controller is automatically inferred
401
283
  #
402
284
  # ActionController::TestCase will automatically infer the controller under test
@@ -412,22 +294,16 @@ module ActionController
412
294
  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
413
295
  # can be used against. These collections are:
414
296
  #
415
- # * assigns: Instance variables assigned in the action that are available for the view.
416
297
  # * session: Objects being saved in the session.
417
298
  # * flash: The flash objects currently in the session.
418
299
  # * cookies: \Cookies being sent to the user on this request.
419
300
  #
420
301
  # These collections can be used just like any other hash:
421
302
  #
422
- # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
423
303
  # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
424
304
  # assert flash.empty? # makes sure that there's nothing in the flash
425
305
  #
426
- # 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
427
- # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
428
- # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
429
- #
430
- # 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>.
431
307
  #
432
308
  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
433
309
  # action call which can then be asserted against.
@@ -459,7 +335,6 @@ module ActionController
459
335
  attr_reader :response, :request
460
336
 
461
337
  module ClassMethods
462
-
463
338
  # Sets the controller class name. Useful if the name can't be inferred from test class.
464
339
  # Normalizes +controller_class+ before using.
465
340
  #
@@ -482,7 +357,7 @@ module ActionController
482
357
  end
483
358
 
484
359
  def controller_class
485
- if current_controller_class = self._controller_class
360
+ if current_controller_class = _controller_class
486
361
  current_controller_class
487
362
  else
488
363
  self.controller_class = determine_default_controller_class(name)
@@ -499,169 +374,148 @@ module ActionController
499
374
  # Simulate a GET request with the given parameters.
500
375
  #
501
376
  # - +action+: The controller action to call.
502
- # - +parameters+: The HTTP parameters that you want to pass. This may
503
- # be +nil+, a hash, or a string that is appropriately encoded
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
504
379
  # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
505
380
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
506
381
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
507
382
  #
508
383
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
509
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' }
510
391
  #
511
392
  # Note that the request method is not verified. The different methods are
512
393
  # available to make the tests more expressive.
513
- def get(action, *args)
514
- process(action, "GET", *args)
394
+ def get(action, **args)
395
+ res = process(action, method: "GET", **args)
396
+ cookies.update res.cookies
397
+ res
515
398
  end
516
399
 
517
400
  # Simulate a POST request with the given parameters and set/volley the response.
518
401
  # See +get+ for more details.
519
- def post(action, *args)
520
- process(action, "POST", *args)
402
+ def post(action, **args)
403
+ process(action, method: "POST", **args)
521
404
  end
522
405
 
523
406
  # Simulate a PATCH request with the given parameters and set/volley the response.
524
407
  # See +get+ for more details.
525
- def patch(action, *args)
526
- process(action, "PATCH", *args)
408
+ def patch(action, **args)
409
+ process(action, method: "PATCH", **args)
527
410
  end
528
411
 
529
412
  # Simulate a PUT request with the given parameters and set/volley the response.
530
413
  # See +get+ for more details.
531
- def put(action, *args)
532
- process(action, "PUT", *args)
414
+ def put(action, **args)
415
+ process(action, method: "PUT", **args)
533
416
  end
534
417
 
535
418
  # Simulate a DELETE request with the given parameters and set/volley the response.
536
419
  # See +get+ for more details.
537
- def delete(action, *args)
538
- process(action, "DELETE", *args)
420
+ def delete(action, **args)
421
+ process(action, method: "DELETE", **args)
539
422
  end
540
423
 
541
424
  # Simulate a HEAD request with the given parameters and set/volley the response.
542
425
  # See +get+ for more details.
543
- def head(action, *args)
544
- process(action, "HEAD", *args)
426
+ def head(action, **args)
427
+ process(action, method: "HEAD", **args)
545
428
  end
546
429
 
547
- def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
548
- @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
549
- @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
550
- __send__(request_method, action, parameters, session, flash).tap do
551
- @request.env.delete 'HTTP_X_REQUESTED_WITH'
552
- @request.env.delete 'HTTP_ACCEPT'
553
- end
554
- end
555
- alias xhr :xml_http_request
556
-
557
- def paramify_values(hash_or_array_or_value)
558
- case hash_or_array_or_value
559
- when Hash
560
- Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
561
- when Array
562
- hash_or_array_or_value.map {|i| paramify_values(i)}
563
- when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
564
- hash_or_array_or_value
565
- else
566
- hash_or_array_or_value.to_param
567
- end
568
- end
569
-
570
- # Simulate a HTTP request to +action+ by specifying request method,
430
+ # Simulate an HTTP request to +action+ by specifying request method,
571
431
  # parameters and set/volley the response.
572
432
  #
573
433
  # - +action+: The controller action to call.
574
- # - +http_method+: Request method used to send the http request. Possible values
575
- # are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+.
576
- # - +parameters+: The HTTP parameters. This may be +nil+, a hash, or a
577
- # string that is appropriately encoded (+application/x-www-form-urlencoded+
578
- # or +multipart/form-data+).
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>).
579
439
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
580
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.
581
444
  #
582
445
  # Example calling +create+ action and sending two params:
583
446
  #
584
- # process :create, 'POST', user: { name: 'Gaurish Sharma', email: 'user@example.com' }
585
- #
586
- # Example sending parameters, +nil+ session and setting a flash message:
587
- #
588
- # process :view, 'GET', { id: 7 }, nil, { notice: 'This is flash message' }
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' }
589
454
  #
590
455
  # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
591
456
  # prefer using #get, #post, #patch, #put, #delete and #head methods
592
457
  # respectively which will make tests more expressive.
593
458
  #
594
459
  # Note that the request method is not verified.
595
- def process(action, http_method = 'GET', *args)
460
+ def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
596
461
  check_required_ivars
597
462
 
598
- if args.first.is_a?(String) && http_method != 'HEAD'
599
- @request.env['RAW_POST_DATA'] = args.shift
600
- end
601
-
602
- parameters, session, flash = args
603
- parameters ||= {}
604
-
605
- # Ensure that numbers and symbols passed as params are converted to
606
- # proper params, as is the case when engaging rack.
607
- parameters = paramify_values(parameters) if html_format?(parameters)
463
+ action = +action.to_s
464
+ http_method = method.to_s.upcase
608
465
 
609
466
  @html_document = nil
610
- @html_scanner_document = nil
611
467
 
612
- unless @controller.respond_to?(:recycle!)
613
- @controller.extend(Testing::Functional)
614
- end
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"
615
472
 
616
- @request.recycle!
617
- @response.recycle!
473
+ @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class
474
+ @response = build_response @response_klass
475
+ @response.request = @request
618
476
  @controller.recycle!
619
477
 
620
- @request.env['REQUEST_METHOD'] = http_method
621
-
622
- controller_class_name = @controller.class.anonymous? ?
623
- "anonymous" :
624
- @controller.class.controller_path
625
-
626
- @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
627
-
628
- @request.session.update(session) if session
629
- @request.flash.update(flash || {})
478
+ if body
479
+ @request.set_header "RAW_POST_DATA", body
480
+ end
630
481
 
631
- @controller.request = @request
632
- @controller.response = @response
482
+ @request.set_header "REQUEST_METHOD", http_method
633
483
 
634
- build_request_uri(action, parameters)
484
+ if as
485
+ @request.content_type = Mime[as].to_s
486
+ format ||= as
487
+ end
635
488
 
636
- name = @request.parameters[:action]
489
+ parameters = (params || {}).symbolize_keys
637
490
 
638
- @controller.recycle!
639
- @controller.process(name)
640
-
641
- if cookies = @request.env['action_dispatch.cookies']
642
- unless @response.committed?
643
- cookies.write(@response)
644
- end
491
+ if format
492
+ parameters[:format] = format
645
493
  end
646
- @response.prepare!
647
494
 
648
- @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
495
+ setup_request(controller_class_name, action, parameters, session, flash, xhr)
496
+ process_controller_response(action, cookies, xhr)
497
+ end
649
498
 
650
- if flash_value = @request.flash.to_session_value
651
- @request.session['flash'] = flash_value
652
- end
499
+ def controller_class_name
500
+ @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
501
+ end
653
502
 
654
- @response
503
+ def generated_path(generated_extras)
504
+ generated_extras[0]
505
+ end
506
+
507
+ def query_parameter_names(generated_extras)
508
+ generated_extras[1] + [:controller, :action]
655
509
  end
656
510
 
657
511
  def setup_controller_request_and_response
658
512
  @controller = nil unless defined? @controller
659
513
 
660
- response_klass = TestResponse
514
+ @response_klass = ActionDispatch::TestResponse
661
515
 
662
516
  if klass = self.class.controller_class
663
517
  if klass < ActionController::Live
664
- response_klass = LiveTestResponse
518
+ @response_klass = LiveTestResponse
665
519
  end
666
520
  unless @controller
667
521
  begin
@@ -672,8 +526,8 @@ module ActionController
672
526
  end
673
527
  end
674
528
 
675
- @request = build_request
676
- @response = build_response response_klass
529
+ @request = TestRequest.create(@controller.class)
530
+ @response = build_response @response_klass
677
531
  @response.request = @request
678
532
 
679
533
  if @controller
@@ -682,12 +536,8 @@ module ActionController
682
536
  end
683
537
  end
684
538
 
685
- def build_request
686
- TestRequest.new
687
- end
688
-
689
539
  def build_response(klass)
690
- klass.new
540
+ klass.create
691
541
  end
692
542
 
693
543
  included do
@@ -695,65 +545,89 @@ module ActionController
695
545
  include ActionDispatch::Assertions
696
546
  class_attribute :_controller_class
697
547
  setup :setup_controller_request_and_response
548
+ ActiveSupport.run_load_hooks(:action_controller_test_case, self)
698
549
  end
699
550
 
700
551
  private
552
+ def setup_request(controller_class_name, action, parameters, session, flash, xhr)
553
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
554
+ generated_path = generated_path(generated_extras)
555
+ query_string_keys = query_parameter_names(generated_extras)
701
556
 
702
- def document_root_element
703
- html_document.root
704
- end
557
+ @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
558
+
559
+ @request.session.update(session) if session
560
+ @request.flash.update(flash || {})
561
+
562
+ if xhr
563
+ @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
564
+ @request.fetch_header("HTTP_ACCEPT") do |k|
565
+ @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
566
+ end
567
+ end
705
568
 
706
- def check_required_ivars
707
- # Sanity check for required instance variables so we can give an
708
- # understandable error message.
709
- [:@routes, :@controller, :@request, :@response].each do |iv_name|
710
- if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
711
- raise "#{iv_name} is nil: make sure you set it in your test's setup method."
569
+ @request.fetch_header("SCRIPT_NAME") do |k|
570
+ @request.set_header k, @controller.config.relative_url_root
712
571
  end
713
572
  end
714
- end
715
573
 
716
- def build_request_uri(action, parameters)
717
- unless @request.env["PATH_INFO"]
718
- options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
719
- options.update(
720
- :action => action,
721
- :relative_url_root => nil,
722
- :_recall => @request.path_parameters)
723
-
724
- if route_name = options.delete(:use_route)
725
- ActiveSupport::Deprecation.warn <<-MSG.squish
726
- Passing the `use_route` option in functional tests are deprecated.
727
- Support for this option in the `process` method (and the related
728
- `get`, `head`, `post`, `patch`, `put` and `delete` helpers) will
729
- be removed in the next version without replacement.
730
-
731
- Functional tests are essentially unit tests for controllers and
732
- they should not require knowledge to how the application's routes
733
- are configured. Instead, you should explicitly pass the appropiate
734
- params to the `process` method.
735
-
736
- Previously the engines guide also contained an incorrect example
737
- that recommended using this option to test an engine's controllers
738
- within the dummy application. That recommendation was incorrect
739
- and has since been corrected. Instead, you should override the
740
- `@routes` variable in the test case with `Foo::Engine.routes`. See
741
- the updated engines guide for details.
742
- MSG
574
+ def process_controller_response(action, cookies, xhr)
575
+ begin
576
+ @controller.recycle!
577
+ @controller.dispatch(action, @request, @response)
578
+ ensure
579
+ @request = @controller.request
580
+ @response = @controller.response
581
+
582
+ if @request.have_cookie_jar?
583
+ unless @request.cookie_jar.committed?
584
+ @request.cookie_jar.write(@response)
585
+ cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
586
+ end
587
+ end
588
+ @response.prepare!
589
+
590
+ if flash_value = @request.flash.to_session_value
591
+ @request.session["flash"] = flash_value
592
+ else
593
+ @request.session.delete("flash")
594
+ end
595
+
596
+ if xhr
597
+ @request.delete_header "HTTP_X_REQUESTED_WITH"
598
+ @request.delete_header "HTTP_ACCEPT"
599
+ end
600
+ @request.query_string = ""
601
+
602
+ @response.sent!
743
603
  end
744
604
 
745
- url, query_string = @routes.path_for(options, route_name).split("?", 2)
605
+ @response
606
+ end
746
607
 
747
- @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
748
- @request.env["PATH_INFO"] = url
749
- @request.env["QUERY_STRING"] = query_string || ""
608
+ def scrub_env!(env)
609
+ env.delete_if do |k, _|
610
+ k.start_with?("rack.request", "action_dispatch.request", "action_dispatch.rescue")
611
+ end
612
+ env["rack.input"] = StringIO.new
613
+ env.delete "CONTENT_LENGTH"
614
+ env.delete "RAW_POST_DATA"
615
+ env
750
616
  end
751
- end
752
617
 
753
- def html_format?(parameters)
754
- return true unless parameters.key?(:format)
755
- Mime.fetch(parameters[:format]) { Mime['html'] }.html?
756
- end
618
+ def document_root_element
619
+ html_document.root
620
+ end
621
+
622
+ def check_required_ivars
623
+ # Sanity check for required instance variables so we can give an
624
+ # understandable error message.
625
+ [:@routes, :@controller, :@request, :@response].each do |iv_name|
626
+ if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
627
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
628
+ end
629
+ end
630
+ end
757
631
  end
758
632
 
759
633
  include Behavior