actionpack 4.2.10 → 6.1.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 (187) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +311 -461
  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 +110 -119
  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 +84 -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 +298 -421
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +108 -58
  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 +82 -50
  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 +91 -44
  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 +16 -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 +5 -3
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +75 -46
  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,84 @@
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
17
27
 
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 =~ /^.*\/_[^\/]*$/
28
+ # Avoid a deadlock from the queue filling up
29
+ Buffer.queue_size = nil
30
+ end
40
31
 
41
- if partial
42
- @_partials[virtual_path] += 1
43
- @_partials[virtual_path.split("/").last] += 1
44
- end
32
+ # ActionController::TestCase will be deprecated and moved to a gem in the future.
33
+ # Please use ActionDispatch::IntegrationTest going forward.
34
+ class TestRequest < ActionDispatch::TestRequest #:nodoc:
35
+ DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
36
+ DEFAULT_ENV.delete "PATH_INFO"
45
37
 
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
38
+ def self.new_session
39
+ TestSession.new
55
40
  end
56
41
 
57
- def teardown_subscriptions
58
- return unless defined?(@_subscribers)
42
+ attr_reader :controller_class
59
43
 
60
- @_subscribers.each do |subscriber|
61
- ActiveSupport::Notifications.unsubscribe(subscriber)
62
- end
44
+ # Create a new test request with default `env` values.
45
+ def self.create(controller_class)
46
+ env = {}
47
+ env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
48
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
49
+ new(default_env.merge(env), new_session, controller_class)
63
50
  end
64
51
 
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
52
+ def self.default_env
53
+ DEFAULT_ENV
77
54
  end
55
+ private_class_method :default_env
78
56
 
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)/, '/')
57
+ def initialize(env, session, controller_class)
58
+ super(env)
166
59
 
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
60
+ self.session = session
61
+ self.session_options = TestSession::DEFAULT_OPTIONS.dup
62
+ @controller_class = controller_class
63
+ @custom_param_parsers = {
64
+ xml: lambda { |raw_post| Hash.from_xml(raw_post)["hash"] }
65
+ }
194
66
  end
195
- end
196
67
 
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
68
+ def query_string=(string)
69
+ set_header Rack::QUERY_STRING, string
70
+ end
203
71
 
204
- self.session = TestSession.new
205
- self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
72
+ def content_type=(type)
73
+ set_header "CONTENT_TYPE", type
206
74
  end
207
75
 
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
76
+ def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
77
+ non_path_parameters = {}
78
+ path_parameters = {}
220
79
 
221
- if extra_keys.include?(key)
80
+ parameters.each do |key, value|
81
+ if query_string_keys.include?(key)
222
82
  non_path_parameters[key] = value
223
83
  else
224
84
  if value.is_a?(Array)
@@ -227,88 +87,104 @@ module ActionController
227
87
  value = value.to_param
228
88
  end
229
89
 
230
- path_parameters[key] = value
90
+ path_parameters[key.to_sym] = value
231
91
  end
232
92
  end
233
93
 
234
- # Clear the combined params hash in case it was already referenced.
235
- @env.delete("action_dispatch.request.parameters")
94
+ if get?
95
+ if query_string.blank?
96
+ self.query_string = non_path_parameters.to_query
97
+ end
98
+ else
99
+ if ENCODER.should_multipart?(non_path_parameters)
100
+ self.content_type = ENCODER.content_type
101
+ data = ENCODER.build_multipart non_path_parameters
102
+ else
103
+ fetch_header("CONTENT_TYPE") do |k|
104
+ set_header k, "application/x-www-form-urlencoded"
105
+ end
106
+
107
+ case content_mime_type.to_sym
108
+ when nil
109
+ raise "Unknown Content-Type: #{content_type}"
110
+ when :json
111
+ data = ActiveSupport::JSON.encode(non_path_parameters)
112
+ when :xml
113
+ data = non_path_parameters.to_xml
114
+ when :url_encoded_form
115
+ data = non_path_parameters.to_query
116
+ else
117
+ @custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
118
+ data = non_path_parameters.to_query
119
+ end
120
+ end
236
121
 
237
- # Clear the filter cache variables so they're not stale
238
- @filtered_parameters = @filtered_env = @filtered_path = nil
122
+ data_stream = StringIO.new(data)
123
+ set_header "CONTENT_LENGTH", data_stream.length.to_s
124
+ set_header "rack.input", data_stream
125
+ end
239
126
 
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)
127
+ fetch_header("PATH_INFO") do |k|
128
+ set_header k, generated_path
244
129
  end
