actionpack 5.2.8.1 → 6.1.6.1

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +383 -346
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +38 -4
  6. data/lib/abstract_controller/caching/fragments.rb +6 -22
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/callbacks.rb +14 -2
  9. data/lib/abstract_controller/collector.rb +5 -4
  10. data/lib/abstract_controller/helpers.rb +106 -90
  11. data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
  12. data/lib/abstract_controller/rendering.rb +9 -9
  13. data/lib/abstract_controller/translation.rb +11 -5
  14. data/lib/abstract_controller.rb +1 -0
  15. data/lib/action_controller/api.rb +4 -3
  16. data/lib/action_controller/base.rb +6 -9
  17. data/lib/action_controller/caching.rb +1 -3
  18. data/lib/action_controller/log_subscriber.rb +10 -7
  19. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  20. data/lib/action_controller/metal/conditional_get.rb +19 -5
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -2
  22. data/lib/action_controller/metal/cookies.rb +3 -1
  23. data/lib/action_controller/metal/data_streaming.rb +6 -7
  24. data/lib/action_controller/metal/default_headers.rb +17 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
  26. data/lib/action_controller/metal/exceptions.rb +56 -2
  27. data/lib/action_controller/metal/flash.rb +5 -5
  28. data/lib/action_controller/metal/head.rb +7 -4
  29. data/lib/action_controller/metal/helpers.rb +14 -5
  30. data/lib/action_controller/metal/http_authentication.rb +25 -23
  31. data/lib/action_controller/metal/implicit_render.rb +5 -15
  32. data/lib/action_controller/metal/instrumentation.rb +13 -14
  33. data/lib/action_controller/metal/live.rb +39 -32
  34. data/lib/action_controller/metal/logging.rb +20 -0
  35. data/lib/action_controller/metal/mime_responds.rb +19 -4
  36. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  37. data/lib/action_controller/metal/params_wrapper.rb +32 -22
  38. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  39. data/lib/action_controller/metal/redirecting.rb +6 -6
  40. data/lib/action_controller/metal/renderers.rb +4 -4
  41. data/lib/action_controller/metal/rendering.rb +8 -3
  42. data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
  43. data/lib/action_controller/metal/rescue.rb +1 -1
  44. data/lib/action_controller/metal/streaming.rb +0 -1
  45. data/lib/action_controller/metal/strong_parameters.rb +168 -59
  46. data/lib/action_controller/metal/url_for.rb +1 -1
  47. data/lib/action_controller/metal.rb +10 -8
  48. data/lib/action_controller/railties/helpers.rb +1 -1
  49. data/lib/action_controller/renderer.rb +37 -13
  50. data/lib/action_controller/template_assertions.rb +1 -1
  51. data/lib/action_controller/test_case.rb +71 -63
  52. data/lib/action_controller.rb +7 -4
  53. data/lib/action_dispatch/http/cache.rb +31 -27
  54. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  55. data/lib/action_dispatch/http/content_security_policy.rb +34 -18
  56. data/lib/action_dispatch/http/filter_parameters.rb +9 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  58. data/lib/action_dispatch/http/headers.rb +4 -4
  59. data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
  60. data/lib/action_dispatch/http/mime_type.rb +43 -24
  61. data/lib/action_dispatch/http/parameters.rb +14 -23
  62. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  63. data/lib/action_dispatch/http/request.rb +45 -22
  64. data/lib/action_dispatch/http/response.rb +45 -25
  65. data/lib/action_dispatch/http/upload.rb +9 -1
  66. data/lib/action_dispatch/http/url.rb +82 -82
  67. data/lib/action_dispatch/journey/formatter.rb +55 -31
  68. data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
  69. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  70. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
  71. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  72. data/lib/action_dispatch/journey/nodes/node.rb +13 -11
  73. data/lib/action_dispatch/journey/parser.rb +13 -13
  74. data/lib/action_dispatch/journey/parser.y +1 -1
  75. data/lib/action_dispatch/journey/path/pattern.rb +19 -21
  76. data/lib/action_dispatch/journey/route.rb +10 -20
  77. data/lib/action_dispatch/journey/router/utils.rb +14 -12
  78. data/lib/action_dispatch/journey/router.rb +26 -34
  79. data/lib/action_dispatch/journey/routes.rb +0 -2
  80. data/lib/action_dispatch/journey/scanner.rb +10 -4
  81. data/lib/action_dispatch/journey/visitors.rb +1 -4
  82. data/lib/action_dispatch/journey.rb +0 -2
  83. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  84. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  85. data/lib/action_dispatch/middleware/cookies.rb +128 -109
  86. data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
  87. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  88. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  89. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
  90. data/lib/action_dispatch/middleware/flash.rb +1 -1
  91. data/lib/action_dispatch/middleware/host_authorization.rb +170 -0
  92. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  93. data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
  94. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  95. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
  96. data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
  97. data/lib/action_dispatch/middleware/show_exceptions.rb +13 -2
  98. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  99. data/lib/action_dispatch/middleware/stack.rb +56 -2
  100. data/lib/action_dispatch/middleware/static.rb +153 -93
  101. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  103. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  107. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  108. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  111. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  112. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  120. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
  122. data/lib/action_dispatch/railtie.rb +8 -2
  123. data/lib/action_dispatch/request/session.rb +11 -10
  124. data/lib/action_dispatch/request/utils.rb +26 -2
  125. data/lib/action_dispatch/routing/inspector.rb +100 -52
  126. data/lib/action_dispatch/routing/mapper.rb +155 -103
  127. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
  128. data/lib/action_dispatch/routing/redirection.rb +4 -4
  129. data/lib/action_dispatch/routing/route_set.rb +71 -69
  130. data/lib/action_dispatch/routing/url_for.rb +2 -2
  131. data/lib/action_dispatch/routing.rb +21 -20
  132. data/lib/action_dispatch/system_test_case.rb +60 -11
  133. data/lib/action_dispatch/system_testing/browser.rb +53 -16
  134. data/lib/action_dispatch/system_testing/driver.rb +11 -3
  135. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
  136. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
  137. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  138. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  139. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  140. data/lib/action_dispatch/testing/assertions.rb +1 -1
  141. data/lib/action_dispatch/testing/integration.rb +60 -28
  142. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  143. data/lib/action_dispatch/testing/test_process.rb +32 -4
  144. data/lib/action_dispatch/testing/test_request.rb +3 -3
  145. data/lib/action_dispatch/testing/test_response.rb +4 -32
  146. data/lib/action_dispatch.rb +9 -3
  147. data/lib/action_pack/gem_version.rb +3 -3
  148. data/lib/action_pack.rb +1 -1
  149. metadata +34 -21
  150. data/lib/action_controller/metal/force_ssl.rb +0 -99
  151. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  152. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  153. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  154. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  155. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -3,10 +3,11 @@
