actionpack 5.2.3
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +429 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller.rb +27 -0
- data/lib/abstract_controller/asset_paths.rb +12 -0
- data/lib/abstract_controller/base.rb +265 -0
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/caching/fragments.rb +166 -0
- data/lib/abstract_controller/callbacks.rb +212 -0
- data/lib/abstract_controller/collector.rb +43 -0
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +194 -0
- data/lib/abstract_controller/logger.rb +14 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
- data/lib/abstract_controller/rendering.rb +127 -0
- data/lib/abstract_controller/translation.rb +31 -0
- data/lib/abstract_controller/url_for.rb +35 -0
- data/lib/action_controller.rb +66 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/base.rb +276 -0
- data/lib/action_controller/caching.rb +46 -0
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +78 -0
- data/lib/action_controller/metal.rb +256 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +274 -0
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +16 -0
- data/lib/action_controller/metal/data_streaming.rb +152 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
- data/lib/action_controller/metal/exceptions.rb +53 -0
- data/lib/action_controller/metal/flash.rb +61 -0
- data/lib/action_controller/metal/force_ssl.rb +99 -0
- data/lib/action_controller/metal/head.rb +60 -0
- data/lib/action_controller/metal/helpers.rb +123 -0
- data/lib/action_controller/metal/http_authentication.rb +519 -0
- data/lib/action_controller/metal/implicit_render.rb +73 -0
- data/lib/action_controller/metal/instrumentation.rb +107 -0
- data/lib/action_controller/metal/live.rb +312 -0
- data/lib/action_controller/metal/mime_responds.rb +313 -0
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +293 -0
- data/lib/action_controller/metal/redirecting.rb +133 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +122 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
- data/lib/action_controller/metal/rescue.rb +28 -0
- data/lib/action_controller/metal/streaming.rb +223 -0
- data/lib/action_controller/metal/strong_parameters.rb +1086 -0
- data/lib/action_controller/metal/testing.rb +16 -0
- data/lib/action_controller/metal/url_for.rb +58 -0
- data/lib/action_controller/railtie.rb +89 -0
- data/lib/action_controller/railties/helpers.rb +24 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +629 -0
- data/lib/action_dispatch.rb +112 -0
- data/lib/action_dispatch/http/cache.rb +222 -0
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +84 -0
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +132 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
- data/lib/action_dispatch/http/mime_type.rb +342 -0
- data/lib/action_dispatch/http/mime_types.rb +50 -0
- data/lib/action_dispatch/http/parameter_filter.rb +86 -0
- data/lib/action_dispatch/http/parameters.rb +126 -0
- data/lib/action_dispatch/http/rack_cache.rb +63 -0
- data/lib/action_dispatch/http/request.rb +430 -0
- data/lib/action_dispatch/http/response.rb +519 -0
- data/lib/action_dispatch/http/upload.rb +84 -0
- data/lib/action_dispatch/http/url.rb +350 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/journey/formatter.rb +189 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
- data/lib/action_dispatch/journey/nodes/node.rb +140 -0
- data/lib/action_dispatch/journey/parser.rb +199 -0
- data/lib/action_dispatch/journey/parser.y +50 -0
- data/lib/action_dispatch/journey/parser_extras.rb +31 -0
- data/lib/action_dispatch/journey/path/pattern.rb +198 -0
- data/lib/action_dispatch/journey/route.rb +203 -0
- data/lib/action_dispatch/journey/router.rb +156 -0
- data/lib/action_dispatch/journey/router/utils.rb +102 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +64 -0
- data/lib/action_dispatch/journey/visitors.rb +268 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/middleware/callbacks.rb +36 -0
- data/lib/action_dispatch/middleware/cookies.rb +685 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +300 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
- data/lib/action_dispatch/middleware/reloader.rb +12 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
- data/lib/action_dispatch/middleware/request_id.rb +43 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
- data/lib/action_dispatch/middleware/ssl.rb +150 -0
- data/lib/action_dispatch/middleware/stack.rb +116 -0
- data/lib/action_dispatch/middleware/static.rb +130 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +55 -0
- data/lib/action_dispatch/request/session.rb +234 -0
- data/lib/action_dispatch/request/utils.rb +78 -0
- data/lib/action_dispatch/routing.rb +260 -0
- data/lib/action_dispatch/routing/endpoint.rb +17 -0
- data/lib/action_dispatch/routing/inspector.rb +225 -0
- data/lib/action_dispatch/routing/mapper.rb +2267 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
- data/lib/action_dispatch/routing/redirection.rb +201 -0
- data/lib/action_dispatch/routing/route_set.rb +890 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
- data/lib/action_dispatch/routing/url_for.rb +236 -0
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions.rb +24 -0
- data/lib/action_dispatch/testing/assertions/response.rb +107 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
- data/lib/action_dispatch/testing/integration.rb +652 -0
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +50 -0
- data/lib/action_dispatch/testing/test_request.rb +71 -0
- data/lib/action_dispatch/testing/test_response.rb +53 -0
- data/lib/action_pack.rb +26 -0
- data/lib/action_pack/gem_version.rb +17 -0
- data/lib/action_pack/version.rb +10 -0
- metadata +318 -0
@@ -0,0 +1,49 @@
|
|
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
|
+
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?
|
37
|
+
|
38
|
+
options
|
39
|
+
end
|
40
|
+
|
41
|
+
def headless_firefox_browser_options
|
42
|
+
options = Selenium::WebDriver::Firefox::Options.new
|
43
|
+
options.args << "-headless"
|
44
|
+
|
45
|
+
options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
module SystemTesting
|
5
|
+
class Driver # :nodoc:
|
6
|
+
def initialize(name, **options)
|
7
|
+
@name = name
|
8
|
+
@browser = Browser.new(options[:using])
|
9
|
+
@screen_size = options[:screen_size]
|
10
|
+
@options = options[:options]
|
11
|
+
end
|
12
|
+
|
13
|
+
def use
|
14
|
+
register if registerable?
|
15
|
+
|
16
|
+
setup
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def registerable?
|
21
|
+
[:selenium, :poltergeist, :webkit].include?(@name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def register
|
25
|
+
Capybara.register_driver @name do |app|
|
26
|
+
case @name
|
27
|
+
when :selenium then register_selenium(app)
|
28
|
+
when :poltergeist then register_poltergeist(app)
|
29
|
+
when :webkit then register_webkit(app)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def browser_options
|
35
|
+
@options.merge(options: @browser.options).compact
|
36
|
+
end
|
37
|
+
|
38
|
+
def register_selenium(app)
|
39
|
+
Capybara::Selenium::Driver.new(app, { browser: @browser.type }.merge(browser_options)).tap do |driver|
|
40
|
+
driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def register_poltergeist(app)
|
45
|
+
Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
|
46
|
+
end
|
47
|
+
|
48
|
+
def register_webkit(app)
|
49
|
+
Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
|
50
|
+
driver.resize_window_to(driver.current_window_handle, *@screen_size)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup
|
55
|
+
Capybara.current_driver = @name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
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,96 @@
|
|
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/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
|
+
failed? ? "failures_#{method_name}" : method_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def image_path
|
46
|
+
@image_path ||= absolute_image_path.relative_path_from(Pathname.pwd).to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def absolute_image_path
|
50
|
+
Rails.root.join("tmp/screenshots/#{image_name}.png")
|
51
|
+
end
|
52
|
+
|
53
|
+
def save_image
|
54
|
+
page.save_screenshot(absolute_image_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def output_type
|
58
|
+
# Environment variables have priority
|
59
|
+
output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"]
|
60
|
+
|
61
|
+
# Default to outputting a path to the screenshot
|
62
|
+
output_type ||= "simple"
|
63
|
+
|
64
|
+
output_type
|
65
|
+
end
|
66
|
+
|
67
|
+
def display_image
|
68
|
+
message = "[Screenshot]: #{image_path}\n".dup
|
69
|
+
|
70
|
+
case output_type
|
71
|
+
when "artifact"
|
72
|
+
message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
|
73
|
+
when "inline"
|
74
|
+
name = inline_base64(File.basename(absolute_image_path))
|
75
|
+
image = inline_base64(File.read(absolute_image_path))
|
76
|
+
message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
message
|
80
|
+
end
|
81
|
+
|
82
|
+
def inline_base64(path)
|
83
|
+
Base64.encode64(path).gsub("\n", "")
|
84
|
+
end
|
85
|
+
|
86
|
+
def failed?
|
87
|
+
!passed? && !skipped?
|
88
|
+
end
|
89
|
+
|
90
|
+
def supports_screenshot?
|
91
|
+
Capybara.current_driver != :rack_test
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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 after_teardown
|
20
|
+
begin
|
21
|
+
take_failed_screenshot
|
22
|
+
ensure
|
23
|
+
Capybara.reset_sessions!
|
24
|
+
end
|
25
|
+
ensure
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
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.content_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,107 @@
|
|
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)}>"
|
84
|
+
.dup.concat(location_if_redirected).concat(response_body_if_short)
|
85
|
+
end
|
86
|
+
|
87
|
+
def response_body_if_short
|
88
|
+
return "" if @response.body.size > 500
|
89
|
+
"\nResponse body: #{@response.body}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def location_if_redirected
|
93
|
+
return "" unless @response.redirection? && @response.location.present?
|
94
|
+
location = normalize_argument_to_redirection(@response.location)
|
95
|
+
" redirect to <#{location}>"
|
96
|
+
end
|
97
|
+
|
98
|
+
def code_with_name(code_or_name)
|
99
|
+
if RESPONSE_PREDICATES.values.include?("#{code_or_name}?".to_sym)
|
100
|
+
code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
|
101
|
+
end
|
102
|
+
|
103
|
+
AssertionResponse.new(code_or_name).code_and_name
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|