omg-actionpack 8.0.0.alpha1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +129 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller/asset_paths.rb +14 -0
- data/lib/abstract_controller/base.rb +299 -0
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +265 -0
- data/lib/abstract_controller/collector.rb +44 -0
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +243 -0
- data/lib/abstract_controller/logger.rb +16 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
- data/lib/abstract_controller/rendering.rb +126 -0
- data/lib/abstract_controller/translation.rb +42 -0
- data/lib/abstract_controller/url_for.rb +37 -0
- data/lib/abstract_controller.rb +36 -0
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +155 -0
- data/lib/action_controller/base.rb +332 -0
- data/lib/action_controller/caching.rb +49 -0
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +96 -0
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +341 -0
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +20 -0
- data/lib/action_controller/metal/data_streaming.rb +154 -0
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
- data/lib/action_controller/metal/exceptions.rb +106 -0
- data/lib/action_controller/metal/flash.rb +67 -0
- data/lib/action_controller/metal/head.rb +67 -0
- data/lib/action_controller/metal/helpers.rb +129 -0
- data/lib/action_controller/metal/http_authentication.rb +565 -0
- data/lib/action_controller/metal/implicit_render.rb +67 -0
- data/lib/action_controller/metal/instrumentation.rb +120 -0
- data/lib/action_controller/metal/live.rb +398 -0
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +337 -0
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +312 -0
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +251 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +260 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
- data/lib/action_controller/metal/rescue.rb +33 -0
- data/lib/action_controller/metal/streaming.rb +183 -0
- data/lib/action_controller/metal/strong_parameters.rb +1546 -0
- data/lib/action_controller/metal/testing.rb +25 -0
- data/lib/action_controller/metal/url_for.rb +65 -0
- data/lib/action_controller/metal.rb +339 -0
- data/lib/action_controller/railtie.rb +149 -0
- data/lib/action_controller/railties/helpers.rb +26 -0
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +691 -0
- data/lib/action_controller.rb +80 -0
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +249 -0
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +365 -0
- data/lib/action_dispatch/http/filter_parameters.rb +80 -0
- data/lib/action_dispatch/http/filter_redirect.rb +50 -0
- data/lib/action_dispatch/http/headers.rb +134 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
- data/lib/action_dispatch/http/mime_type.rb +389 -0
- data/lib/action_dispatch/http/mime_types.rb +54 -0
- data/lib/action_dispatch/http/parameters.rb +119 -0
- data/lib/action_dispatch/http/permissions_policy.rb +189 -0
- data/lib/action_dispatch/http/rack_cache.rb +67 -0
- data/lib/action_dispatch/http/request.rb +498 -0
- data/lib/action_dispatch/http/response.rb +556 -0
- data/lib/action_dispatch/http/upload.rb +107 -0
- data/lib/action_dispatch/http/url.rb +344 -0
- data/lib/action_dispatch/journey/formatter.rb +226 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
- data/lib/action_dispatch/journey/nodes/node.rb +208 -0
- data/lib/action_dispatch/journey/parser.rb +103 -0
- data/lib/action_dispatch/journey/path/pattern.rb +209 -0
- data/lib/action_dispatch/journey/route.rb +189 -0
- data/lib/action_dispatch/journey/router/utils.rb +105 -0
- data/lib/action_dispatch/journey/router.rb +151 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +70 -0
- data/lib/action_dispatch/journey/visitors.rb +267 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +38 -0
- data/lib/action_dispatch/middleware/cookies.rb +719 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +318 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
- data/lib/action_dispatch/middleware/reloader.rb +16 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
- data/lib/action_dispatch/middleware/request_id.rb +50 -0
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
- data/lib/action_dispatch/middleware/ssl.rb +180 -0
- data/lib/action_dispatch/middleware/stack.rb +194 -0
- data/lib/action_dispatch/middleware/static.rb +192 -0
- 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 +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
- data/lib/action_dispatch/railtie.rb +77 -0
- data/lib/action_dispatch/request/session.rb +283 -0
- data/lib/action_dispatch/request/utils.rb +109 -0
- data/lib/action_dispatch/routing/endpoint.rb +19 -0
- data/lib/action_dispatch/routing/inspector.rb +323 -0
- data/lib/action_dispatch/routing/mapper.rb +2372 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
- data/lib/action_dispatch/routing/redirection.rb +218 -0
- data/lib/action_dispatch/routing/route_set.rb +958 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
- data/lib/action_dispatch/routing/url_for.rb +244 -0
- data/lib/action_dispatch/routing.rb +262 -0
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +75 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +114 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
- data/lib/action_dispatch/testing/assertions.rb +25 -0
- data/lib/action_dispatch/testing/integration.rb +694 -0
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +57 -0
- data/lib/action_dispatch/testing/test_request.rb +73 -0
- data/lib/action_dispatch/testing/test_response.rb +58 -0
- data/lib/action_dispatch.rb +147 -0
- data/lib/action_pack/gem_version.rb +19 -0
- data/lib/action_pack/version.rb +12 -0
- data/lib/action_pack.rb +27 -0
- metadata +375 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
module ActionDispatch
|
|
6
|
+
module Assertions
|
|
7
|
+
# A small suite of assertions that test responses from Rails applications.
|
|
8
|
+
module ResponseAssertions
|
|
9
|
+
RESPONSE_PREDICATES = { # :nodoc:
|
|
10
|
+
success: :successful?,
|
|
11
|
+
missing: :not_found?,
|
|
12
|
+
redirect: :redirection?,
|
|
13
|
+
error: :server_error?,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Asserts that the response is one of the following types:
|
|
17
|
+
#
|
|
18
|
+
# * `:success` - Status code was in the 200-299 range
|
|
19
|
+
# * `:redirect` - Status code was in the 300-399 range
|
|
20
|
+
# * `:missing` - Status code was 404
|
|
21
|
+
# * `:error` - Status code was in the 500-599 range
|
|
22
|
+
#
|
|
23
|
+
#
|
|
24
|
+
# You can also pass an explicit status number like `assert_response(501)` or its
|
|
25
|
+
# symbolic equivalent `assert_response(:not_implemented)`. See
|
|
26
|
+
# `Rack::Utils::SYMBOL_TO_STATUS_CODE` for a full list.
|
|
27
|
+
#
|
|
28
|
+
# # Asserts that the response was a redirection
|
|
29
|
+
# assert_response :redirect
|
|
30
|
+
#
|
|
31
|
+
# # Asserts that the response code was status code 401 (unauthorized)
|
|
32
|
+
# assert_response 401
|
|
33
|
+
def assert_response(type, message = nil)
|
|
34
|
+
message ||= generate_response_message(type)
|
|
35
|
+
|
|
36
|
+
if RESPONSE_PREDICATES.key?(type)
|
|
37
|
+
assert @response.public_send(RESPONSE_PREDICATES[type]), message
|
|
38
|
+
else
|
|
39
|
+
assert_equal AssertionResponse.new(type).code, @response.response_code, message
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Asserts that the response is a redirect to a URL matching the given options.
|
|
44
|
+
#
|
|
45
|
+
# # Asserts that the redirection was to the "index" action on the WeblogController
|
|
46
|
+
# assert_redirected_to controller: "weblog", action: "index"
|
|
47
|
+
#
|
|
48
|
+
# # Asserts that the redirection was to the named route login_url
|
|
49
|
+
# assert_redirected_to login_url
|
|
50
|
+
#
|
|
51
|
+
# # Asserts that the redirection was to the URL for @customer
|
|
52
|
+
# assert_redirected_to @customer
|
|
53
|
+
#
|
|
54
|
+
# # Asserts that the redirection matches the regular expression
|
|
55
|
+
# assert_redirected_to %r(\Ahttp://example.org)
|
|
56
|
+
#
|
|
57
|
+
# # Asserts that the redirection has the HTTP status code 301 (Moved
|
|
58
|
+
# # Permanently).
|
|
59
|
+
# assert_redirected_to "/some/path", status: :moved_permanently
|
|
60
|
+
def assert_redirected_to(url_options = {}, options = {}, message = nil)
|
|
61
|
+
options, message = {}, options unless options.is_a?(Hash)
|
|
62
|
+
|
|
63
|
+
status = options[:status] || :redirect
|
|
64
|
+
assert_response(status, message)
|
|
65
|
+
return true if url_options === @response.location
|
|
66
|
+
|
|
67
|
+
redirect_is = normalize_argument_to_redirection(@response.location)
|
|
68
|
+
redirect_expected = normalize_argument_to_redirection(url_options)
|
|
69
|
+
|
|
70
|
+
message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
|
|
71
|
+
assert_operator redirect_expected, :===, redirect_is, message
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
# Proxy to to_param if the object will respond to it.
|
|
76
|
+
def parameterize(value)
|
|
77
|
+
value.respond_to?(:to_param) ? value.to_param : value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def normalize_argument_to_redirection(fragment)
|
|
81
|
+
if Regexp === fragment
|
|
82
|
+
fragment
|
|
83
|
+
else
|
|
84
|
+
handle = @controller || ActionController::Redirecting
|
|
85
|
+
handle._compute_redirect_to_location(@request, fragment)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def generate_response_message(expected, actual = @response.response_code)
|
|
90
|
+
(+"Expected response to be a <#{code_with_name(expected)}>,"\
|
|
91
|
+
" but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def response_body_if_short
|
|
95
|
+
return "" if @response.body.size > 500
|
|
96
|
+
"\nResponse body: #{@response.body}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def location_if_redirected
|
|
100
|
+
return "" unless @response.redirection? && @response.location.present?
|
|
101
|
+
location = normalize_argument_to_redirection(@response.location)
|
|
102
|
+
" redirect to <#{location}>"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def code_with_name(code_or_name)
|
|
106
|
+
if RESPONSE_PREDICATES.value?("#{code_or_name}?".to_sym)
|
|
107
|
+
code_or_name = RESPONSE_PREDICATES.invert["#{code_or_name}?".to_sym]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
AssertionResponse.new(code_or_name).code_and_name
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
require "uri"
|
|
6
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
7
|
+
require "active_support/core_ext/string/access"
|
|
8
|
+
require "action_controller/metal/exceptions"
|
|
9
|
+
|
|
10
|
+
module ActionDispatch
|
|
11
|
+
module Assertions
|
|
12
|
+
# Suite of assertions to test routes generated by Rails and the handling of
|
|
13
|
+
# requests made to them.
|
|
14
|
+
module RoutingAssertions
|
|
15
|
+
extend ActiveSupport::Concern
|
|
16
|
+
|
|
17
|
+
module WithIntegrationRouting # :nodoc:
|
|
18
|
+
extend ActiveSupport::Concern
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
def with_routing(&block)
|
|
22
|
+
old_routes = nil
|
|
23
|
+
old_integration_session = nil
|
|
24
|
+
|
|
25
|
+
setup do
|
|
26
|
+
old_routes = app.routes
|
|
27
|
+
old_integration_session = integration_session
|
|
28
|
+
create_routes(&block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
teardown do
|
|
32
|
+
reset_routes(old_routes, old_integration_session)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def with_routing(&block)
|
|
38
|
+
old_routes = app.routes
|
|
39
|
+
old_integration_session = integration_session
|
|
40
|
+
create_routes(&block)
|
|
41
|
+
ensure
|
|
42
|
+
reset_routes(old_routes, old_integration_session)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
def create_routes
|
|
47
|
+
app = self.app
|
|
48
|
+
routes = ActionDispatch::Routing::RouteSet.new
|
|
49
|
+
rack_app = app.config.middleware.build(routes)
|
|
50
|
+
https = integration_session.https?
|
|
51
|
+
host = integration_session.host
|
|
52
|
+
|
|
53
|
+
app.instance_variable_set(:@routes, routes)
|
|
54
|
+
app.instance_variable_set(:@app, rack_app)
|
|
55
|
+
@integration_session = Class.new(ActionDispatch::Integration::Session) do
|
|
56
|
+
include app.routes.url_helpers
|
|
57
|
+
include app.routes.mounted_helpers
|
|
58
|
+
end.new(app)
|
|
59
|
+
@integration_session.https! https
|
|
60
|
+
@integration_session.host! host
|
|
61
|
+
@routes = routes
|
|
62
|
+
|
|
63
|
+
yield routes
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def reset_routes(old_routes, old_integration_session)
|
|
67
|
+
old_rack_app = app.config.middleware.build(old_routes)
|
|
68
|
+
|
|
69
|
+
app.instance_variable_set(:@routes, old_routes)
|
|
70
|
+
app.instance_variable_set(:@app, old_rack_app)
|
|
71
|
+
@integration_session = old_integration_session
|
|
72
|
+
@routes = old_routes
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
module ClassMethods
|
|
77
|
+
# A helper to make it easier to test different route configurations. This method
|
|
78
|
+
# temporarily replaces @routes with a new RouteSet instance before each test.
|
|
79
|
+
#
|
|
80
|
+
# The new instance is yielded to the passed block. Typically the block will
|
|
81
|
+
# create some routes using `set.draw { match ... }`:
|
|
82
|
+
#
|
|
83
|
+
# with_routing do |set|
|
|
84
|
+
# set.draw do
|
|
85
|
+
# resources :users
|
|
86
|
+
# end
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
def with_routing(&block)
|
|
90
|
+
old_routes, old_controller = nil
|
|
91
|
+
|
|
92
|
+
setup do
|
|
93
|
+
old_routes, old_controller = @routes, @controller
|
|
94
|
+
create_routes(&block)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
teardown do
|
|
98
|
+
reset_routes(old_routes, old_controller)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def setup # :nodoc:
|
|
104
|
+
@routes ||= nil
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# A helper to make it easier to test different route configurations. This method
|
|
109
|
+
# temporarily replaces @routes with a new RouteSet instance.
|
|
110
|
+
#
|
|
111
|
+
# The new instance is yielded to the passed block. Typically the block will
|
|
112
|
+
# create some routes using `set.draw { match ... }`:
|
|
113
|
+
#
|
|
114
|
+
# with_routing do |set|
|
|
115
|
+
# set.draw do
|
|
116
|
+
# resources :users
|
|
117
|
+
# end
|
|
118
|
+
# assert_equal "/users", users_path
|
|
119
|
+
# end
|
|
120
|
+
#
|
|
121
|
+
def with_routing(&block)
|
|
122
|
+
old_routes, old_controller = @routes, @controller
|
|
123
|
+
create_routes(&block)
|
|
124
|
+
ensure
|
|
125
|
+
reset_routes(old_routes, old_controller)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Asserts that the routing of the given `path` was handled correctly and that
|
|
129
|
+
# the parsed options (given in the `expected_options` hash) match `path`.
|
|
130
|
+
# Basically, it asserts that Rails recognizes the route given by
|
|
131
|
+
# `expected_options`.
|
|
132
|
+
#
|
|
133
|
+
# Pass a hash in the second argument (`path`) to specify the request method.
|
|
134
|
+
# This is useful for routes requiring a specific HTTP method. The hash should
|
|
135
|
+
# contain a `:path` with the incoming request path and a `:method` containing
|
|
136
|
+
# the required HTTP verb.
|
|
137
|
+
#
|
|
138
|
+
# # Asserts that POSTing to /items will call the create action on ItemsController
|
|
139
|
+
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
|
|
140
|
+
#
|
|
141
|
+
# You can also pass in `extras` with a hash containing URL parameters that would
|
|
142
|
+
# normally be in the query string. This can be used to assert that values in the
|
|
143
|
+
# query string will end up in the params hash correctly. To test query strings
|
|
144
|
+
# you must use the extras argument because appending the query string on the
|
|
145
|
+
# path directly will not work. For example:
|
|
146
|
+
#
|
|
147
|
+
# # Asserts that a path of '/items/list/1?view=print' returns the correct options
|
|
148
|
+
# assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
|
|
149
|
+
#
|
|
150
|
+
# The `message` parameter allows you to pass in an error message that is
|
|
151
|
+
# displayed upon failure.
|
|
152
|
+
#
|
|
153
|
+
# # Check the default route (i.e., the index action)
|
|
154
|
+
# assert_recognizes({controller: 'items', action: 'index'}, 'items')
|
|
155
|
+
#
|
|
156
|
+
# # Test a specific action
|
|
157
|
+
# assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
|
|
158
|
+
#
|
|
159
|
+
# # Test an action with a parameter
|
|
160
|
+
# assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
|
|
161
|
+
#
|
|
162
|
+
# # Test a custom route
|
|
163
|
+
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
|
|
164
|
+
def assert_recognizes(expected_options, path, extras = {}, msg = nil)
|
|
165
|
+
if path.is_a?(Hash) && path[:method].to_s == "all"
|
|
166
|
+
[:get, :post, :put, :delete].each do |method|
|
|
167
|
+
assert_recognizes(expected_options, path.merge(method: method), extras, msg)
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
request = recognized_request_for(path, extras, msg)
|
|
171
|
+
|
|
172
|
+
expected_options = expected_options.clone
|
|
173
|
+
|
|
174
|
+
expected_options.stringify_keys!
|
|
175
|
+
|
|
176
|
+
msg = message(msg, "") {
|
|
177
|
+
sprintf("The recognized options <%s> did not match <%s>, difference:",
|
|
178
|
+
request.path_parameters, expected_options)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
assert_equal(expected_options, request.path_parameters, msg)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Asserts that the provided options can be used to generate the provided path.
|
|
186
|
+
# This is the inverse of `assert_recognizes`. The `extras` parameter is used to
|
|
187
|
+
# tell the request the names and values of additional request parameters that
|
|
188
|
+
# would be in a query string. The `message` parameter allows you to specify a
|
|
189
|
+
# custom error message for assertion failures.
|
|
190
|
+
#
|
|
191
|
+
# The `defaults` parameter is unused.
|
|
192
|
+
#
|
|
193
|
+
# # Asserts that the default action is generated for a route with no action
|
|
194
|
+
# assert_generates "/items", controller: "items", action: "index"
|
|
195
|
+
#
|
|
196
|
+
# # Tests that the list action is properly routed
|
|
197
|
+
# assert_generates "/items/list", controller: "items", action: "list"
|
|
198
|
+
#
|
|
199
|
+
# # Tests the generation of a route with a parameter
|
|
200
|
+
# assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
|
|
201
|
+
#
|
|
202
|
+
# # Asserts that the generated route gives us our custom route
|
|
203
|
+
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
|
|
204
|
+
def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
|
|
205
|
+
if expected_path.include?("://")
|
|
206
|
+
fail_on(URI::InvalidURIError, message) do
|
|
207
|
+
uri = URI.parse(expected_path)
|
|
208
|
+
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
|
209
|
+
end
|
|
210
|
+
else
|
|
211
|
+
expected_path = "/#{expected_path}" unless expected_path.start_with?("/")
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
options = options.clone
|
|
215
|
+
generated_path, query_string_keys = @routes.generate_extras(options, defaults)
|
|
216
|
+
found_extras = options.reject { |k, _| ! query_string_keys.include? k }
|
|
217
|
+
|
|
218
|
+
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
|
|
219
|
+
assert_equal(extras, found_extras, msg)
|
|
220
|
+
|
|
221
|
+
msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
|
|
222
|
+
expected_path)
|
|
223
|
+
assert_equal(expected_path, generated_path, msg)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Asserts that path and options match both ways; in other words, it verifies
|
|
227
|
+
# that `path` generates `options` and then that `options` generates `path`. This
|
|
228
|
+
# essentially combines `assert_recognizes` and `assert_generates` into one step.
|
|
229
|
+
#
|
|
230
|
+
# The `extras` hash allows you to specify options that would normally be
|
|
231
|
+
# provided as a query string to the action. The `message` parameter allows you
|
|
232
|
+
# to specify a custom error message to display upon failure.
|
|
233
|
+
#
|
|
234
|
+
# # Asserts a basic route: a controller with the default action (index)
|
|
235
|
+
# assert_routing '/home', controller: 'home', action: 'index'
|
|
236
|
+
#
|
|
237
|
+
# # Test a route generated with a specific controller, action, and parameter (id)
|
|
238
|
+
# assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
|
|
239
|
+
#
|
|
240
|
+
# # Asserts a basic route (controller + default action), with an error message if it fails
|
|
241
|
+
# assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
|
|
242
|
+
#
|
|
243
|
+
# # Tests a route, providing a defaults hash
|
|
244
|
+
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
|
|
245
|
+
#
|
|
246
|
+
# # Tests a route with an HTTP method
|
|
247
|
+
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
|
|
248
|
+
def assert_routing(path, options, defaults = {}, extras = {}, message = nil)
|
|
249
|
+
assert_recognizes(options, path, extras, message)
|
|
250
|
+
|
|
251
|
+
controller, default_controller = options[:controller], defaults[:controller]
|
|
252
|
+
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
|
|
253
|
+
options[:controller] = "/#{controller}"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
generate_options = options.dup.delete_if { |k, _| defaults.key?(k) }
|
|
257
|
+
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# ROUTES TODO: These assertions should really work in an integration context
|
|
261
|
+
def method_missing(selector, ...)
|
|
262
|
+
if @controller && @routes&.named_routes&.route_defined?(selector)
|
|
263
|
+
@controller.public_send(selector, ...)
|
|
264
|
+
else
|
|
265
|
+
super
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
private
|
|
270
|
+
def create_routes
|
|
271
|
+
@routes = ActionDispatch::Routing::RouteSet.new
|
|
272
|
+
if @controller
|
|
273
|
+
@controller = @controller.clone
|
|
274
|
+
_routes = @routes
|
|
275
|
+
|
|
276
|
+
@controller.singleton_class.include(_routes.url_helpers)
|
|
277
|
+
|
|
278
|
+
if @controller.respond_to? :view_context_class
|
|
279
|
+
view_context_class = Class.new(@controller.view_context_class) do
|
|
280
|
+
include _routes.url_helpers
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
custom_view_context = Module.new {
|
|
284
|
+
define_method(:view_context_class) do
|
|
285
|
+
view_context_class
|
|
286
|
+
end
|
|
287
|
+
}
|
|
288
|
+
@controller.extend(custom_view_context)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
yield @routes
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def reset_routes(old_routes, old_controller)
|
|
295
|
+
@routes = old_routes
|
|
296
|
+
if @controller
|
|
297
|
+
@controller = old_controller
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Recognizes the route for a given path.
|
|
302
|
+
def recognized_request_for(path, extras = {}, msg)
|
|
303
|
+
if path.is_a?(Hash)
|
|
304
|
+
method = path[:method]
|
|
305
|
+
path = path[:path]
|
|
306
|
+
else
|
|
307
|
+
method = :get
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
controller = @controller if defined?(@controller)
|
|
311
|
+
request = ActionController::TestRequest.create controller&.class
|
|
312
|
+
|
|
313
|
+
if path.include?("://")
|
|
314
|
+
fail_on(URI::InvalidURIError, msg) do
|
|
315
|
+
uri = URI.parse(path)
|
|
316
|
+
request.env["rack.url_scheme"] = uri.scheme || "http"
|
|
317
|
+
request.host = uri.host if uri.host
|
|
318
|
+
request.port = uri.port if uri.port
|
|
319
|
+
request.path = uri.path.to_s.empty? ? "/" : uri.path
|
|
320
|
+
end
|
|
321
|
+
else
|
|
322
|
+
path = "/#{path}" unless path.start_with?("/")
|
|
323
|
+
request.path = path
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
request.request_method = method if method
|
|
327
|
+
|
|
328
|
+
params = fail_on(ActionController::RoutingError, msg) do
|
|
329
|
+
@routes.recognize_path(path, method: method, extras: extras)
|
|
330
|
+
end
|
|
331
|
+
request.path_parameters = params.with_indifferent_access
|
|
332
|
+
|
|
333
|
+
request
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def fail_on(exception_class, message)
|
|
337
|
+
yield
|
|
338
|
+
rescue exception_class => e
|
|
339
|
+
raise Minitest::Assertion, message || e.message
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
require "rails-dom-testing"
|
|
6
|
+
require "action_dispatch/testing/assertions/response"
|
|
7
|
+
require "action_dispatch/testing/assertions/routing"
|
|
8
|
+
|
|
9
|
+
module ActionDispatch
|
|
10
|
+
module Assertions
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
include ResponseAssertions
|
|
14
|
+
include RoutingAssertions
|
|
15
|
+
include Rails::Dom::Testing::Assertions
|
|
16
|
+
|
|
17
|
+
def html_document
|
|
18
|
+
@html_document ||= if @response.media_type&.end_with?("xml")
|
|
19
|
+
Nokogiri::XML::Document.parse(@response.body)
|
|
20
|
+
else
|
|
21
|
+
Rails::Dom::Testing.html_document.parse(@response.body)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|