3
3
  module ActionDispatch
4
4
  module SystemTesting
5
5
  class Browser # :nodoc:
6
- attr_reader :name
6
+ attr_reader :name, :options
7
7
 
8
8
  def initialize(name)
9
9
  @name = name
10
+ set_default_options
10
11
  end
11
12
 
12
13
  def type
@@ -20,29 +21,65 @@ module ActionDispatch
20
21
  end
21
22
  end
22
23
 
23
- def options
24
- case name
25
- when :headless_chrome
26
- headless_chrome_browser_options
27
- when :headless_firefox
28
- headless_firefox_browser_options
24
+ def configure
25
+ initialize_options
26
+ yield options if block_given? && options
27
+ end
28
+
29
+ # driver_path can be configured as a proc. The webdrivers gem uses this
30
+ # proc to update web drivers. Running this proc early allows us to only
31
+ # update the webdriver once and avoid race conditions when using
32
+ # parallel tests.
33
+ def preload
34
+ case type
35
+ when :chrome
36
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
37
+ ::Selenium::WebDriver::Chrome::Service.driver_path&.call
38
+ else
39
+ # Selenium <= v3.141.0
40
+ ::Selenium::WebDriver::Chrome.driver_path
41
+ end
42
+ when :firefox
43
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
44
+ ::Selenium::WebDriver::Firefox::Service.driver_path&.call
45
+ else
46
+ # Selenium <= v3.141.0
47
+ ::Selenium::WebDriver::Firefox.driver_path
48
+ end
29
49
  end
30
50
  end
31
51
 
32
52
  private
