actionpack 4.2.11.1 → 6.1.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Browser # :nodoc:
6
+ attr_reader :name, :options
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ set_default_options
11
+ end
12
+
13
+ def type
14
+ case name
15
+ when :headless_chrome
16
+ :chrome
17
+ when :headless_firefox
18
+ :firefox
19
+ else
20
+ name
21
+ end
22
+ end
23
+
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
49
+ end
50
+ end
51
+
52
+ private
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
62
+
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
70
+ end
71
+
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
78
+
79
+ def set_headless_firefox_browser_options
80
+ configure do |capabilities|
81
+ capabilities.add_argument("-headless")
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Driver # :nodoc:
6
+ def initialize(name, **options, &capabilities)
7
+ @name = name
8
+ @browser = Browser.new(options[:using])
9
+ @screen_size = options[:screen_size]
10
+ @options = options[:options] || {}
11
+ @capabilities = capabilities
12
+
13
+ if name == :selenium
14
+ require "selenium/webdriver"
15
+ @browser.preload
16
+ end
17
+ end
18
+
19
+ def use
20
+ register if registerable?
21
+
22
+ setup
23
+ end
24
+
25
+ private
26
+ def registerable?
27
+ [:selenium, :poltergeist, :webkit].include?(@name)
28
+ end
29
+
30
+ def register
31
+ @browser.configure(&@capabilities)
32
+
33
+ Capybara.register_driver @name do |app|
34
+ case @name
35
+ when :selenium then register_selenium(app)
36
+ when :poltergeist then register_poltergeist(app)
37
+ when :webkit then register_webkit(app)
38
+ end
39
+ end
40
+ end
41
+
42
+ def browser_options
43
+ @options.merge(options: @browser.options).compact
44
+ end
45
+
46
+ def register_selenium(app)
47
+ Capybara::Selenium::Driver.new(app, **{ browser: @browser.type }.merge(browser_options)).tap do |driver|
48
+ driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
49
+ end
50
+ end
51
+
52
+ def register_poltergeist(app)
53
+ Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
54
+ end
55
+
56
+ def register_webkit(app)
57
+ Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
58
+ driver.resize_window_to(driver.current_window_handle, *@screen_size)
59
+ end
60
+ end
61
+
62
+ def setup
63
+ Capybara.current_driver = @name
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Server # :nodoc:
6
+ class << self
7
+ attr_accessor :silence_puma
8
+ end
9
+
10
+ self.silence_puma = false
11
+
12
+ def run
13
+ setup
14
+ end
15
+
16
+ private
17
+ def setup
18
+ set_server
19
+ set_port
20
+ end
21
+
22
+ def set_server
23
+ Capybara.server = :puma, { Silent: self.class.silence_puma } if Capybara.server == Capybara.servers[:default]
24
+ end
25
+
26
+ def set_port
27
+ Capybara.always_include_port = true
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ module TestHelpers
6
+ # Screenshot helper for system testing.
7
+ module ScreenshotHelper
8
+ # Takes a screenshot of the current page in the browser.
9
+ #
10
+ # +take_screenshot+ can be used at any point in your system tests to take
11
+ # a screenshot of the current state. This can be useful for debugging or
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)
15
+ #
16
+ # The screenshot will be displayed in your console, if supported.
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
+ #
22
+ # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
23
+ # control the output. Possible values are:
24
+ # * [+simple+ (default)] Only displays the screenshot path.
25
+ # This is the default value.
26
+ # * [+inline+] Display the screenshot in the terminal using the
27
+ # iTerm image protocol (https://iterm2.com/documentation-images.html).
28
+ # * [+artifact+] Display the screenshot in the terminal, using the terminal
29
+ # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
30
+ def take_screenshot
31
+ increment_unique
32
+ save_html if save_html?
33
+ save_image
34
+ puts display_image
35
+ end
36
+
37
+ # Takes a screenshot of the current page in the browser if the test
38
+ # failed.
39
+ #
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.
44
+ def take_failed_screenshot
45
+ take_screenshot if failed? && supports_screenshot?
46
+ end
47
+
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
+
64
+ def image_name
65
+ sanitized_method_name = method_name.tr("/\\", "--")
66
+ name = "#{unique}_#{sanitized_method_name}"
67
+ name[0...225]
68
+ end
69
+
70
+ def image_path
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}")
80
+ end
81
+
82
+ def absolute_image_path
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)
92
+ end
93
+
94
+ def save_image
95
+ page.save_screenshot(absolute_image_path)
96
+ end
97
+
98
+ def output_type
99
+ # Environment variables have priority
100
+ output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"]
101
+
102
+ # Default to outputting a path to the screenshot
103
+ output_type ||= "simple"
104
+
105
+ output_type
106
+ end
107
+
108
+ def display_image
109
+ message = +"[Screenshot Image]: #{image_path}\n"
110
+ message << +"[Screenshot HTML]: #{html_path}\n" if save_html?
111
+
112
+ case output_type
113
+ when "artifact"
114
+ message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
115
+ when "inline"
116
+ name = inline_base64(File.basename(absolute_image_path))
117
+ image = inline_base64(File.read(absolute_image_path))
118
+ message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
119
+ end
120
+
121
+ message
122
+ end
123
+
124
+ def inline_base64(path)
125
+ Base64.strict_encode64(path)
126
+ end
127
+
128
+ def failed?
129
+ !passed? && !skipped?
130
+ end
131
+
132
+ def supports_screenshot?
133
+ Capybara.current_driver != :rack_test
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ module TestHelpers
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
+ def before_teardown
16
+ take_failed_screenshot
17
+ ensure
18
+ super
19
+ end
20
+
21
+ def after_teardown
22
+ Capybara.reset_sessions!
23
+ ensure
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ # This is a class that abstracts away an asserted response. It purposely
5
+ # does not inherit from Response because it doesn't need it. That means it
6
+ # does not have headers or a body.
7
+ class AssertionResponse
8
+ attr_reader :code, :name
9
+
10
+ GENERIC_RESPONSE_CODES = { # :nodoc:
11
+ success: "2XX",
12
+ missing: "404",
13
+ redirect: "3XX",
14
+ error: "5XX"
15
+ }
16
+
17
+ # Accepts a specific response status code as an Integer (404) or String
18
+ # ('404') or a response status range as a Symbol pseudo-code (:success,
19
+ # indicating any 200-299 status code).
20
+ def initialize(code_or_name)
21
+ if code_or_name.is_a?(Symbol)
22
+ @name = code_or_name
23
+ @code = code_from_name(code_or_name)
24
+ else
25
+ @name = name_from_code(code_or_name)
26
+ @code = code_or_name
27
+ end
28
+
29
+ raise ArgumentError, "Invalid response name: #{name}" if @code.nil?
30
+ raise ArgumentError, "Invalid response code: #{code}" if @name.nil?
31
+ end
32
+
33
+ def code_and_name
34
+ "#{code}: #{name}"
35
+ end
36
+
37
+ private
38
+ def code_from_name(name)
39
+ GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
40
+ end
41
+
42
+ def name_from_code(code)
43
+ GENERIC_RESPONSE_CODES.invert[code] || Rack::Utils::HTTP_STATUS_CODES[code]
44
+ end
45
+ end
46
+ end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module ActionDispatch
3
4
  module Assertions
