actionpack 6.1.7.5 → 7.1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -13,42 +13,45 @@ module ActionDispatch
13
13
  # to investigate changes at different points during your test. These will be
14
14
  # named with a sequential prefix (or 'failed' for failing tests)
15
15
  #
16
- # The screenshot will be displayed in your console, if supported.
16
+ # The default screenshots directory is +tmp/screenshots+ but you can set a different
17
+ # one with +Capybara.save_path+
17
18
  #
18
- # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+ environment variable to
19
- # save the HTML from the page that is being screenhoted so you can investigate the
20
- # elements on the page at the time of the screenshot
19
+ # You can use the +html+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+
20
+ # environment variable to save the HTML from the page that is being screenshotted
21
+ # so you can investigate the elements on the page at the time of the screenshot
21
22
  #
22
- # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
23
- # control the output. Possible values are:
23
+ # You can use the +screenshot+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT+
24
+ # environment variable to control the output. Possible values are:
24
25
  # * [+simple+ (default)] Only displays the screenshot path.
25
26
  # This is the default value.
26
27
  # * [+inline+] Display the screenshot in the terminal using the
27
28
  # iTerm image protocol (https://iterm2.com/documentation-images.html).
28
29
  # * [+artifact+] Display the screenshot in the terminal, using the terminal
29
30
  # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
30
- def take_screenshot
31
+ def take_screenshot(html: false, screenshot: nil)
32
+ showing_html = html || html_from_env?
33
+
31
34
  increment_unique
32
- save_html if save_html?
35
+ save_html if showing_html
33
36
  save_image
34
- puts display_image
37
+ show display_image(html: showing_html, screenshot_output: screenshot)
35
38
  end
36
39
 
37
40
  # Takes a screenshot of the current page in the browser if the test
38
41
  # failed.
39
42
  #
40
- # +take_failed_screenshot+ is included in <tt>application_system_test_case.rb</tt>
41
- # that is generated with the application. To take screenshots when a test
42
- # fails add +take_failed_screenshot+ to the teardown block before clearing
43
- # sessions.
43
+ # +take_failed_screenshot+ is called during system test teardown.
44
44
  def take_failed_screenshot
45
- take_screenshot if failed? && supports_screenshot?
45
+ return unless failed? && supports_screenshot? && Capybara::Session.instance_created?
46
+
47
+ take_screenshot
48
+ metadata[:failure_screenshot_path] = relative_image_path if Minitest::Runnable.method_defined?(:metadata)
46
49
  end
47
50
 
48
51
  private
49
52
  attr_accessor :_screenshot_counter
50
53
 
51
- def save_html?
54
+ def html_from_env?
52
55
  ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
53
56
  end
54
57
 
@@ -62,7 +65,7 @@ module ActionDispatch
62
65
  end
63
66
 
64
67
  def image_name
65
- sanitized_method_name = method_name.tr("/\\", "--")
68
+ sanitized_method_name = method_name.gsub(/[^\w]+/, "-")
66
69
  name = "#{unique}_#{sanitized_method_name}"
67
70
  name[0...225]
68
71
  end
@@ -76,13 +79,21 @@ module ActionDispatch
76
79
  end
77
80
 
78
81
  def absolute_path
79
- Rails.root.join("tmp/screenshots/#{image_name}")
82
+ Rails.root.join(screenshots_dir, image_name)
83
+ end
84
+
85
+ def screenshots_dir
86
+ Capybara.save_path.presence || "tmp/screenshots"
80
87
  end
81
88
 
82
89
  def absolute_image_path
83
90
  "#{absolute_path}.png"
84
91
  end
85
92
 
93
+ def relative_image_path
94
+ "#{absolute_path.relative_path_from(Rails.root)}.png"
95
+ end
96
+
86
97
  def absolute_html_path
87
98
  "#{absolute_path}.html"
88
99
  end
@@ -105,11 +116,15 @@ module ActionDispatch
105
116
  output_type
106
117
  end
107
118
 
108
- def display_image
119
+ def show(img)
120
+ puts img
121
+ end
122
+
123
+ def display_image(html:, screenshot_output:)
109
124
  message = +"[Screenshot Image]: #{image_path}\n"
110
- message << +"[Screenshot HTML]: #{html_path}\n" if save_html?
125
+ message << +"[Screenshot HTML]: #{html_path}\n" if html
111
126
 
112
- case output_type
127
+ case screenshot_output || output_type
113
128
  when "artifact"
114
129
  message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
115
130
  when "inline"