33
- def headless_chrome_browser_options
34
- options = Selenium::WebDriver::Chrome::Options.new
35
- options.args << "--headless"
36
- options.args << "--disable-gpu" if Gem.win_platform?
53
+ def initialize_options
54
+ @options ||=
55
+ case type
56
+ when :chrome
57
+ ::Selenium::WebDriver::Chrome::Options.new
58
+ when :firefox
59
+ ::Selenium::WebDriver::Firefox::Options.new
60
+ end
61
+ end
37
62
 
38
- options
63
+ def set_default_options
64
+ case name
65
+ when :headless_chrome
66
+ set_headless_chrome_browser_options
67
+ when :headless_firefox
68
+ set_headless_firefox_browser_options
69
+ end
39
70
  end
40
71
 
41
- def headless_firefox_browser_options
42
- options = Selenium::WebDriver::Firefox::Options.new
43
- options.args << "-headless"
72
+ def set_headless_chrome_browser_options
73
+ configure do |capabilities|
74
+ capabilities.add_argument("--headless")
75
+ capabilities.add_argument("--disable-gpu") if Gem.win_platform?
76
+ end
77
+ end
44
78
 
45
- options
79
+ def set_headless_firefox_browser_options
80
+ configure do |capabilities|
81
+ capabilities.add_argument("-headless")
82
+ end
46
83
  end
47
84
  end
48
85
  end
@@ -3,11 +3,17 @@
3
3
  module ActionDispatch
4
4
  module SystemTesting
5
5
  class Driver # :nodoc:
6
- def initialize(name, **options)
6
+ def initialize(name, **options, &capabilities)
7
7
  @name = name
8
8
  @browser = Browser.new(options[:using])
9
9
  @screen_size = options[:screen_size]
10
- @options = options[:options]
10
+ @options = options[:options] || {}
11
+ @capabilities = capabilities
12
+
13
+ if name == :selenium
14
+ require "selenium/webdriver"
15
+ @browser.preload
16
+ end
11
17
  end
12
18
 
13
19
  def use
@@ -22,6 +28,8 @@ module ActionDispatch
22
28
  end
23
29
 
24
30
  def register
31
+ @browser.configure(&@capabilities)
32
+
25
33
  Capybara.register_driver @name do |app|
26
34
  case @name
27
35
  when :selenium then register_selenium(app)
@@ -36,7 +44,7 @@ module ActionDispatch
36
44
  end
37
45
 
38
46
  def register_selenium(app)
39
- Capybara::Selenium::Driver.new(app, { browser: @browser.type }.merge(browser_options)).tap do |driver|
47
+ Capybara::Selenium::Driver.new(app, **{ browser: @browser.type }.merge(browser_options)).tap do |driver|
40
48
  driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
41
49
  end
42
50
  end
@@ -9,10 +9,16 @@ module ActionDispatch
9
9
  #
10
10
  # +take_screenshot+ can be used at any point in your system tests to take
11
11
  # a screenshot of the current state. This can be useful for debugging or
12
- # automating visual testing.
12
+ # automating visual testing. You can take multiple screenshots per test
13
+ # to investigate changes at different points during your test. These will be
14
+ # named with a sequential prefix (or 'failed' for failing tests)
13
15
  #
14
16
  # The screenshot will be displayed in your console, if supported.
15
17
  #
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
21
+ #
16
22
  # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
17
23
  # control the output. Possible values are:
18
24
  # * [+simple+ (default)] Only displays the screenshot path.
@@ -20,8 +26,10 @@ module ActionDispatch
20
26
  # * [+inline+] Display the screenshot in the terminal using the
21
27
  # iTerm image protocol (https://iterm2.com/documentation-images.html).
22
28
  # * [+artifact+] Display the screenshot in the terminal, using the terminal
23
- # artifact format (https://buildkite.github.io/terminal/inline-images/).
29
+ # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
24
30
  def take_screenshot
31
+ increment_unique
32
+ save_html if save_html?
25
33
  save_image
26
34
  puts display_image
27
35
  end
@@ -38,16 +46,49 @@ module ActionDispatch
38
46
  end
39
47
 
40
48
  private
49
+ attr_accessor :_screenshot_counter
50
+
51
+ def save_html?
52
+ ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
53
+ end
54
+
55
+ def increment_unique
56
+ @_screenshot_counter ||= 0
57
+ @_screenshot_counter += 1
58
+ end
59
+
60
+ def unique
61
+ failed? ? "failures" : (_screenshot_counter || 0).to_s
62
+ end
63
+
41
64
  def image_name