245
- data = params.to_query
130
+ path_parameters[:controller] = controller_path
131
+ path_parameters[:action] = action
246
132
 
247
- @env['CONTENT_LENGTH'] = data.length.to_s
248
- @env['rack.input'] = StringIO.new(data)
133
+ self.path_parameters = path_parameters
249
134
  end
250
135
 
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
136
+ ENCODER = Class.new do
137
+ include Rack::Test::Utils
138
+
139
+ def should_multipart?(params)
140
+ # FIXME: lifted from Rack-Test. We should push this separation upstream.
141
+ multipart = false
142
+ query = lambda { |value|
143
+ case value
144
+ when Array
145
+ value.each(&query)
146
+ when Hash
147
+ value.values.each(&query)
148
+ when Rack::Test::UploadedFile
149
+ multipart = true
150
+ end
151
+ }
152
+ params.values.each(&query)
153
+ multipart
154
+ end
267
155
 
268
- private
156
+ public :build_multipart
269
157
 
270
- def default_env
271
- DEFAULT_ENV
272
- end
273
- end
158
+ def content_type
159
+ "multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
160
+ end
161
+ end.new
274
162
 
275
- class TestResponse < ActionDispatch::TestResponse
276
- def recycle!
277
- initialize
278
- end
163
+ private
164
+ def params_parsers
165
+ super.merge @custom_param_parsers
166
+ end
279
167
  end
280
168
 
281
169
  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
170
  # Was the response successful?
292
171
  alias_method :success?, :successful?
293
172
 
294
173
  # Was the URL not found?
295
174
  alias_method :missing?, :not_found?
296
175
 
297
- # Were we redirected?
298
- alias_method :redirect?, :redirection?
299
-
300
176
  # Was there a server-side error?
301
177
  alias_method :error?, :server_error?
302
178
  end
303
179
 
304
180
  # Methods #destroy and #load! are overridden to avoid calling methods on the
305
181
  # @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
182
+ class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash #:nodoc:
183
+ DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
308
184
 
309
185
  def initialize(session = {})
310
186
  super(nil, nil)
311
- @id = SecureRandom.hex(16)
187
+ @id = Rack::Session::SessionId.new(SecureRandom.hex(16))
312
188
  @data = stringify_keys(session)
313
189
  @loaded = true
314
190
  end
@@ -329,22 +205,34 @@ module ActionController
329
205
  clear
330
206
  end
331
207
 
208
+ def dig(*keys)
209
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
210
+ @data.dig(*keys)
211
+ end
212
+
332
213
  def fetch(key, *args, &block)
333
214
  @data.fetch(key.to_s, *args, &block)
334
215
  end
335
216
 
336
217
  private
337
-
338
218
  def load!
339
219
  @id
340
220
  end
341
221
  end
342
222
 
343
223
  # 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).
224
+ # test a single controller action per test method.
225
+ #
226
+ # == Use integration style controller tests over functional style controller tests.
227
+ #
228
+ # Rails discourages the use of functional tests in favor of integration tests
229
+ # (use ActionDispatch::IntegrationTest).
230
+ #
231
+ # New Rails applications no longer generate functional style controller tests and they should
232
+ # only be used for backward compatibility. Integration style controller tests perform actual
233
+ # requests, whereas functional style controller tests merely simulate a request. Besides,
234
+ # integration tests are as fast as functional tests and provide lot of helpers such as +as+,
235
+ # +parsed_body+ for effective testing of controller actions including even API endpoints.
348
236
  #
349
237
  # == Basic example
350
238
  #
@@ -359,13 +247,13 @@ module ActionController
359
247
  # class BooksControllerTest < ActionController::TestCase
360
248
  # def test_create
361
249
  # # Simulate a POST response with the given HTTP parameters.
362
- # post(:create, book: { title: "Love Hina" })
250
+ # post(:create, params: { book: { title: "Love Hina" }})
363
251
  #
364
- # # Assert that the controller tried to redirect us to
252
+ # # Asserts that the controller tried to redirect us to
365
253
  # # the created book's URI.
366
254
  # assert_response :found
367
255
  #
