actionpack 6.1.7.5 → 7.1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

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