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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- 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
|
-
# #
|
25
|
+
# # Asserts that the response was a redirection
|
18
26
|
# assert_response :redirect
|
19
27
|
#
|
20
|
-
# #
|
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 ||=
|
31
|
+
message ||= generate_response_message(type)
|
24
32
|
|
25
|
-
if
|
26
|
-
|
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
|
-
#
|
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"
|
46
44
|
#
|
47
|
-
# #
|
45
|
+
# # Asserts that the redirection was to the named route login_url
|
48
46
|
# assert_redirected_to login_url
|
49
47
|
#
|
50
|
-
# #
|
48
|
+
# # Asserts that the redirection was to the URL for @customer
|
51
49
|
# assert_redirected_to @customer
|
52
50
|
#
|
53
|
-
# #
|
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
|