42
- failed? ? "failures_#{method_name}" : method_name
65
+ sanitized_method_name = method_name.tr("/\\", "--")
66
+ name = "#{unique}_#{sanitized_method_name}"
67
+ name[0...225]
43
68
  end
44
69
 
45
70
  def image_path
46
- @image_path ||= absolute_image_path.relative_path_from(Pathname.pwd).to_s
71
+ absolute_image_path.to_s
72
+ end
73
+
74
+ def html_path
75
+ absolute_html_path.to_s
76
+ end
77
+
78
+ def absolute_path
79
+ Rails.root.join("tmp/screenshots/#{image_name}")
47
80
  end
48
81
 
49
82
  def absolute_image_path
50
- Rails.root.join("tmp/screenshots/#{image_name}.png")
83
+ "#{absolute_path}.png"
84
+ end
85
+
86
+ def absolute_html_path
87
+ "#{absolute_path}.html"
88
+ end
89
+
90
+ def save_html
91
+ page.save_page(absolute_html_path)
51
92
  end
52
93
 
53
94
  def save_image
@@ -65,7 +106,8 @@ module ActionDispatch
65
106
  end
66
107
 
67
108
  def display_image
68
- message = "[Screenshot]: #{image_path}\n".dup
109
+ message = +"[Screenshot Image]: #{image_path}\n"
110
+ message << +"[Screenshot HTML]: #{html_path}\n" if save_html?
69
111
 
70
112
  case output_type
71
113
  when "artifact"
@@ -80,7 +122,7 @@ module ActionDispatch
80
122
  end
81
123
 
82
124
  def inline_base64(path)
83
- Base64.encode64(path).gsub("\n", "")
125
+ Base64.strict_encode64(path)
84
126
  end
85
127
 
86
128
  def failed?
@@ -4,24 +4,22 @@ module ActionDispatch
4
4
  module SystemTesting
5
5
  module TestHelpers
6
6
  module SetupAndTeardown # :nodoc:
7
- DEFAULT_HOST = "http://127.0.0.1"
8
-
9
7
  def host!(host)
10
- super
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
+
11
12
  Capybara.app_host = host
12
13
  end
13
14
 
14
- def before_setup
15
- host! DEFAULT_HOST
15
+ def before_teardown
16
+ take_failed_screenshot
17
+ ensure
16
18
  super
17
19
  end
18
20
 
19
21
  def after_teardown
20
- begin
21
- take_failed_screenshot
22
- ensure
23
- Capybara.reset_sessions!
24
- end
22
+ Capybara.reset_sessions!
25
23
  ensure
26
24
  super
27
25
  end
@@ -35,7 +35,6 @@ module ActionDispatch
35
35
  end
36
36
 
37
37
  private
38
-
39
38
  def code_from_name(name)
40
39
  GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
41
40
  end
@@ -31,15 +31,13 @@ module ActionDispatch
31
31
  message ||= generate_response_message(type)
32
32
 
33
33
  if RESPONSE_PREDICATES.keys.include?(type)
34
- assert @response.send(RESPONSE_PREDICATES[type]), message
34
+ assert @response.public_send(RESPONSE_PREDICATES[type]), message
35
35
  else
36
36
  assert_equal AssertionResponse.new(type).code, @response.response_code, message
37
37
  end
38
38
  end
39
39
 
40
- # Asserts that the redirection options passed in match those of the redirect called in the latest action.
41
- # This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
42
- # match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
40
+ # Asserts that the response is a redirect to a URL matching the given options.
43
41
  #
44
42
  # # Asserts that the redirection was to the "index" action on the WeblogController
45
43
  # assert_redirected_to controller: "weblog", action: "index"
@@ -79,9 +77,8 @@ module ActionDispatch
79
77
  end
80
78
 
81
79
  def generate_response_message(expected, actual = @response.response_code)