368
- # # Assert that the controller really put the book in the database.
256
+ # # Asserts that the controller really put the book in the database.
369
257
  # assert_not_nil Book.find_by(title: "Love Hina")
370
258
  # end
371
259
  # end
@@ -374,7 +262,7 @@ module ActionController
374
262
  #
375
263
  # def test_create
376
264
  # json = {book: { title: "Love Hina" }}.to_json
377
- # post :create, json
265
+ # post :create, body: json
378
266
  # end
379
267
  #
380
268
  # == Special instance variables
@@ -389,14 +277,11 @@ module ActionController
389
277
  # request. You can modify this object before sending the HTTP request. For example,
390
278
  # you might want to set some session properties before sending a GET request.
391
279
  # <b>@response</b>::
392
- # An ActionController::TestResponse object, representing the response
280
+ # An ActionDispatch::TestResponse object, representing the response
393
281
  # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
394
282
  # after calling +post+. If the various assert methods are not sufficient, then you
395
283
  # may use this object to inspect the HTTP response in detail.
396
284
  #
397
- # (Earlier versions of \Rails required each functional test to subclass
398
- # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
399
- #
400
285
  # == Controller is automatically inferred
401
286
  #
402
287
  # ActionController::TestCase will automatically infer the controller under test
@@ -412,22 +297,16 @@ module ActionController
412
297
  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
413
298
  # can be used against. These collections are:
414
299
  #
415
- # * assigns: Instance variables assigned in the action that are available for the view.
416
300
  # * session: Objects being saved in the session.
417
301
  # * flash: The flash objects currently in the session.
418
302
  # * cookies: \Cookies being sent to the user on this request.
419
303
  #
420
304
  # These collections can be used just like any other hash:
421
305
  #
422
- # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
423
306
  # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
424
307
  # assert flash.empty? # makes sure that there's nothing in the flash
425
308
  #
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>.
309
+ # On top of the collections, you have the complete URL that a given action redirected to available in <tt>redirect_to_url</tt>.
431
310
  #
432
311
  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
433
312
  # action call which can then be asserted against.
@@ -459,7 +338,6 @@ module ActionController
459
338
  attr_reader :response, :request
460
339
 
461
340
  module ClassMethods
462
-
463
341
  # Sets the controller class name. Useful if the name can't be inferred from test class.
464
342
  # Normalizes +controller_class+ before using.
465
343
  #
@@ -482,7 +360,7 @@ module ActionController
482
360
  end
483
361
 
484
362
  def controller_class
485
- if current_controller_class = self._controller_class
363
+ if current_controller_class = _controller_class
486
364
  current_controller_class
487
365
  else
488
366
  self.controller_class = determine_default_controller_class(name)
@@ -499,169 +377,148 @@ module ActionController
499
377
  # Simulate a GET request with the given parameters.
500
378
  #
501
379
  # - +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
380
+ # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
381
+ # - +body+: The request body with a string that is appropriately encoded
504
382
  # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
505
383
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
506
384
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
507
385
  #
508
386
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
509
387
  # +post+, +patch+, +put+, +delete+, and +head+.
388
+ # Example sending parameters, session and setting a flash message:
389
+ #
390
+ # get :show,
391
+ # params: { id: 7 },
392
+ # session: { user_id: 1 },
393
+ # flash: { notice: 'This is flash message' }
510
394
  #
511
395
  # Note that the request method is not verified. The different methods are
512
396
  # available to make the tests more expressive.
513
- def get(action, *args)
514
- process(action, "GET", *args)
397
+ def get(action, **args)
398
+ res = process(action, method: "GET", **args)
399
+ cookies.update res.cookies
400
+ res
515
401
  end
516
402
 
517
403
  # Simulate a POST request with the given parameters and set/volley the response.
518
404
  # See +get+ for more details.
519
- def post(action, *args)
520
- process(action, "POST", *args)
405
+ def post(action, **args)
406
+ process(action, method: "POST", **args)
521
407
  end
522
408
 
523
409
  # Simulate a PATCH request with the given parameters and set/volley the response.
524
410
  # See +get+ for more details.
525
- def patch(action, *args)
526
- process(action, "PATCH", *args)
411
+ def patch(action, **args)
412
+ process(action, method: "PATCH", **args)
527
413
  end
528
414
 
529
415
  # Simulate a PUT request with the given parameters and set/volley the response.