@@ -4,14 +4,6 @@ module ActionDispatch
4
4
  module SystemTesting
5
5
  module TestHelpers
6
6
  module SetupAndTeardown # :nodoc:
7
- def host!(host)
8
- ActiveSupport::Deprecation.warn \
9
- "ActionDispatch::SystemTestCase#host! is deprecated with no replacement. " \
10
- "Set Capybara.app_host directly or rely on Capybara's default host."
11
-
12
- Capybara.app_host = host
13
- end
14
-
15
7
  def before_teardown
16
8
  take_failed_screenshot
17
9
  ensure
@@ -36,7 +36,7 @@ module ActionDispatch
36
36
 
37
37
  private
38
38
  def code_from_name(name)
39
- GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
39
+ GENERIC_RESPONSE_CODES[name] || Rack::Utils.status_code(name)
40
40
  end
41
41
 
42
42
  def name_from_code(code)
@@ -20,7 +20,7 @@ module ActionDispatch
20
20
  #
21
21
  # You can also pass an explicit status number like <tt>assert_response(501)</tt>
22
22
  # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
23
- # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
23
+ # See +Rack::Utils::SYMBOL_TO_STATUS_CODE+ for a full list.
24
24
  #
25
25
  # # Asserts that the response was a redirection
26
26
  # assert_response :redirect
@@ -30,7 +30,7 @@ module ActionDispatch
30
30
  def assert_response(type, message = nil)
31
31
  message ||= generate_response_message(type)
32
32
 
33
- if RESPONSE_PREDICATES.keys.include?(type)
33
+ if RESPONSE_PREDICATES.key?(type)
34
34
  assert @response.public_send(RESPONSE_PREDICATES[type]), message
35
35
  else
36
36
  assert_equal AssertionResponse.new(type).code, @response.response_code, message
@@ -50,12 +50,19 @@ module ActionDispatch
50
50
  #
51
51
  # # Asserts that the redirection matches the regular expression
52
52
  # assert_redirected_to %r(\Ahttp://example.org)
53
- def assert_redirected_to(options = {}, message = nil)
54
- assert_response(:redirect, message)
55
- return true if options === @response.location
53
+ #
54
+ # # Asserts that the redirection has the HTTP status code 301 (Moved
55
+ # # Permanently).
56
+ # assert_redirected_to "/some/path", status: :moved_permanently
57
+ def assert_redirected_to(url_options = {}, options = {}, message = nil)
58
+ options, message = {}, options unless options.is_a?(Hash)
59
+
60
+ status = options[:status] || :redirect
61
+ assert_response(status, message)
62
+ return true if url_options === @response.location
56
63
 
57
64
  redirect_is = normalize_argument_to_redirection(@response.location)
58
- redirect_expected = normalize_argument_to_redirection(options)
65
+ redirect_expected = normalize_argument_to_redirection(url_options)
59
66
 
60
67
  message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
61
68
  assert_operator redirect_expected, :===, redirect_is, message
@@ -93,7 +100,7 @@ module ActionDispatch
93
100
  end
94
101
 
95
102
  def code_with_name(code_or_name)
96
- if RESPONSE_PREDICATES.values.include?("#{code_or_name}?".to_sym)
103
+ if RESPONSE_PREDICATES.value?("#{code_or_name}?".to_sym)
97
104
  code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
98
105
  end
99
106
 
@@ -9,6 +9,36 @@ module ActionDispatch
9
9
  module Assertions
10
10
  # Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
11
11
  module RoutingAssertions
12
+ extend ActiveSupport::Concern
13
+
14
+ module ClassMethods
15
+ # A helper to make it easier to test different route configurations.
16
+ # This method temporarily replaces @routes with a new RouteSet instance
17
+ # before each test.
18
+ #
19
+ # The new instance is yielded to the passed block. Typically the block
20
+ # will create some routes using <tt>set.draw { match ... }</tt>:
21
+ #
22
+ # with_routing do |set|
23
+ # set.draw do
24
+ # resources :users
25
+ # end
26
+ # end
27
+ #
28
+ def with_routing(&block)
29
+ old_routes, old_controller = nil
30
+
31
+ setup do
32
+ old_routes, old_controller = @routes, @controller
33
+ create_routes(&block)
34
+ end
35
+
36
+ teardown do
37
+ reset_routes(old_routes, old_controller)
38
+ end
39
+ end
40
+ end
41
+
12
42
  def setup # :nodoc:
13
43
  @routes ||= nil
