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