530
416
  # See +get+ for more details.
531
- def put(action, *args)
532
- process(action, "PUT", *args)
417
+ def put(action, **args)
418
+ process(action, method: "PUT", **args)
533
419
  end
534
420
 
535
421
  # Simulate a DELETE request with the given parameters and set/volley the response.
536
422
  # See +get+ for more details.
537
- def delete(action, *args)
538
- process(action, "DELETE", *args)
423
+ def delete(action, **args)
424
+ process(action, method: "DELETE", **args)
539
425
  end
540
426
 
541
427
  # Simulate a HEAD request with the given parameters and set/volley the response.
542
428
  # See +get+ for more details.
543
- def head(action, *args)
544
- process(action, "HEAD", *args)
429
+ def head(action, **args)
430
+ process(action, method: "HEAD", **args)
545
431
  end
546
432
 
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,
433
+ # Simulate an HTTP request to +action+ by specifying request method,
571
434
  # parameters and set/volley the response.
572
435
  #
573
436
  # - +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+).
437
+ # - +method+: Request method used to send the HTTP request. Possible values
438
+ # are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+. Can be a symbol.
439
+ # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
440
+ # - +body+: The request body with a string that is appropriately encoded
441
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
579
442
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
580
443
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
444
+ # - +format+: Request format. Defaults to +nil+. Can be string or symbol.
445
+ # - +as+: Content type. Defaults to +nil+. Must be a symbol that corresponds
446
+ # to a mime type.
581
447
  #
582
448
  # Example calling +create+ action and sending two params:
583
449
  #
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' }
450
+ # process :create,
451
+ # method: 'POST',
452
+ # params: {
453
+ # user: { name: 'Gaurish Sharma', email: 'user@example.com' }
454
+ # },
455
+ # session: { user_id: 1 },
456
+ # flash: { notice: 'This is flash message' }
589
457
  #
590
458
  # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
591
459
  # prefer using #get, #post, #patch, #put, #delete and #head methods
592
460
  # respectively which will make tests more expressive.
593
461
  #
594
462
  # Note that the request method is not verified.
595
- def process(action, http_method = 'GET', *args)
463
+ def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
596
464
  check_required_ivars
597
465
 
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)
466
+ action = +action.to_s
467
+ http_method = method.to_s.upcase
608
468
 
609
469
  @html_document = nil
610
- @html_scanner_document = nil
611
470
 
612
- unless @controller.respond_to?(:recycle!)
613
- @controller.extend(Testing::Functional)
614
- end
471
+ cookies.update(@request.cookies)
472
+ cookies.update_cookies_from_jar
473
+ @request.set_header "HTTP_COOKIE", cookies.to_header
474
+ @request.delete_header "action_dispatch.cookies"
615
475
 
616
- @request.recycle!
617
- @response.recycle!
476
+ @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class
477
+ @response = build_response @response_klass
478
+ @response.request = @request
618
479
  @controller.recycle!
619
480
 
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 || {})
481
+ if body
482
+ @request.set_header "RAW_POST_DATA", body
483
+ end
630
484
 
631
- @controller.request = @request
632
- @controller.response = @response
485
+ @request.set_header "REQUEST_METHOD", http_method
633
486
 
634
- build_request_uri(action, parameters)
487
+ if as
488
+ @request.content_type = Mime[as].to_s
489
+ format ||= as
490
+ end
635
491
 
636
- name = @request.parameters[:action]
492
+ parameters = (params || {}).symbolize_keys
637
493
 
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
494
+ if format
495
+ parameters[:format] = format
645
496
  end
646
- @response.prepare!
647
497
 
648
- @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
498
+ setup_request(controller_class_name, action, parameters, session, flash, xhr)
499
+ process_controller_response(action, cookies, xhr)
500
+ end
649
501
 
650
- if flash_value = @request.flash.to_session_value
651
- @request.session['flash'] = flash_value
652
- end
502
+ def controller_class_name
503
+ @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
504
+ end
653
505
 
654
- @response
506
+ def generated_path(generated_extras)
507
+ generated_extras[0]
508
+ end
509
+
510
+ def query_parameter_names(generated_extras)
511
+ generated_extras[1] + [:controller, :action]
655
512
  end
656
513
 
657
514
  def setup_controller_request_and_response
658
515
  @controller = nil unless defined? @controller
659
516
 