82
- "Expected response to be a <#{code_with_name(expected)}>,"\
83
- " but was a <#{code_with_name(actual)}>"
84
- .dup.concat(location_if_redirected).concat(response_body_if_short)
80
+ (+"Expected response to be a <#{code_with_name(expected)}>,"\
81
+ " but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
85
82
  end
86
83
 
87
84
  def response_body_if_short
@@ -9,6 +9,11 @@ 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
+ def setup # :nodoc:
13
+ @routes ||= nil
14
+ super
15
+ end
16
+
12
17
  # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
13
18
  # match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
14
19
  #
@@ -78,15 +83,14 @@ module ActionDispatch
78
83
  # # Asserts that the generated route gives us our custom route
79
84
  # assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
80
85
  def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
81
- if expected_path =~ %r{://}
86
+ if %r{://}.match?(expected_path)
82
87
  fail_on(URI::InvalidURIError, message) do
83
88
  uri = URI.parse(expected_path)
84
89
  expected_path = uri.path.to_s.empty? ? "/" : uri.path
85
90
  end
86
91
  else
87
- expected_path = "/#{expected_path}" unless expected_path.first == "/"
92
+ expected_path = "/#{expected_path}" unless expected_path.start_with?("/")
88
93
  end
89
- # Load routes.rb if it hasn't been loaded.
90
94
 
91
95
  options = options.clone
92
96
  generated_path, query_string_keys = @routes.generate_extras(options, defaults)
@@ -155,9 +159,16 @@ module ActionDispatch
155
159
  @controller.singleton_class.include(_routes.url_helpers)
156
160
 
157
161
  if @controller.respond_to? :view_context_class
158
- @controller.view_context_class = Class.new(@controller.view_context_class) do
162
+ view_context_class = Class.new(@controller.view_context_class) do
159
163
  include _routes.url_helpers
160
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)
161
172
  end
162
173
  end
163
174
  yield @routes
@@ -171,7 +182,7 @@ module ActionDispatch
171
182
  # ROUTES TODO: These assertions should really work in an integration context
172
183
  def method_missing(selector, *args, &block)
173
184
  if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)
174
- @controller.send(selector, *args, &block)
185
+ @controller.public_send(selector, *args, &block)
175
186
  else
176
187
  super
177
188
  end
@@ -187,9 +198,10 @@ module ActionDispatch
187
198
  method = :get
188
199
  end
189
200
 
190
- request = ActionController::TestRequest.create @controller.class
201
+ controller = @controller if defined?(@controller)
202
+ request = ActionController::TestRequest.create controller&.class
191
203
 
192
- if path =~ %r{://}
204
+ if %r{://}.match?(path)
193
205
  fail_on(URI::InvalidURIError, msg) do
194
206
  uri = URI.parse(path)
195
207
  request.env["rack.url_scheme"] = uri.scheme || "http"
@@ -198,7 +210,7 @@ module ActionDispatch
198
210
  request.path = uri.path.to_s.empty? ? "/" : uri.path
199
211
  end
200
212
  else
201
- path = "/#{path}" unless path.first == "/"
213
+ path = "/#{path}" unless path.start_with?("/")
202
214
  request.path = path
203
215
  end
204
216
 
@@ -14,7 +14,7 @@ module ActionDispatch
14
14
  include Rails::Dom::Testing::Assertions
15
15
 
16
16
  def html_document
17
- @html_document ||= if @response.content_type.to_s.end_with?("xml")
17
+ @html_document ||= if @response.media_type&.end_with?("xml")
18
18
  Nokogiri::XML::Document.parse(@response.body)
19
19
  else
20
20
  Nokogiri::HTML::Document.parse(@response.body)
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "stringio"
4
4
  require "uri"
5
- require "active_support/core_ext/kernel/singleton_class"
6
- require "active_support/core_ext/object/try"
7
5
  require "rack/test"
8
6
  require "minitest"
9
7
 
@@ -44,16 +42,33 @@ module ActionDispatch
44
42
 
45
43
  # Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process
46
44
  # for more details.
47
- def head(path, *args)
48
- process(:head, path, *args)
45
+ def head(path, **args)
46
+ process(:head, path, **args)
47
+ end
48
+
49
+ # Performs an OPTIONS request with the given parameters. See ActionDispatch::Integration::Session#process
50
+ # for more details.
51
+ def options(path, **args)
52
+ process(:options, path, **args)
49
53
  end
50
54
 
51
55
  # Follow a single redirect response. If the last response was not a
52
56
  # redirect, an exception will be raised. Otherwise, the redirect is
53
- # performed on the location header.
54
- def follow_redirect!
57
+ # performed on the location header. If the redirection is a 307 or 308 redirect,
58
+ # the same HTTP verb will be used when redirecting, otherwise a GET request
59
+ # will be performed. Any arguments are passed to the
60
+ # underlying request.
61
+ def follow_redirect!(**args)
55
62
  raise "not a redirect! #{status} #{status_message}" unless redirect?
56
- get(response.location)
63
+
64
+ method =
65
+ if [307, 308].include?(response.status)
66
+ request.method.downcase
67
+ else
68
+ :get
69
+ end
70
+
71
+ public_send(method, response.location, **args)
57
72
  status
58
73
  end
59
74
  end
@@ -72,13 +87,8 @@ module ActionDispatch
72
87
  include Minitest::Assertions
73
88
  include TestProcess, RequestHelpers, Assertions
74
89
 
75
- %w( status status_message headers body redirect? ).each do |method|
76
- delegate method, to: :response, allow_nil: true
77
- end
78
-
79
- %w( path ).each do |method|
80
- delegate method, to: :request, allow_nil: true
81
- end
90
+ delegate :status, :status_message, :headers, :body, :redirect?, to: :response, allow_nil: true
91
+ delegate :path, to: :request, allow_nil: true
82
92
 
83
93
  # The hostname used in the last request.
84
94
  def host
@@ -122,7 +132,7 @@ module ActionDispatch
122
132
 
123
133
  def url_options
124
134
  @url_options ||= default_url_options.dup.tap do |url_options|
125
- url_options.reverse_merge!(controller.url_options) if controller
135
+ url_options.reverse_merge!(controller.url_options) if controller.respond_to?(:url_options)
126
136
 
127
137
  if @app.respond_to?(:routes)
128
138
  url_options.reverse_merge!(@app.routes.default_url_options)
@@ -189,6 +199,12 @@ module ActionDispatch
189
199
  # merged into the Rack env hash.
190
200
  # - +env+: Additional env to pass, as a Hash. The headers will be
191
201
  # merged into the Rack env hash.
202
+ # - +xhr+: Set to +true+ if you want to make and Ajax request.
203
+ # Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
204
+ # The headers will be merged into the Rack env hash.
205
+ # - +as+: Used for encoding the request with different content type.
206
+ # Supports +:json+ by default and will set the appropriate request headers.
207
+ # The headers will be merged into the Rack env hash.
192
208
  #
193
209
  # This method is rarely used directly. Use +#get+, +#post+, or other standard
194
210
  # HTTP methods in integration tests. +#process+ is only required when using a
@@ -210,7 +226,7 @@ module ActionDispatch
210
226
  method = :post
211
227
  end
212
228
 
213
- if path =~ %r{://}
229
+ if %r{://}.match?(path)
214
230
  path = build_expanded_path(path) do |location|
215
231
  https! URI::HTTPS === location if location.scheme
216
232
 
@@ -303,6 +319,7 @@ module ActionDispatch
303
319
  APP_SESSIONS = {}
304
320
 
305
321
  attr_reader :app
322
+ attr_accessor :root_session # :nodoc:
306
323
 
307
324
  def initialize(*args, &blk)
308
325
  super(*args, &blk)
@@ -328,7 +345,7 @@ module ActionDispatch
328
345
  klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
329
346
  # If the app is a Rails app, make url_helpers available on the session.
330
347
  # This makes app.url_for and app.foo_path available in the console.
331
- if app.respond_to?(:routes)
348
+ if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
332
349
  include app.routes.url_helpers
333
350
  include app.routes.mounted_helpers
334
351
  end
@@ -341,16 +358,22 @@ module ActionDispatch
341
358
  end
342
359
 
343
360
  %w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
344
- define_method(method) do |*args|
345
- # reset the html_document variable, except for cookies/assigns calls
346
- unless method == "cookies" || method == "assigns"
347
- @html_document = nil
348
- end
361
+ # reset the html_document variable, except for cookies/assigns calls
362
+ unless method == "cookies" || method == "assigns"
363
+ reset_html_document = "@html_document = nil"
364
+ end
365
+
366
+ definition = RUBY_VERSION >= "2.7" ? "..." : "*args"
367
+
368
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
369
+ def #{method}(#{definition})
370
+ #{reset_html_document}
349
371
 
350
- integration_session.__send__(method, *args).tap do
372
+ result = integration_session.#{method}(#{definition})
351
373
  copy_session_variables!
374
+ result
352
375
  end
353
- end
376
+ RUBY
354
377
  end
355
378
 
356
379
  # Open a new session instance. If a block is given, the new session is
@@ -366,10 +389,19 @@ module ActionDispatch
366
389
  def open_session
367
390
  dup.tap do |session|
368
391
  session.reset!
392
+ session.root_session = self.root_session || self
369
393
  yield session if block_given?
370
394
  end
371
395
  end
372
396
 
397
+ def assertions # :nodoc:
398
+ root_session ? root_session.assertions : super
399
+ end
400
+
401
+ def assertions=(assertions) # :nodoc:
402
+ root_session ? root_session.assertions = assertions : super
403
+ end
404
+
373
405
  # Copy the instance variables from the current session instance into the
374
406
  # test instance.
375
407
  def copy_session_variables! #:nodoc:
@@ -485,7 +517,7 @@ module ActionDispatch
485
517
  #
486
518
  # A simple integration test that exercises multiple controllers:
487
519
  #
488
- # require 'test_helper'
520
+ # require "test_helper"
489
521
  #
490
522
  # class UserFlowsTest < ActionDispatch::IntegrationTest
491
523
  # test "login and browse site" do
@@ -514,7 +546,7 @@ module ActionDispatch
514
546
  #
515
547
  # Here's an example of multiple sessions and custom DSL in an integration test
516
548
  #
517
- # require 'test_helper'
549
+ # require "test_helper"
518
550
  #
519
551
  # class UserFlowsTest < ActionDispatch::IntegrationTest
520
552
  # test "login and browse site" do
@@ -634,8 +666,8 @@ module ActionDispatch
634
666
  @@app = app
635
667
  end
636
668
 
637
- def register_encoder(*args)
638
- RequestEncoder.register_encoder(*args)
669
+ def register_encoder(*args, **options)
670
+ RequestEncoder.register_encoder(*args, **options)
639
671
  end
640
672
  end
641
673
 
@@ -38,8 +38,8 @@ module ActionDispatch
38
38
  end
39
39
 
40
40
  def self.parser(content_type)
41
- mime = Mime::Type.lookup(content_type)
42
- encoder(mime ? mime.ref : nil).response_parser
41
+ type = Mime::Type.lookup(content_type).ref if content_type
42
+ encoder(type).response_parser
43
43
  end
44
44
 
45
45
  def self.encoder(name)
@@ -6,19 +6,47 @@ require "action_dispatch/middleware/flash"
6
6
  module ActionDispatch
7
7
  module TestProcess
8
8
  module FixtureFile
9
- # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>:
9
+ # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.file_fixture_path, path), type)</tt>:
10
10
  #
11
- # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
11
+ # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png') }
12
+ #
13
+ # Default fixture files location is <tt>test/fixtures/files</tt>.
12
14
  #
13
15
  # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
14
16
  # This will not affect other platforms:
15
17
  #
16
- # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
18
+ # post :change_avatar, params: { avatar: fixture_file_upload('spongebob.png', 'image/png', :binary) }
17
19
  def fixture_file_upload(path, mime_type = nil, binary = false)
18
20
  if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
19
21
  !File.exist?(path)
20
- path = File.join(self.class.fixture_path, 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)
47
+ path = file_fixture(path)
21
48
  end
49
+
22
50
  Rack::Test::UploadedFile.new(path, mime_type, binary)
23
51
  end
24
52
  end
@@ -6,9 +6,9 @@ require "rack/utils"
6
6
  module ActionDispatch
7
7
  class TestRequest < Request
8
8
  DEFAULT_ENV = Rack::MockRequest.env_for("/",
9
- "HTTP_HOST" => "test.host",
10
- "REMOTE_ADDR" => "0.0.0.0",
11
- "HTTP_USER_AGENT" => "Rails Testing",
9
+ "HTTP_HOST" => "test.host".b,
10
+ "REMOTE_ADDR" => "0.0.0.0".b,
11
+ "HTTP_USER_AGENT" => "Rails Testing".b,
12
12
  )
13
13
 
14
14
  # Create a new test request with default +env+ values.