14
44
  super
@@ -18,8 +48,8 @@ module ActionDispatch
18
48
  # match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
19
49
  #
20
50
  # Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
21
- # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
22
- # and a :method containing the required HTTP verb.
51
+ # requiring a specific HTTP method. The hash should contain a +:path+ with the incoming request path
52
+ # and a +:method+ containing the required HTTP verb.
23
53
  #
24
54
  # # Asserts that POSTing to /items will call the create action on ItemsController
25
55
  # assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
@@ -83,7 +113,7 @@ module ActionDispatch
83
113
  # # Asserts that the generated route gives us our custom route
84
114
  # assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
85
115
  def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
86
- if %r{://}.match?(expected_path)
116
+ if expected_path.include?("://")
87
117
  fail_on(URI::InvalidURIError, message) do
88
118
  uri = URI.parse(expected_path)
89
119
  expected_path = uri.path.to_s.empty? ? "/" : uri.path
@@ -150,33 +180,11 @@ module ActionDispatch
150
180
  # assert_equal "/users", users_path
151
181
  # end
152
182
  #
153
- def with_routing
154
- old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
155
- if defined?(@controller) && @controller
156
- old_controller, @controller = @controller, @controller.clone
157
- _routes = @routes
158
-
159
- @controller.singleton_class.include(_routes.url_helpers)
160
-
161
- if @controller.respond_to? :view_context_class
162
- view_context_class = Class.new(@controller.view_context_class) do
163
- include _routes.url_helpers
164
- end
165
-
166
- custom_view_context = Module.new {
167
- define_method(:view_context_class) do
168
- view_context_class
169
- end
170
- }
171
- @controller.extend(custom_view_context)
172
- end
173
- end
174
- yield @routes
183
+ def with_routing(&block)
184
+ old_routes, old_controller = @routes, @controller
185
+ create_routes(&block)
175
186
  ensure
176
- @routes = old_routes
177
- if defined?(@controller) && @controller
178
- @controller = old_controller
179
- end
187
+ reset_routes(old_routes, old_controller)
180
188
  end
181
189
 
182
190
  # ROUTES TODO: These assertions should really work in an integration context
@@ -187,8 +195,40 @@ module ActionDispatch
187
195
  super
188
196
  end
189
197
  end
198
+ ruby2_keywords(:method_missing)
190
199
 
191
200
  private
201
+ def create_routes
202
+ @routes = ActionDispatch::Routing::RouteSet.new
203
+ if defined?(@controller) && @controller
204
+ @controller = @controller.clone
205
+ _routes = @routes
206
+
207
+ @controller.singleton_class.include(_routes.url_helpers)
208
+
209
+ if @controller.respond_to? :view_context_class
210
+ view_context_class = Class.new(@controller.view_context_class) do
211
+ include _routes.url_helpers
212
+ end
213
+
214
+ custom_view_context = Module.new {
215
+ define_method(:view_context_class) do
216
+ view_context_class
217
+ end
218
+ }
219
+ @controller.extend(custom_view_context)
220
+ end
221
+ end
222
+ yield @routes
223
+ end
224
+
225
+ def reset_routes(old_routes, old_controller)
226
+ @routes = old_routes
227
+ if defined?(@controller) && @controller
228
+ @controller = old_controller
229
+ end
230
+ end
231
+
192
232
  # Recognizes the route for a given path.
193
233
  def recognized_request_for(path, extras = {}, msg)
194
234
  if path.is_a?(Hash)
@@ -201,7 +241,7 @@ module ActionDispatch
201
241
  controller = @controller if defined?(@controller)
202
242
  request = ActionController::TestRequest.create controller&.class
203
243
 
204
- if %r{://}.match?(path)
244
+ if path.include?("://")
205
245
  fail_on(URI::InvalidURIError, msg) do
206
246
  uri = URI.parse(path)
207
247
  request.env["rack.url_scheme"] = uri.scheme || "http"
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails-dom-testing"
4
+ require "action_dispatch/testing/assertions/response"
5
+ require "action_dispatch/testing/assertions/routing"
4
6
 
5
7
  module ActionDispatch
6
8
  module Assertions
7
- autoload :ResponseAssertions, "action_dispatch/testing/assertions/response"
8
- autoload :RoutingAssertions, "action_dispatch/testing/assertions/routing"
9
-
10
9
  extend ActiveSupport::Concern
11
10
 
12
11
  include ResponseAssertions
@@ -17,7 +16,7 @@ module ActionDispatch
17
16
  @html_document ||= if @response.media_type&.end_with?("xml")
18
17
  Nokogiri::XML::Document.parse(@response.body)
19
18
  else
20
- Nokogiri::HTML::Document.parse(@response.body)
19
+ Rails::Dom::Testing.html_document.parse(@response.body)
21
20
  end
22
21
  end
23
22
  end
@@ -3,12 +3,12 @@
3
3
  require "stringio"
4
4
  require "uri"
5
5
  require "rack/test"
6
- require "minitest"
6
+ require "active_support/test_case"
7
7
 
8
8
  require "action_dispatch/testing/request_encoder"
9
9
 
10
10
  module ActionDispatch
11
- module Integration #:nodoc:
11
+ module Integration # :nodoc:
12
12
  module RequestHelpers
13
13
  # Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
14
14
  # for more details.
@@ -58,7 +58,9 @@ module ActionDispatch
58
58
  # the same HTTP verb will be used when redirecting, otherwise a GET request
59
59
  # will be performed. Any arguments are passed to the
60
60
  # underlying request.
61
- def follow_redirect!(**args)
61
+ #
62
+ # The HTTP_REFERER header will be set to the previous url.
63
+ def follow_redirect!(headers: {}, **args)
62
64
  raise "not a redirect! #{status} #{status_message}" unless redirect?
63
65
 
64
66
  method =
@@ -68,7 +70,11 @@ module ActionDispatch
68
70
  :get
69
71
  end
70
72
 
71
- public_send(method, response.location, **args)
73
+ if [ :HTTP_REFERER, "HTTP_REFERER" ].none? { |key| headers.key? key }
74
+ headers["HTTP_REFERER"] = request.url
75
+ end
76
+
77
+ public_send(method, response.location, headers: headers, **args)
72
78
  status
73
79
  end
74
80
  end
@@ -78,9 +84,8 @@ module ActionDispatch
78
84
  # multiple sessions and run them side-by-side, you can also mimic (to some
79
85
  # limited extent) multiple simultaneous users interacting with your system.
80
86
  #
81
- # Typically, you will instantiate a new session using
82
- # IntegrationTest#open_session, rather than instantiating
83
- # Integration::Session directly.
87
+ # Typically, you will instantiate a new session using Runner#open_session,
88
+ # rather than instantiating a \Session directly.
84
89
  class Session
85
90
  DEFAULT_HOST = "www.example.com"
86
91
 
@@ -122,7 +127,7 @@ module ActionDispatch
122
127
 
123
128
  include ActionDispatch::Routing::UrlFor
124
129
 
125
- # Create and initialize a new Session instance.
130
+ # Create and initialize a new \Session instance.
126
131
  def initialize(app)
127
132
  super()
128
133
  @app = app
@@ -199,16 +204,17 @@ module ActionDispatch
199
204
  # merged into the Rack env hash.
200
205
  # - +env+: Additional env to pass, as a Hash. The headers will be
201
206
  # merged into the Rack env hash.
202
- # - +xhr+: Set to +true+ if you want to make and Ajax request.
207
+ # - +xhr+: Set to +true+ if you want to make an Ajax request.
203
208
  # Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
204
209
  # The headers will be merged into the Rack env hash.
205
210
  # - +as+: Used for encoding the request with different content type.
206
211
  # Supports +:json+ by default and will set the appropriate request headers.
207
212
  # The headers will be merged into the Rack env hash.
208
213
  #
209
- # This method is rarely used directly. Use +#get+, +#post+, or other standard
210
- # HTTP methods in integration tests. +#process+ is only required when using a
211
- # request method that doesn't have a method defined in the integration tests.
214
+ # This method is rarely used directly. Use RequestHelpers#get,
215
+ # RequestHelpers#post, or other standard HTTP methods in integration
216
+ # tests. +#process+ is only required when using a request method that
217
+ # doesn't have a method defined in the integration tests.
212
218
  #
213
219
  # This method returns the response status, after performing the request.
214
220
  # Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
@@ -226,7 +232,7 @@ module ActionDispatch
226
232
  method = :post
227
233
  end
228
234
 
229
- if %r{://}.match?(path)
235
+ if path.include?("://")
230
236
  path = build_expanded_path(path) do |location|
231
237
  https! URI::HTTPS === location if location.scheme
232
238
 
@@ -252,10 +258,13 @@ module ActionDispatch
252
258
  "REQUEST_URI" => path,
253
259
  "HTTP_HOST" => host,
254
260
  "REMOTE_ADDR" => remote_addr,
255
- "CONTENT_TYPE" => request_encoder.content_type,
256
261
  "HTTP_ACCEPT" => request_encoder.accept_header || accept
257
262
  }
258
263
 
264
+ if request_encoder.content_type
265
+ request_env["CONTENT_TYPE"] = request_encoder.content_type
266
+ end
267
+
259
268
  wrapped_headers = Http::Headers.from_hash({})
260
269
  wrapped_headers.merge!(headers) if headers
261
270
 
@@ -363,13 +372,11 @@ module ActionDispatch
363
372
  reset_html_document = "@html_document = nil"
364
373
  end
365
374
 
366
- definition = RUBY_VERSION >= "2.7" ? "..." : "*args"
367
-
368
375
  module_eval <<~RUBY, __FILE__, __LINE__ + 1
369
- def #{method}(#{definition})
376
+ def #{method}(...)
370
377
  #{reset_html_document}
371
378
 
372
- result = integration_session.#{method}(#{definition})
379
+ result = integration_session.#{method}(...)
373
380
  copy_session_variables!
374
381
  result
375
382
  end
@@ -404,7 +411,7 @@ module ActionDispatch
404
411
 
405
412
  # Copy the instance variables from the current session instance into the
406
413
  # test instance.
407
- def copy_session_variables! #:nodoc:
414
+ def copy_session_variables! # :nodoc:
408
415
  @controller = @integration_session.controller
409
416
  @response = @integration_session.response
410
417
  @request = @integration_session.request
@@ -433,7 +440,7 @@ module ActionDispatch
433
440
  super
434
441
  end
435
442
  end
436
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
443
+ ruby2_keywords(:method_missing)
437
444
  end
438
445
  end
439
446
 
@@ -442,8 +449,9 @@ module ActionDispatch
442
449
  # more completely than either unit or functional tests do, exercising the
443
450
  # entire stack, from the dispatcher to the database.
444
451
  #
445
- # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
446
- # using the get/post methods:
452
+ # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your
453
+ # tests using the Integration::RequestHelpers#get and/or
454
+ # Integration::RequestHelpers#post methods:
447
455
  #
448
456
  # require "test_helper"
449
457
  #
@@ -614,7 +622,7 @@ module ActionDispatch
614
622
  # the request format to JSON unless overridden), sets the content type to
615
623
  # "application/json" and encodes the parameters as JSON.
616
624
  #
617
- # Calling +parsed_body+ on the response parses the response body based on the
625
+ # Calling TestResponse#parsed_body on the response parses the response body based on the
618
626
  # last response MIME type.
619
627
  #
620
628
  # Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
@@ -626,9 +634,9 @@ module ActionDispatch
626
634
  #
627
635
  # Where +param_encoder+ defines how the params should be encoded and
628
636
  # +response_parser+ defines how the response body should be parsed through
629
- # +parsed_body+.
637
+ # TestResponse#parsed_body.
630
638
  #
631
- # Consult the Rails Testing Guide for more.
639
+ # Consult the {Rails Testing Guide}[https://guides.rubyonrails.org/testing.html] for more.
632
640
 
633
641
  class IntegrationTest < ActiveSupport::TestCase
634
642
  include TestProcess::FixtureFile
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "nokogiri"
4
+
3
5
  module ActionDispatch
4
6
  class RequestEncoder # :nodoc:
5
7
  class IdentityEncoder
@@ -50,6 +52,7 @@ module ActionDispatch
50
52
  @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
51
53
  end
52
54
 
53
- register_encoder :json, response_parser: -> body { JSON.parse(body) }
55
+ register_encoder :html, response_parser: -> body { Rails::Dom::Testing.html_document.parse(body) }
56
+ register_encoder :json, response_parser: -> body { JSON.parse(body, object_class: ActiveSupport::HashWithIndifferentAccess) }
54
57
  end
55
58
  end
@@ -8,47 +8,22 @@ module ActionDispatch
8
8
  module FixtureFile
9
9
  # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.file_fixture_path, path), type)</tt>:
10
10
  #
11
- # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png') }
11
+ # post :change_avatar, params: { avatar: file_fixture_upload('david.png', 'image/png') }
12
12
  #
13
13
  # Default fixture files location is <tt>test/fixtures/files</tt>.
14
14
  #
15
15
  # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
16
16
  # This will not affect other platforms:
17
17
  #
18
- # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png', :binary) }
19
- def fixture_file_upload(path, mime_type = nil, binary = false)
20
- if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
21
- !File.exist?(path)
22
- original_path = path
23
- path = Pathname.new(self.class.fixture_path).join(path)
24
-
25
- if !self.class.file_fixture_path
26
- ActiveSupport::Deprecation.warn(<<~EOM)
27
- Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
28
- In Rails 7.0, the path needs to be relative to `file_fixture_path` which you
29
- haven't set yet. Set `file_fixture_path` to discard this warning.
30
- EOM
31
- elsif path.exist?
32
- non_deprecated_path = Pathname(File.absolute_path(path)).relative_path_from(Pathname(File.absolute_path(self.class.file_fixture_path)))
33
-
34
- if Pathname(original_path) != non_deprecated_path
35
- ActiveSupport::Deprecation.warn(<<~EOM)
36
- Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
37
- In Rails 7.0, the path needs to be relative to `file_fixture_path`.
38
-
39
- Please modify the call from
40
- `fixture_file_upload("#{original_path}")` to `fixture_file_upload("#{non_deprecated_path}")`.
41
- EOM
42
- end
43
- else
44
- path = file_fixture(original_path)
45
- end
46
- elsif self.class.file_fixture_path && !File.exist?(path)
18
+ # post :change_avatar, params: { avatar: file_fixture_upload('david.png', 'image/png', :binary) }
19
+ def file_fixture_upload(path, mime_type = nil, binary = false)
20
+ if self.class.file_fixture_path && !File.exist?(path)
47
21
  path = file_fixture(path)
48
22
  end
49
23
 
50
24
  Rack::Test::UploadedFile.new(path, mime_type, binary)
51
25
  end
26
+ alias_method :fixture_file_upload, :file_fixture_upload
52
27
  end
53
28
 
54
29
  include FixtureFile
@@ -32,7 +32,7 @@ module ActionDispatch
32
32
  end
33
33
 
34
34
  def port=(number)
35
- set_header("SERVER_PORT", number.to_i)
35
+ set_header("SERVER_PORT", number)
36
36
  end
37
37
 
38
38
  def request_uri=(uri)
@@ -3,8 +3,8 @@
3
3
  require "action_dispatch/testing/request_encoder"
4
4
 
5
5
  module ActionDispatch
6
- # Integration test methods such as ActionDispatch::Integration::Session#get
7
- # and ActionDispatch::Integration::Session#post return objects of class
6
+ # Integration test methods such as Integration::RequestHelpers#get
7
+ # and Integration::RequestHelpers#post return objects of class
8
8
  # TestResponse, which represent the HTTP response results of the requested
9
9
  # controller actions.
10
10
  #
@@ -14,6 +14,38 @@ module ActionDispatch
14
14
  new response.status, response.headers, response.body
15
15
  end
16
16
 
17
+ # Returns a parsed body depending on the response MIME type. When a parser
18
+ # corresponding to the MIME type is not found, it returns the raw body.
19
+ #
20
+ # ==== Examples
21
+ # get "/posts"
22
+ # response.content_type # => "text/html; charset=utf-8"
23
+ # response.parsed_body.class # => Nokogiri::HTML5::Document
24
+ # response.parsed_body.to_html # => "<!DOCTYPE html>\n<html>\n..."
25
+ #
26
+ # assert_pattern { response.parsed_body.at("main") => { content: "Hello, world" } }
27
+ #
28
+ # response.parsed_body.at("main") => {name:, content:}
29
+ # assert_equal "main", name
30
+ # assert_equal "Some main content", content
31
+ #
32
+ # get "/posts.json"
33
+ # response.content_type # => "application/json; charset=utf-8"
34
+ # response.parsed_body.class # => Array
35
+ # response.parsed_body # => [{"id"=>42, "title"=>"Title"},...
36
+ #
37
+ # assert_pattern { response.parsed_body => [{ id: 42 }] }
38
+ #
39
+ # get "/posts/42.json"
40
+ # response.content_type # => "application/json; charset=utf-8"
41
+ # response.parsed_body.class # => ActiveSupport::HashWithIndifferentAccess
42
+ # response.parsed_body # => {"id"=>42, "title"=>"Title"}
43
+ #
44
+ # assert_pattern { response.parsed_body => [{ title: /title/i }] }
45
+ #
46
+ # response.parsed_body => {id:, title:}
47
+ # assert_equal 42, id
48
+ # assert_equal "Title", title
17
49
  def parsed_body
18
50
  @parsed_body ||= response_parser.call(body)
19
51
  end