660
- response_klass = TestResponse
517
+ @response_klass = ActionDispatch::TestResponse
661
518
 
662
519
  if klass = self.class.controller_class
663
520
  if klass < ActionController::Live
664
- response_klass = LiveTestResponse
521
+ @response_klass = LiveTestResponse
665
522
  end
666
523
  unless @controller
667
524
  begin
@@ -672,8 +529,8 @@ module ActionController
672
529
  end
673
530
  end
674
531
 
675
- @request = build_request
676
- @response = build_response response_klass
532
+ @request = TestRequest.create(@controller.class)
533
+ @response = build_response @response_klass
677
534
  @response.request = @request
678
535
 
679
536
  if @controller
@@ -682,12 +539,8 @@ module ActionController
682
539
  end
683
540
  end
684
541
 
685
- def build_request
686
- TestRequest.new
687
- end
688
-
689
542
  def build_response(klass)
690
- klass.new
543
+ klass.create
691
544
  end
692
545
 
693
546
  included do
@@ -695,65 +548,89 @@ module ActionController
695
548
  include ActionDispatch::Assertions
696
549
  class_attribute :_controller_class
697
550
  setup :setup_controller_request_and_response
551
+ ActiveSupport.run_load_hooks(:action_controller_test_case, self)
698
552
  end
699
553
 
700
554
  private
555
+ def setup_request(controller_class_name, action, parameters, session, flash, xhr)
556
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
557
+ generated_path = generated_path(generated_extras)
558
+ query_string_keys = query_parameter_names(generated_extras)
701
559
 
702
- def document_root_element
703
- html_document.root
704
- end
560
+ @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
561
+
562
+ @request.session.update(session) if session
563
+ @request.flash.update(flash || {})
564
+
565
+ if xhr
566
+ @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
567
+ @request.fetch_header("HTTP_ACCEPT") do |k|
568
+ @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
569
+ end
570
+ end
705
571
 
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."
572
+ @request.fetch_header("SCRIPT_NAME") do |k|
573
+ @request.set_header k, @controller.config.relative_url_root
712
574
  end
713
575
  end
714
- end
715
576
 
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
577
+ def process_controller_response(action, cookies, xhr)
578
+ begin
579
+ @controller.recycle!
580
+ @controller.dispatch(action, @request, @response)
581
+ ensure
582
+ @request = @controller.request
583
+ @response = @controller.response
584
+
585
+ if @request.have_cookie_jar?
586
+ unless @request.cookie_jar.committed?
587
+ @request.cookie_jar.write(@response)
588
+ cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
589
+ end
590
+ end
591
+ @response.prepare!
592
+
593
+ if flash_value = @request.flash.to_session_value
594
+ @request.session["flash"] = flash_value
595
+ else
596
+ @request.session.delete("flash")
597
+ end
598
+
599
+ if xhr
600
+ @request.delete_header "HTTP_X_REQUESTED_WITH"
601
+ @request.delete_header "HTTP_ACCEPT"
602
+ end
603
+ @request.query_string = ""
604
+
605
+ @response.sent!
743
606
  end
744
607
 
745
- url, query_string = @routes.path_for(options, route_name).split("?", 2)
608
+ @response
609
+ end
746
610
 
747
- @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
748
- @request.env["PATH_INFO"] = url
749
- @request.env["QUERY_STRING"] = query_string || ""
611
+ def scrub_env!(env)
612
+ env.delete_if do |k, _|
613
+ k.start_with?("rack.request", "action_dispatch.request", "action_dispatch.rescue")
614
+ end
615
+ env["rack.input"] = StringIO.new
616
+ env.delete "CONTENT_LENGTH"
617
+ env.delete "RAW_POST_DATA"
618
+ env
750
619
  end
751
- end
752
620
 
753
- def html_format?(parameters)
754
- return true unless parameters.key?(:format)
755
- Mime.fetch(parameters[:format]) { Mime['html'] }.html?
756
- end
621
+ def document_root_element
622
+ html_document.root
623
+ end
624
+
625
+ def check_required_ivars
626
+ # Sanity check for required instance variables so we can give an
627
+ # understandable error message.
628
+ [:@routes, :@controller, :@request, :@response].each do |iv_name|
629
+ if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
630
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
631
+ end
632
+ end
633
+ end
757
634
  end
758
635
 
759
636
  include Behavior