actionpack 6.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +311 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +58 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +267 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +150 -0
  10. data/lib/abstract_controller/callbacks.rb +224 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +32 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +67 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +271 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +81 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +280 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +151 -0
  32. data/lib/action_controller/metal/default_headers.rb +17 -0
  33. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  35. data/lib/action_controller/metal/exceptions.rb +74 -0
  36. data/lib/action_controller/metal/flash.rb +61 -0
  37. data/lib/action_controller/metal/force_ssl.rb +58 -0
  38. data/lib/action_controller/metal/head.rb +60 -0
  39. data/lib/action_controller/metal/helpers.rb +122 -0
  40. data/lib/action_controller/metal/http_authentication.rb +518 -0
  41. data/lib/action_controller/metal/implicit_render.rb +63 -0
  42. data/lib/action_controller/metal/instrumentation.rb +105 -0
  43. data/lib/action_controller/metal/live.rb +314 -0
  44. data/lib/action_controller/metal/mime_responds.rb +324 -0
  45. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  46. data/lib/action_controller/metal/params_wrapper.rb +297 -0
  47. data/lib/action_controller/metal/redirecting.rb +133 -0
  48. data/lib/action_controller/metal/renderers.rb +181 -0
  49. data/lib/action_controller/metal/rendering.rb +122 -0
  50. data/lib/action_controller/metal/request_forgery_protection.rb +456 -0
  51. data/lib/action_controller/metal/rescue.rb +28 -0
  52. data/lib/action_controller/metal/streaming.rb +223 -0
  53. data/lib/action_controller/metal/strong_parameters.rb +1105 -0
  54. data/lib/action_controller/metal/testing.rb +16 -0
  55. data/lib/action_controller/metal/url_for.rb +58 -0
  56. data/lib/action_controller/railtie.rb +89 -0
  57. data/lib/action_controller/railties/helpers.rb +24 -0
  58. data/lib/action_controller/renderer.rb +130 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +626 -0
  61. data/lib/action_dispatch.rb +114 -0
  62. data/lib/action_dispatch/http/cache.rb +226 -0
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +284 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +86 -0
  66. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  67. data/lib/action_dispatch/http/headers.rb +132 -0
  68. data/lib/action_dispatch/http/mime_negotiation.rb +177 -0
  69. data/lib/action_dispatch/http/mime_type.rb +350 -0
  70. data/lib/action_dispatch/http/mime_types.rb +50 -0
  71. data/lib/action_dispatch/http/parameter_filter.rb +12 -0
  72. data/lib/action_dispatch/http/parameters.rb +136 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  74. data/lib/action_dispatch/http/request.rb +427 -0
  75. data/lib/action_dispatch/http/response.rb +534 -0
  76. data/lib/action_dispatch/http/upload.rb +92 -0
  77. data/lib/action_dispatch/http/url.rb +350 -0
  78. data/lib/action_dispatch/journey.rb +7 -0
  79. data/lib/action_dispatch/journey/formatter.rb +189 -0
  80. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  81. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  82. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  83. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  84. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  85. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  86. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +141 -0
  88. data/lib/action_dispatch/journey/parser.rb +199 -0
  89. data/lib/action_dispatch/journey/parser.y +50 -0
  90. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +203 -0
  92. data/lib/action_dispatch/journey/route.rb +204 -0
  93. data/lib/action_dispatch/journey/router.rb +153 -0
  94. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  95. data/lib/action_dispatch/journey/routes.rb +81 -0
  96. data/lib/action_dispatch/journey/scanner.rb +71 -0
  97. data/lib/action_dispatch/journey/visitors.rb +268 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  102. data/lib/action_dispatch/middleware/callbacks.rb +34 -0
  103. data/lib/action_dispatch/middleware/cookies.rb +663 -0
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +185 -0
  105. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  106. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -0
  108. data/lib/action_dispatch/middleware/executor.rb +21 -0
  109. data/lib/action_dispatch/middleware/flash.rb +300 -0
  110. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +61 -0
  112. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  113. data/lib/action_dispatch/middleware/remote_ip.rb +181 -0
  114. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +113 -0
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  120. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  121. data/lib/action_dispatch/middleware/stack.rb +148 -0
  122. data/lib/action_dispatch/middleware/static.rb +129 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +24 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +29 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +38 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +165 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  148. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  149. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +203 -0
  150. data/lib/action_dispatch/railtie.rb +58 -0
  151. data/lib/action_dispatch/request/session.rb +242 -0
  152. data/lib/action_dispatch/request/utils.rb +78 -0
  153. data/lib/action_dispatch/routing.rb +261 -0
  154. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  155. data/lib/action_dispatch/routing/inspector.rb +274 -0
  156. data/lib/action_dispatch/routing/mapper.rb +2289 -0
  157. data/lib/action_dispatch/routing/polymorphic_routes.rb +351 -0
  158. data/lib/action_dispatch/routing/redirection.rb +201 -0
  159. data/lib/action_dispatch/routing/route_set.rb +887 -0
  160. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  161. data/lib/action_dispatch/routing/url_for.rb +237 -0
  162. data/lib/action_dispatch/system_test_case.rb +168 -0
  163. data/lib/action_dispatch/system_testing/browser.rb +80 -0
  164. data/lib/action_dispatch/system_testing/driver.rb +68 -0
  165. data/lib/action_dispatch/system_testing/server.rb +31 -0
  166. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +97 -0
  167. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +33 -0
  168. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  169. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  170. data/lib/action_dispatch/testing/assertions.rb +24 -0
  171. data/lib/action_dispatch/testing/assertions/response.rb +106 -0
  172. data/lib/action_dispatch/testing/assertions/routing.rb +234 -0
  173. data/lib/action_dispatch/testing/integration.rb +659 -0
  174. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  175. data/lib/action_dispatch/testing/test_process.rb +50 -0
  176. data/lib/action_dispatch/testing/test_request.rb +71 -0
  177. data/lib/action_dispatch/testing/test_response.rb +25 -0
  178. data/lib/action_pack.rb +26 -0
  179. data/lib/action_pack/gem_version.rb +17 -0
  180. data/lib/action_pack/version.rb +10 -0
  181. metadata +329 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Browser # :nodoc:
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def type
13
+ case name
14
+ when :headless_chrome
15
+ :chrome
16
+ when :headless_firefox
17
+ :firefox
18
+ else
19
+ name
20
+ end
21
+ end
22
+
23
+ def options
24
+ case name
25
+ when :headless_chrome
26
+ headless_chrome_browser_options
27
+ when :headless_firefox
28
+ headless_firefox_browser_options
29
+ end
30
+ end
31
+
32
+ def capabilities
33
+ @option ||=
34
+ case type
35
+ when :chrome
36
+ ::Selenium::WebDriver::Chrome::Options.new
37
+ when :firefox
38
+ ::Selenium::WebDriver::Firefox::Options.new
39
+ end
40
+ end
41
+
42
+ # driver_path can be configured as a proc. The webdrivers gem uses this
43
+ # proc to update web drivers. Running this proc early allows us to only
44
+ # update the webdriver once and avoid race conditions when using
45
+ # parallel tests.
46
+ def preload
47
+ case type
48
+ when :chrome
49
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
50
+ ::Selenium::WebDriver::Chrome::Service.driver_path.try(:call)
51
+ else
52
+ # Selenium <= v3.141.0
53
+ ::Selenium::WebDriver::Chrome.driver_path
54
+ end
55
+ when :firefox
56
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
57
+ ::Selenium::WebDriver::Firefox::Service.driver_path.try(:call)
58
+ else
59
+ # Selenium <= v3.141.0
60
+ ::Selenium::WebDriver::Firefox.driver_path
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+ def headless_chrome_browser_options
67
+ capabilities.args << "--headless"
68
+ capabilities.args << "--disable-gpu" if Gem.win_platform?
69
+
70
+ capabilities
71
+ end
72
+
73
+ def headless_firefox_browser_options
74
+ capabilities.args << "-headless"
75
+
76
+ capabilities
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,68 @@
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
+ @browser.preload
14
+ end
15
+
16
+ def use
17
+ register if registerable?
18
+
19
+ setup
20
+ end
21
+
22
+ private
23
+ def registerable?
24
+ [:selenium, :poltergeist, :webkit].include?(@name)
25
+ end
26
+
27
+ def register
28
+ define_browser_capabilities(@browser.capabilities)
29
+
30
+ Capybara.register_driver @name do |app|
31
+ case @name
32
+ when :selenium then register_selenium(app)
33
+ when :poltergeist then register_poltergeist(app)
34
+ when :webkit then register_webkit(app)
35
+ end
36
+ end
37
+ end
38
+
39
+ def define_browser_capabilities(capabilities)
40
+ @capabilities.call(capabilities) if @capabilities
41
+ end
42
+
43
+ def browser_options
44
+ @options.merge(options: @browser.options).compact
45
+ end
46
+
47
+ def register_selenium(app)
48
+ Capybara::Selenium::Driver.new(app, { browser: @browser.type }.merge(browser_options)).tap do |driver|
49
+ driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
50
+ end
51
+ end
52
+
53
+ def register_poltergeist(app)
54
+ Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
55
+ end
56
+
57
+ def register_webkit(app)
58
+ Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
59
+ driver.resize_window_to(driver.current_window_handle, *@screen_size)
60
+ end
61
+ end
62
+
63
+ def setup
64
+ Capybara.current_driver = @name
65
+ end
66
+ end
67
+ end
68
+ 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,97 @@
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.
13
+ #
14
+ # The screenshot will be displayed in your console, if supported.
15
+ #
16
+ # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
17
+ # control the output. Possible values are:
18
+ # * [+simple+ (default)] Only displays the screenshot path.
19
+ # This is the default value.
20
+ # * [+inline+] Display the screenshot in the terminal using the
21
+ # iTerm image protocol (https://iterm2.com/documentation-images.html).
22
+ # * [+artifact+] Display the screenshot in the terminal, using the terminal
23
+ # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
24
+ def take_screenshot
25
+ save_image
26
+ puts display_image
27
+ end
28
+
29
+ # Takes a screenshot of the current page in the browser if the test
30
+ # failed.
31
+ #
32
+ # +take_failed_screenshot+ is included in <tt>application_system_test_case.rb</tt>
33
+ # that is generated with the application. To take screenshots when a test
34
+ # fails add +take_failed_screenshot+ to the teardown block before clearing
35
+ # sessions.
36
+ def take_failed_screenshot
37
+ take_screenshot if failed? && supports_screenshot?
38
+ end
39
+
40
+ private
41
+ def image_name
42
+ name = method_name[0...225]
43
+ failed? ? "failures_#{name}" : name
44
+ end
45
+
46
+ def image_path
47
+ @image_path ||= absolute_image_path.to_s
48
+ end
49
+
50
+ def absolute_image_path
51
+ Rails.root.join("tmp/screenshots/#{image_name}.png")
52
+ end
53
+
54
+ def save_image
55
+ page.save_screenshot(absolute_image_path)
56
+ end
57
+
58
+ def output_type
59
+ # Environment variables have priority
60
+ output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"]
61
+
62
+ # Default to outputting a path to the screenshot
63
+ output_type ||= "simple"
64
+
65
+ output_type
66
+ end
67
+
68
+ def display_image
69
+ message = +"[Screenshot]: #{image_path}\n"
70
+
71
+ case output_type
72
+ when "artifact"
73
+ message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
74
+ when "inline"
75
+ name = inline_base64(File.basename(absolute_image_path))
76
+ image = inline_base64(File.read(absolute_image_path))
77
+ message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
78
+ end
79
+
80
+ message
81
+ end
82
+
83
+ def inline_base64(path)
84
+ Base64.strict_encode64(path)
85
+ end
86
+
87
+ def failed?
88
+ !passed? && !skipped?
89
+ end
90
+
91
+ def supports_screenshot?
92
+ Capybara.current_driver != :rack_test
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ module TestHelpers
6
+ module SetupAndTeardown # :nodoc:
7
+ DEFAULT_HOST = "http://127.0.0.1"
8
+
9
+ def host!(host)
10
+ super
11
+ Capybara.app_host = host
12
+ end
13
+
14
+ def before_setup
15
+ host! DEFAULT_HOST
16
+ super
17
+ end
18
+
19
+ def before_teardown
20
+ take_failed_screenshot
21
+ ensure
22
+ super
23
+ end
24
+
25
+ def after_teardown
26
+ Capybara.reset_sessions!
27
+ ensure
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ module TestHelpers
6
+ module UndefMethods # :nodoc:
7
+ extend ActiveSupport::Concern
8
+ included do
9
+ METHODS = %i(get post put patch delete).freeze
10
+
11
+ METHODS.each do |verb|
12
+ undef_method verb
13
+ end
14
+
15
+ def method_missing(method, *args, &block)
16
+ if METHODS.include?(method)
17
+ raise NoMethodError, "System tests cannot make direct requests via ##{method}; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information."
18
+ else
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,47 @@
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
+
39
+ def code_from_name(name)
40
+ GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
41
+ end
42
+
43
+ def name_from_code(code)
44
+ GENERIC_RESPONSE_CODES.invert[code] || Rack::Utils::HTTP_STATUS_CODES[code]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails-dom-testing"
4
+
5
+ module ActionDispatch
6
+ module Assertions
7
+ autoload :ResponseAssertions, "action_dispatch/testing/assertions/response"
8
+ autoload :RoutingAssertions, "action_dispatch/testing/assertions/routing"
9
+
10
+ extend ActiveSupport::Concern
11
+
12
+ include ResponseAssertions
13
+ include RoutingAssertions
14
+ include Rails::Dom::Testing::Assertions
15
+
16
+ def html_document
17
+ @html_document ||= if @response.media_type.to_s.end_with?("xml")
18
+ Nokogiri::XML::Document.parse(@response.body)
19
+ else
20
+ Nokogiri::HTML::Document.parse(@response.body)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Assertions
5
+ # A small suite of assertions that test responses from \Rails applications.
6
+ module ResponseAssertions
7
+ RESPONSE_PREDICATES = { # :nodoc:
8
+ success: :successful?,
9
+ missing: :not_found?,
10
+ redirect: :redirection?,
11
+ error: :server_error?,
12
+ }
13
+
14
+ # Asserts that the response is one of the following types:
15
+ #
16
+ # * <tt>:success</tt> - Status code was in the 200-299 range
17
+ # * <tt>:redirect</tt> - Status code was in the 300-399 range
18
+ # * <tt>:missing</tt> - Status code was 404
19
+ # * <tt>:error</tt> - Status code was in the 500-599 range
20
+ #
21
+ # You can also pass an explicit status number like <tt>assert_response(501)</tt>
22
+ # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
23
+ # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
24
+ #
25
+ # # Asserts that the response was a redirection
26
+ # assert_response :redirect
27
+ #
28
+ # # Asserts that the response code was status code 401 (unauthorized)
29
+ # assert_response 401
30
+ def assert_response(type, message = nil)
31
+ message ||= generate_response_message(type)
32
+
33
+ if RESPONSE_PREDICATES.keys.include?(type)
34
+ assert @response.send(RESPONSE_PREDICATES[type]), message
35
+ else
36
+ assert_equal AssertionResponse.new(type).code, @response.response_code, message
37
+ end
38
+ end
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.
43
+ #
44
+ # # Asserts that the redirection was to the "index" action on the WeblogController
45
+ # assert_redirected_to controller: "weblog", action: "index"
46
+ #
47
+ # # Asserts that the redirection was to the named route login_url
48
+ # assert_redirected_to login_url
49
+ #
50
+ # # Asserts that the redirection was to the URL for @customer
51
+ # assert_redirected_to @customer
52
+ #
53
+ # # Asserts that the redirection matches the regular expression
54
+ # assert_redirected_to %r(\Ahttp://example.org)
55
+ def assert_redirected_to(options = {}, message = nil)
56
+ assert_response(:redirect, message)
57
+ return true if options === @response.location
58
+
59
+ redirect_is = normalize_argument_to_redirection(@response.location)
60
+ redirect_expected = normalize_argument_to_redirection(options)
61
+
62
+ message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
63
+ assert_operator redirect_expected, :===, redirect_is, message
64
+ end
65
+
66
+ private
67
+ # Proxy to to_param if the object will respond to it.
68
+ def parameterize(value)
69
+ value.respond_to?(:to_param) ? value.to_param : value
70
+ end
71
+
72
+ def normalize_argument_to_redirection(fragment)
73
+ if Regexp === fragment
74
+ fragment
75
+ else
76
+ handle = @controller || ActionController::Redirecting
77
+ handle._compute_redirect_to_location(@request, fragment)
78
+ end
79
+ end
80
+
81
+ 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)}>").concat(location_if_redirected).concat(response_body_if_short)
84
+ end
85
+
86
+ def response_body_if_short
87
+ return "" if @response.body.size > 500
88
+ "\nResponse body: #{@response.body}"
89
+ end
90
+
91
+ def location_if_redirected
92
+ return "" unless @response.redirection? && @response.location.present?
93
+ location = normalize_argument_to_redirection(@response.location)
94
+ " redirect to <#{location}>"
95
+ end
96
+
97
+ def code_with_name(code_or_name)
98
+ if RESPONSE_PREDICATES.values.include?("#{code_or_name}?".to_sym)
99
+ code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
100
+ end
101
+
102
+ AssertionResponse.new(code_or_name).code_and_name
103
+ end
104
+ end
105
+ end
106
+ end