4
5
  # A small suite of assertions that test responses from \Rails applications.
5
6
  module ResponseAssertions
7
+ RESPONSE_PREDICATES = { # :nodoc:
8
+ success: :successful?,
9
+ missing: :not_found?,
10
+ redirect: :redirection?,
11
+ error: :server_error?,
12
+ }
13
+
6
14
  # Asserts that the response is one of the following types:
7
15
  #
8
16
  # * <tt>:success</tt> - Status code was in the 200-299 range
@@ -14,45 +22,35 @@ module ActionDispatch
14
22
  # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
15
23
  # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
16
24
  #
17
- # # assert that the response was a redirection
25
+ # # Asserts that the response was a redirection
18
26
  # assert_response :redirect
19
27
  #
20
- # # assert that the response code was status code 401 (unauthorized)
28
+ # # Asserts that the response code was status code 401 (unauthorized)
21
29
  # assert_response 401
22
30
  def assert_response(type, message = nil)
23
- message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
31
+ message ||= generate_response_message(type)
24
32
 
25
- if Symbol === type
26
- if [:success, :missing, :redirect, :error].include?(type)
27
- assert @response.send("#{type}?"), message
28
- else
29
- code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
30
- if code.nil?
31
- raise ArgumentError, "Invalid response type :#{type}"
32
- end
33
- assert_equal code, @response.response_code, message
34
- end
33
+ if RESPONSE_PREDICATES.keys.include?(type)
34
+ assert @response.public_send(RESPONSE_PREDICATES[type]), message
35
35
  else
36
- assert_equal type, @response.response_code, message
36
+ assert_equal AssertionResponse.new(type).code, @response.response_code, message
37
37
  end
38
38
  end
39
39
 
40
- # Assert 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
- # # assert that the redirection was to the "index" action on the WeblogController
42
+ # # Asserts that the redirection was to the "index" action on the WeblogController
45
43
  # assert_redirected_to controller: "weblog", action: "index"
46
44
  #
47
- # # assert that the redirection was to the named route login_url
45
+ # # Asserts that the redirection was to the named route login_url
48
46
  # assert_redirected_to login_url
49
47
  #
50
- # # assert that the redirection was to the url for @customer
48
+ # # Asserts that the redirection was to the URL for @customer
51
49
  # assert_redirected_to @customer
52
50
  #
53
- # # asserts that the redirection matches the regular expression
51
+ # # Asserts that the redirection matches the regular expression
54
52
  # assert_redirected_to %r(\Ahttp://example.org)
55
- def assert_redirected_to(options = {}, message=nil)
53
+ def assert_redirected_to(options = {}, message = nil)
56
54
  assert_response(:redirect, message)
57
55
  return true if options === @response.location
58
56
 
@@ -77,6 +75,30 @@ module ActionDispatch
77
75
  handle._compute_redirect_to_location(@request, fragment)
78
76
  end
79
77
  end
78
+
79
+ def generate_response_message(expected, actual = @response.response_code)
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)
82
+ end
83
+
84
+ def response_body_if_short
85
+ return "" if @response.body.size > 500
86
+ "\nResponse body: #{@response.body}"
87
+ end
88
+
89
+ def location_if_redirected
90
+ return "" unless @response.redirection? && @response.location.present?
91
+ location = normalize_argument_to_redirection(@response.location)
92
+ " redirect to <#{location}>"
93
+ end
94
+
95
+ def code_with_name(code_or_name)
96
+ if RESPONSE_PREDICATES.values.include?("#{code_or_name}?".to_sym)
97
+ code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
98
+ end
99
+
100
+ AssertionResponse.new(code_or_name).code_and_name
101
+ end
80
102
  end
81
103
  end
82
104
  end