actionpack 6.1.7.5 → 7.1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +67 -30
@@ -13,42 +13,45 @@ module ActionDispatch
|
|
13
13
|
# to investigate changes at different points during your test. These will be
|
14
14
|
# named with a sequential prefix (or 'failed' for failing tests)
|
15
15
|
#
|
16
|
-
# The
|
16
|
+
# The default screenshots directory is +tmp/screenshots+ but you can set a different
|
17
|
+
# one with +Capybara.save_path+
|
17
18
|
#
|
18
|
-
# You can
|
19
|
-
# save the HTML from the page that is being
|
20
|
-
# elements on the page at the time of the screenshot
|
19
|
+
# You can use the +html+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+
|
20
|
+
# environment variable to save the HTML from the page that is being screenshotted
|
21
|
+
# so you can investigate the elements on the page at the time of the screenshot
|
21
22
|
#
|
22
|
-
# You can
|
23
|
-
# control the output. Possible values are:
|
23
|
+
# You can use the +screenshot+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT+
|
24
|
+
# environment variable to control the output. Possible values are:
|
24
25
|
# * [+simple+ (default)] Only displays the screenshot path.
|
25
26
|
# This is the default value.
|
26
27
|
# * [+inline+] Display the screenshot in the terminal using the
|
27
28
|
# iTerm image protocol (https://iterm2.com/documentation-images.html).
|
28
29
|
# * [+artifact+] Display the screenshot in the terminal, using the terminal
|
29
30
|
# artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
30
|
-
def take_screenshot
|
31
|
+
def take_screenshot(html: false, screenshot: nil)
|
32
|
+
showing_html = html || html_from_env?
|
33
|
+
|
31
34
|
increment_unique
|
32
|
-
save_html if
|
35
|
+
save_html if showing_html
|
33
36
|
save_image
|
34
|
-
|
37
|
+
show display_image(html: showing_html, screenshot_output: screenshot)
|
35
38
|
end
|
36
39
|
|
37
40
|
# Takes a screenshot of the current page in the browser if the test
|
38
41
|
# failed.
|
39
42
|
#
|
40
|
-
# +take_failed_screenshot+ is
|
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.
|
43
|
+
# +take_failed_screenshot+ is called during system test teardown.
|
44
44
|
def take_failed_screenshot
|
45
|
-
|
45
|
+
return unless failed? && supports_screenshot? && Capybara::Session.instance_created?
|
46
|
+
|
47
|
+
take_screenshot
|
48
|
+
metadata[:failure_screenshot_path] = relative_image_path if Minitest::Runnable.method_defined?(:metadata)
|
46
49
|
end
|
47
50
|
|
48
51
|
private
|
49
52
|
attr_accessor :_screenshot_counter
|
50
53
|
|
51
|
-
def
|
54
|
+
def html_from_env?
|
52
55
|
ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
|
53
56
|
end
|
54
57
|
|
@@ -62,7 +65,7 @@ module ActionDispatch
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def image_name
|
65
|
-
sanitized_method_name = method_name.
|
68
|
+
sanitized_method_name = method_name.gsub(/[^\w]+/, "-")
|
66
69
|
name = "#{unique}_#{sanitized_method_name}"
|
67
70
|
name[0...225]
|
68
71
|
end
|
@@ -76,13 +79,21 @@ module ActionDispatch
|
|
76
79
|
end
|
77
80
|
|
78
81
|
def absolute_path
|
79
|
-
Rails.root.join(
|
82
|
+
Rails.root.join(screenshots_dir, image_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
def screenshots_dir
|
86
|
+
Capybara.save_path.presence || "tmp/screenshots"
|
80
87
|
end
|
81
88
|
|
82
89
|
def absolute_image_path
|
83
90
|
"#{absolute_path}.png"
|
84
91
|
end
|
85
92
|
|
93
|
+
def relative_image_path
|
94
|
+
"#{absolute_path.relative_path_from(Rails.root)}.png"
|
95
|
+
end
|
96
|
+
|
86
97
|
def absolute_html_path
|
87
98
|
"#{absolute_path}.html"
|
88
99
|
end
|
@@ -105,11 +116,15 @@ module ActionDispatch
|
|
105
116
|
output_type
|
106
117
|
end
|
107
118
|
|
108
|
-
def
|
119
|
+
def show(img)
|
120
|
+
puts img
|
121
|
+
end
|
122
|
+
|
123
|
+
def display_image(html:, screenshot_output:)
|
109
124
|
message = +"[Screenshot Image]: #{image_path}\n"
|
110
|
-
message << +"[Screenshot HTML]: #{html_path}\n" if
|
125
|
+
message << +"[Screenshot HTML]: #{html_path}\n" if html
|
111
126
|
|
112
|
-
case output_type
|
127
|
+
case screenshot_output || output_type
|
113
128
|
when "artifact"
|
114
129
|
message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
|
115
130
|
when "inline"
|
@@ -4,14 +4,6 @@ module ActionDispatch
|
|
4
4
|
module SystemTesting
|
5
5
|
module TestHelpers
|
6
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
7
|
def before_teardown
|
16
8
|
take_failed_screenshot
|
17
9
|
ensure
|
@@ -20,7 +20,7 @@ module ActionDispatch
|
|
20
20
|
#
|
21
21
|
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
|
22
22
|
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
|
23
|
-
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
|
23
|
+
# See +Rack::Utils::SYMBOL_TO_STATUS_CODE+ for a full list.
|
24
24
|
#
|
25
25
|
# # Asserts that the response was a redirection
|
26
26
|
# assert_response :redirect
|
@@ -30,7 +30,7 @@ module ActionDispatch
|
|
30
30
|
def assert_response(type, message = nil)
|
31
31
|
message ||= generate_response_message(type)
|
32
32
|
|
33
|
-
if RESPONSE_PREDICATES.
|
33
|
+
if RESPONSE_PREDICATES.key?(type)
|
34
34
|
assert @response.public_send(RESPONSE_PREDICATES[type]), message
|
35
35
|
else
|
36
36
|
assert_equal AssertionResponse.new(type).code, @response.response_code, message
|
@@ -50,12 +50,19 @@ module ActionDispatch
|
|
50
50
|
#
|
51
51
|
# # Asserts that the redirection matches the regular expression
|
52
52
|
# assert_redirected_to %r(\Ahttp://example.org)
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
#
|
54
|
+
# # Asserts that the redirection has the HTTP status code 301 (Moved
|
55
|
+
# # Permanently).
|
56
|
+
# assert_redirected_to "/some/path", status: :moved_permanently
|
57
|
+
def assert_redirected_to(url_options = {}, options = {}, message = nil)
|
58
|
+
options, message = {}, options unless options.is_a?(Hash)
|
59
|
+
|
60
|
+
status = options[:status] || :redirect
|
61
|
+
assert_response(status, message)
|
62
|
+
return true if url_options === @response.location
|
56
63
|
|
57
64
|
redirect_is = normalize_argument_to_redirection(@response.location)
|
58
|
-
redirect_expected = normalize_argument_to_redirection(
|
65
|
+
redirect_expected = normalize_argument_to_redirection(url_options)
|
59
66
|
|
60
67
|
message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
|
61
68
|
assert_operator redirect_expected, :===, redirect_is, message
|
@@ -93,7 +100,7 @@ module ActionDispatch
|
|
93
100
|
end
|
94
101
|
|
95
102
|
def code_with_name(code_or_name)
|
96
|
-
if RESPONSE_PREDICATES.
|
103
|
+
if RESPONSE_PREDICATES.value?("#{code_or_name}?".to_sym)
|
97
104
|
code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
|
98
105
|
end
|
99
106
|
|
@@ -9,6 +9,36 @@ 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
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
# A helper to make it easier to test different route configurations.
|
16
|
+
# This method temporarily replaces @routes with a new RouteSet instance
|
17
|
+
# before each test.
|
18
|
+
#
|
19
|
+
# The new instance is yielded to the passed block. Typically the block
|
20
|
+
# will create some routes using <tt>set.draw { match ... }</tt>:
|
21
|
+
#
|
22
|
+
# with_routing do |set|
|
23
|
+
# set.draw do
|
24
|
+
# resources :users
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
def with_routing(&block)
|
29
|
+
old_routes, old_controller = nil
|
30
|
+
|
31
|
+
setup do
|
32
|
+
old_routes, old_controller = @routes, @controller
|
33
|
+
create_routes(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
teardown do
|
37
|
+
reset_routes(old_routes, old_controller)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
12
42
|
def setup # :nodoc:
|
13
43
|
@routes ||= nil
|
14
44
|
super
|
@@ -18,8 +48,8 @@ module ActionDispatch
|
|
18
48
|
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
|
19
49
|
#
|
20
50
|
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
|
21
|
-
# requiring a specific HTTP method. The hash should contain a
|
22
|
-
# and a
|
51
|
+
# requiring a specific HTTP method. The hash should contain a +:path+ with the incoming request path
|
52
|
+
# and a +:method+ containing the required HTTP verb.
|
23
53
|
#
|
24
54
|
# # Asserts that POSTing to /items will call the create action on ItemsController
|
25
55
|
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
|
@@ -83,7 +113,7 @@ module ActionDispatch
|
|
83
113
|
# # Asserts that the generated route gives us our custom route
|
84
114
|
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
|
85
115
|
def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
|
86
|
-
if
|
116
|
+
if expected_path.include?("://")
|
87
117
|
fail_on(URI::InvalidURIError, message) do
|
88
118
|
uri = URI.parse(expected_path)
|
89
119
|
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
@@ -150,33 +180,11 @@ module ActionDispatch
|
|
150
180
|
# assert_equal "/users", users_path
|
151
181
|
# end
|
152
182
|
#
|
153
|
-
def with_routing
|
154
|
-
old_routes,
|
155
|
-
|
156
|
-
old_controller, @controller = @controller, @controller.clone
|
157
|
-
_routes = @routes
|
158
|
-
|
159
|
-
@controller.singleton_class.include(_routes.url_helpers)
|
160
|
-
|
161
|
-
if @controller.respond_to? :view_context_class
|
162
|
-
view_context_class = Class.new(@controller.view_context_class) do
|
163
|
-
include _routes.url_helpers
|
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)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
yield @routes
|
183
|
+
def with_routing(&block)
|
184
|
+
old_routes, old_controller = @routes, @controller
|
185
|
+
create_routes(&block)
|
175
186
|
ensure
|
176
|
-
|
177
|
-
if defined?(@controller) && @controller
|
178
|
-
@controller = old_controller
|
179
|
-
end
|
187
|
+
reset_routes(old_routes, old_controller)
|
180
188
|
end
|
181
189
|
|
182
190
|
# ROUTES TODO: These assertions should really work in an integration context
|
@@ -187,8 +195,40 @@ module ActionDispatch
|
|
187
195
|
super
|
188
196
|
end
|
189
197
|
end
|
198
|
+
ruby2_keywords(:method_missing)
|
190
199
|
|
191
200
|
private
|
201
|
+
def create_routes
|
202
|
+
@routes = ActionDispatch::Routing::RouteSet.new
|
203
|
+
if defined?(@controller) && @controller
|
204
|
+
@controller = @controller.clone
|
205
|
+
_routes = @routes
|
206
|
+
|
207
|
+
@controller.singleton_class.include(_routes.url_helpers)
|
208
|
+
|
209
|
+
if @controller.respond_to? :view_context_class
|
210
|
+
view_context_class = Class.new(@controller.view_context_class) do
|
211
|
+
include _routes.url_helpers
|
212
|
+
end
|
213
|
+
|
214
|
+
custom_view_context = Module.new {
|
215
|
+
define_method(:view_context_class) do
|
216
|
+
view_context_class
|
217
|
+
end
|
218
|
+
}
|
219
|
+
@controller.extend(custom_view_context)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
yield @routes
|
223
|
+
end
|
224
|
+
|
225
|
+
def reset_routes(old_routes, old_controller)
|
226
|
+
@routes = old_routes
|
227
|
+
if defined?(@controller) && @controller
|
228
|
+
@controller = old_controller
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
192
232
|
# Recognizes the route for a given path.
|
193
233
|
def recognized_request_for(path, extras = {}, msg)
|
194
234
|
if path.is_a?(Hash)
|
@@ -201,7 +241,7 @@ module ActionDispatch
|
|
201
241
|
controller = @controller if defined?(@controller)
|
202
242
|
request = ActionController::TestRequest.create controller&.class
|
203
243
|
|
204
|
-
if
|
244
|
+
if path.include?("://")
|
205
245
|
fail_on(URI::InvalidURIError, msg) do
|
206
246
|
uri = URI.parse(path)
|
207
247
|
request.env["rack.url_scheme"] = uri.scheme || "http"
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "rails-dom-testing"
|
4
|
+
require "action_dispatch/testing/assertions/response"
|
5
|
+
require "action_dispatch/testing/assertions/routing"
|
4
6
|
|
5
7
|
module ActionDispatch
|
6
8
|
module Assertions
|
7
|
-
autoload :ResponseAssertions, "action_dispatch/testing/assertions/response"
|
8
|
-
autoload :RoutingAssertions, "action_dispatch/testing/assertions/routing"
|
9
|
-
|
10
9
|
extend ActiveSupport::Concern
|
11
10
|
|
12
11
|
include ResponseAssertions
|
@@ -17,7 +16,7 @@ module ActionDispatch
|
|
17
16
|
@html_document ||= if @response.media_type&.end_with?("xml")
|
18
17
|
Nokogiri::XML::Document.parse(@response.body)
|
19
18
|
else
|
20
|
-
|
19
|
+
Rails::Dom::Testing.html_document.parse(@response.body)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -3,12 +3,12 @@
|
|
3
3
|
require "stringio"
|
4
4
|
require "uri"
|
5
5
|
require "rack/test"
|
6
|
-
require "
|
6
|
+
require "active_support/test_case"
|
7
7
|
|
8
8
|
require "action_dispatch/testing/request_encoder"
|
9
9
|
|
10
10
|
module ActionDispatch
|
11
|
-
module Integration
|
11
|
+
module Integration # :nodoc:
|
12
12
|
module RequestHelpers
|
13
13
|
# Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process
|
14
14
|
# for more details.
|
@@ -58,7 +58,9 @@ module ActionDispatch
|
|
58
58
|
# the same HTTP verb will be used when redirecting, otherwise a GET request
|
59
59
|
# will be performed. Any arguments are passed to the
|
60
60
|
# underlying request.
|
61
|
-
|
61
|
+
#
|
62
|
+
# The HTTP_REFERER header will be set to the previous url.
|
63
|
+
def follow_redirect!(headers: {}, **args)
|
62
64
|
raise "not a redirect! #{status} #{status_message}" unless redirect?
|
63
65
|
|
64
66
|
method =
|
@@ -68,7 +70,11 @@ module ActionDispatch
|
|
68
70
|
:get
|
69
71
|
end
|
70
72
|
|
71
|
-
|
73
|
+
if [ :HTTP_REFERER, "HTTP_REFERER" ].none? { |key| headers.key? key }
|
74
|
+
headers["HTTP_REFERER"] = request.url
|
75
|
+
end
|
76
|
+
|
77
|
+
public_send(method, response.location, headers: headers, **args)
|
72
78
|
status
|
73
79
|
end
|
74
80
|
end
|
@@ -78,9 +84,8 @@ module ActionDispatch
|
|
78
84
|
# multiple sessions and run them side-by-side, you can also mimic (to some
|
79
85
|
# limited extent) multiple simultaneous users interacting with your system.
|
80
86
|
#
|
81
|
-
# Typically, you will instantiate a new session using
|
82
|
-
#
|
83
|
-
# Integration::Session directly.
|
87
|
+
# Typically, you will instantiate a new session using Runner#open_session,
|
88
|
+
# rather than instantiating a \Session directly.
|
84
89
|
class Session
|
85
90
|
DEFAULT_HOST = "www.example.com"
|
86
91
|
|
@@ -122,7 +127,7 @@ module ActionDispatch
|
|
122
127
|
|
123
128
|
include ActionDispatch::Routing::UrlFor
|
124
129
|
|
125
|
-
# Create and initialize a new Session instance.
|
130
|
+
# Create and initialize a new \Session instance.
|
126
131
|
def initialize(app)
|
127
132
|
super()
|
128
133
|
@app = app
|
@@ -199,16 +204,17 @@ module ActionDispatch
|
|
199
204
|
# merged into the Rack env hash.
|
200
205
|
# - +env+: Additional env to pass, as a Hash. The headers will be
|
201
206
|
# merged into the Rack env hash.
|
202
|
-
# - +xhr+: Set to +true+ if you want to make
|
207
|
+
# - +xhr+: Set to +true+ if you want to make an Ajax request.
|
203
208
|
# Adds request headers characteristic of XMLHttpRequest e.g. HTTP_X_REQUESTED_WITH.
|
204
209
|
# The headers will be merged into the Rack env hash.
|
205
210
|
# - +as+: Used for encoding the request with different content type.
|
206
211
|
# Supports +:json+ by default and will set the appropriate request headers.
|
207
212
|
# The headers will be merged into the Rack env hash.
|
208
213
|
#
|
209
|
-
# This method is rarely used directly. Use
|
210
|
-
# HTTP methods in integration
|
211
|
-
#
|
214
|
+
# This method is rarely used directly. Use RequestHelpers#get,
|
215
|
+
# RequestHelpers#post, or other standard HTTP methods in integration
|
216
|
+
# tests. +#process+ is only required when using a request method that
|
217
|
+
# doesn't have a method defined in the integration tests.
|
212
218
|
#
|
213
219
|
# This method returns the response status, after performing the request.
|
214
220
|
# Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
|
@@ -226,7 +232,7 @@ module ActionDispatch
|
|
226
232
|
method = :post
|
227
233
|
end
|
228
234
|
|
229
|
-
if
|
235
|
+
if path.include?("://")
|
230
236
|
path = build_expanded_path(path) do |location|
|
231
237
|
https! URI::HTTPS === location if location.scheme
|
232
238
|
|
@@ -252,10 +258,13 @@ module ActionDispatch
|
|
252
258
|
"REQUEST_URI" => path,
|
253
259
|
"HTTP_HOST" => host,
|
254
260
|
"REMOTE_ADDR" => remote_addr,
|
255
|
-
"CONTENT_TYPE" => request_encoder.content_type,
|
256
261
|
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
257
262
|
}
|
258
263
|
|
264
|
+
if request_encoder.content_type
|
265
|
+
request_env["CONTENT_TYPE"] = request_encoder.content_type
|
266
|
+
end
|
267
|
+
|
259
268
|
wrapped_headers = Http::Headers.from_hash({})
|
260
269
|
wrapped_headers.merge!(headers) if headers
|
261
270
|
|
@@ -363,13 +372,11 @@ module ActionDispatch
|
|
363
372
|
reset_html_document = "@html_document = nil"
|
364
373
|
end
|
365
374
|
|
366
|
-
definition = RUBY_VERSION >= "2.7" ? "..." : "*args"
|
367
|
-
|
368
375
|
module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
369
|
-
def #{method}(
|
376
|
+
def #{method}(...)
|
370
377
|
#{reset_html_document}
|
371
378
|
|
372
|
-
result = integration_session.#{method}(
|
379
|
+
result = integration_session.#{method}(...)
|
373
380
|
copy_session_variables!
|
374
381
|
result
|
375
382
|
end
|
@@ -404,7 +411,7 @@ module ActionDispatch
|
|
404
411
|
|
405
412
|
# Copy the instance variables from the current session instance into the
|
406
413
|
# test instance.
|
407
|
-
def copy_session_variables!
|
414
|
+
def copy_session_variables! # :nodoc:
|
408
415
|
@controller = @integration_session.controller
|
409
416
|
@response = @integration_session.response
|
410
417
|
@request = @integration_session.request
|
@@ -433,7 +440,7 @@ module ActionDispatch
|
|
433
440
|
super
|
434
441
|
end
|
435
442
|
end
|
436
|
-
ruby2_keywords(:method_missing)
|
443
|
+
ruby2_keywords(:method_missing)
|
437
444
|
end
|
438
445
|
end
|
439
446
|
|
@@ -442,8 +449,9 @@ module ActionDispatch
|
|
442
449
|
# more completely than either unit or functional tests do, exercising the
|
443
450
|
# entire stack, from the dispatcher to the database.
|
444
451
|
#
|
445
|
-
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your
|
446
|
-
# using the get/
|
452
|
+
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your
|
453
|
+
# tests using the Integration::RequestHelpers#get and/or
|
454
|
+
# Integration::RequestHelpers#post methods:
|
447
455
|
#
|
448
456
|
# require "test_helper"
|
449
457
|
#
|
@@ -614,7 +622,7 @@ module ActionDispatch
|
|
614
622
|
# the request format to JSON unless overridden), sets the content type to
|
615
623
|
# "application/json" and encodes the parameters as JSON.
|
616
624
|
#
|
617
|
-
# Calling
|
625
|
+
# Calling TestResponse#parsed_body on the response parses the response body based on the
|
618
626
|
# last response MIME type.
|
619
627
|
#
|
620
628
|
# Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
|
@@ -626,9 +634,9 @@ module ActionDispatch
|
|
626
634
|
#
|
627
635
|
# Where +param_encoder+ defines how the params should be encoded and
|
628
636
|
# +response_parser+ defines how the response body should be parsed through
|
629
|
-
#
|
637
|
+
# TestResponse#parsed_body.
|
630
638
|
#
|
631
|
-
# Consult the Rails Testing Guide for more.
|
639
|
+
# Consult the {Rails Testing Guide}[https://guides.rubyonrails.org/testing.html] for more.
|
632
640
|
|
633
641
|
class IntegrationTest < ActiveSupport::TestCase
|
634
642
|
include TestProcess::FixtureFile
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "nokogiri"
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
6
|
class RequestEncoder # :nodoc:
|
5
7
|
class IdentityEncoder
|
@@ -50,6 +52,7 @@ module ActionDispatch
|
|
50
52
|
@encoders[mime_name] = new(mime_name, param_encoder, response_parser)
|
51
53
|
end
|
52
54
|
|
53
|
-
register_encoder :
|
55
|
+
register_encoder :html, response_parser: -> body { Rails::Dom::Testing.html_document.parse(body) }
|
56
|
+
register_encoder :json, response_parser: -> body { JSON.parse(body, object_class: ActiveSupport::HashWithIndifferentAccess) }
|
54
57
|
end
|
55
58
|
end
|
@@ -8,47 +8,22 @@ module ActionDispatch
|
|
8
8
|
module FixtureFile
|
9
9
|
# Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.file_fixture_path, path), type)</tt>:
|
10
10
|
#
|
11
|
-
# post :change_avatar, params: { avatar:
|
11
|
+
# post :change_avatar, params: { avatar: file_fixture_upload('david.png', 'image/png') }
|
12
12
|
#
|
13
13
|
# Default fixture files location is <tt>test/fixtures/files</tt>.
|
14
14
|
#
|
15
15
|
# To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
|
16
16
|
# This will not affect other platforms:
|
17
17
|
#
|
18
|
-
# post :change_avatar, params: { avatar:
|
19
|
-
def
|
20
|
-
if self.class.
|
21
|
-
!File.exist?(path)
|
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)
|
18
|
+
# post :change_avatar, params: { avatar: file_fixture_upload('david.png', 'image/png', :binary) }
|
19
|
+
def file_fixture_upload(path, mime_type = nil, binary = false)
|
20
|
+
if self.class.file_fixture_path && !File.exist?(path)
|
47
21
|
path = file_fixture(path)
|
48
22
|
end
|
49
23
|
|
50
24
|
Rack::Test::UploadedFile.new(path, mime_type, binary)
|
51
25
|
end
|
26
|
+
alias_method :fixture_file_upload, :file_fixture_upload
|
52
27
|
end
|
53
28
|
|
54
29
|
include FixtureFile
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "action_dispatch/testing/request_encoder"
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
|
-
# Integration test methods such as
|
7
|
-
# and
|
6
|
+
# Integration test methods such as Integration::RequestHelpers#get
|
7
|
+
# and Integration::RequestHelpers#post return objects of class
|
8
8
|
# TestResponse, which represent the HTTP response results of the requested
|
9
9
|
# controller actions.
|
10
10
|
#
|
@@ -14,6 +14,38 @@ module ActionDispatch
|
|
14
14
|
new response.status, response.headers, response.body
|
15
15
|
end
|
16
16
|
|
17
|
+
# Returns a parsed body depending on the response MIME type. When a parser
|
18
|
+
# corresponding to the MIME type is not found, it returns the raw body.
|
19
|
+
#
|
20
|
+
# ==== Examples
|
21
|
+
# get "/posts"
|
22
|
+
# response.content_type # => "text/html; charset=utf-8"
|
23
|
+
# response.parsed_body.class # => Nokogiri::HTML5::Document
|
24
|
+
# response.parsed_body.to_html # => "<!DOCTYPE html>\n<html>\n..."
|
25
|
+
#
|
26
|
+
# assert_pattern { response.parsed_body.at("main") => { content: "Hello, world" } }
|
27
|
+
#
|
28
|
+
# response.parsed_body.at("main") => {name:, content:}
|
29
|
+
# assert_equal "main", name
|
30
|
+
# assert_equal "Some main content", content
|
31
|
+
#
|
32
|
+
# get "/posts.json"
|
33
|
+
# response.content_type # => "application/json; charset=utf-8"
|
34
|
+
# response.parsed_body.class # => Array
|
35
|
+
# response.parsed_body # => [{"id"=>42, "title"=>"Title"},...
|
36
|
+
#
|
37
|
+
# assert_pattern { response.parsed_body => [{ id: 42 }] }
|
38
|
+
#
|
39
|
+
# get "/posts/42.json"
|
40
|
+
# response.content_type # => "application/json; charset=utf-8"
|
41
|
+
# response.parsed_body.class # => ActiveSupport::HashWithIndifferentAccess
|
42
|
+
# response.parsed_body # => {"id"=>42, "title"=>"Title"}
|
43
|
+
#
|
44
|
+
# assert_pattern { response.parsed_body => [{ title: /title/i }] }
|
45
|
+
#
|
46
|
+
# response.parsed_body => {id:, title:}
|
47
|
+
# assert_equal 42, id
|
48
|
+
# assert_equal "Title", title
|
17
49
|
def parsed_body
|
18
50
|
@parsed_body ||= response_parser.call(body)
|
19
51
|
end
|