actionpack 7.0.4.3 → 7.1.3.4
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 +380 -284
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +9 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- 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 +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +17 -8
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +25 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +158 -101
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +15 -5
- data/lib/action_controller.rb +8 -1
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +8 -10
- data/lib/action_dispatch/http/content_security_policy.rb +14 -9
- data/lib/action_dispatch/http/filter_parameters.rb +14 -28
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +35 -12
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +63 -30
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- 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 +85 -102
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- 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 +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +25 -18
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- 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 +59 -41
- data/lib/action_dispatch/railtie.rb +14 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +58 -24
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +52 -22
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +20 -19
- data/lib/action_dispatch/system_testing/driver.rb +14 -22
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- 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 +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +37 -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 +65 -29
@@ -18,10 +18,19 @@ module ActionDispatch
|
|
18
18
|
def redirect?; true; end
|
19
19
|
|
20
20
|
def call(env)
|
21
|
-
|
21
|
+
ActiveSupport::Notifications.instrument("redirect.action_dispatch") do |payload|
|
22
|
+
request = Request.new(env)
|
23
|
+
response = build_response(request)
|
24
|
+
|
25
|
+
payload[:status] = @status
|
26
|
+
payload[:location] = response.headers["Location"]
|
27
|
+
payload[:request] = request
|
28
|
+
|
29
|
+
response.to_a
|
30
|
+
end
|
22
31
|
end
|
23
32
|
|
24
|
-
def
|
33
|
+
def build_response(req)
|
25
34
|
uri = URI.parse(path(req.path_parameters, req))
|
26
35
|
|
27
36
|
unless uri.host
|
@@ -38,15 +47,15 @@ module ActionDispatch
|
|
38
47
|
|
39
48
|
req.commit_flash
|
40
49
|
|
41
|
-
body =
|
50
|
+
body = ""
|
42
51
|
|
43
52
|
headers = {
|
44
53
|
"Location" => uri.to_s,
|
45
|
-
"Content-Type" => "text/html",
|
54
|
+
"Content-Type" => "text/html; charset=#{ActionDispatch::Response.default_charset}",
|
46
55
|
"Content-Length" => body.length.to_s
|
47
56
|
}
|
48
57
|
|
49
|
-
|
58
|
+
ActionDispatch::Response.new(status, headers, body)
|
50
59
|
end
|
51
60
|
|
52
61
|
def path(params, request)
|
@@ -59,7 +68,7 @@ module ActionDispatch
|
|
59
68
|
|
60
69
|
private
|
61
70
|
def relative_path?(path)
|
62
|
-
path && !path.empty? && path
|
71
|
+
path && !path.empty? && !path.start_with?("/")
|
63
72
|
end
|
64
73
|
|
65
74
|
def escape(params)
|
@@ -34,20 +34,20 @@ module ActionDispatch
|
|
34
34
|
if @raise_on_name_error
|
35
35
|
raise
|
36
36
|
else
|
37
|
-
[404, {
|
37
|
+
[404, { Constants::X_CASCADE => "pass" }, []]
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
private
|
42
|
+
def controller(req)
|
43
|
+
req.controller_class
|
44
|
+
rescue NameError => e
|
45
|
+
raise ActionController::RoutingError, e.message, e.backtrace
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
def dispatch(controller, action, req, res)
|
49
|
+
controller.dispatch(action, req, res)
|
50
|
+
end
|
51
51
|
end
|
52
52
|
|
53
53
|
class StaticDispatcher < Dispatcher
|
@@ -282,7 +282,7 @@ module ActionDispatch
|
|
282
282
|
|
283
283
|
if args.size < path_params_size
|
284
284
|
path_params -= controller_options.keys
|
285
|
-
path_params -= result.keys
|
285
|
+
path_params -= (result[:path_params] || {}).merge(result).keys
|
286
286
|
else
|
287
287
|
path_params = path_params.dup
|
288
288
|
end
|
@@ -375,6 +375,7 @@ module ActionDispatch
|
|
375
375
|
@disable_clear_and_finalize = false
|
376
376
|
@finalized = false
|
377
377
|
@env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
|
378
|
+
@default_env = nil
|
378
379
|
|
379
380
|
@set = Journey::Routes.new
|
380
381
|
@router = Journey::Router.new @set
|
@@ -405,6 +406,25 @@ module ActionDispatch
|
|
405
406
|
end
|
406
407
|
private :make_request
|
407
408
|
|
409
|
+
def default_env
|
410
|
+
if default_url_options != @default_env&.[]("action_dispatch.routes.default_url_options")
|
411
|
+
url_options = default_url_options.dup.freeze
|
412
|
+
uri = URI(ActionDispatch::Http::URL.full_url_for(host: "example.org", **url_options))
|
413
|
+
|
414
|
+
@default_env = {
|
415
|
+
"action_dispatch.routes" => self,
|
416
|
+
"action_dispatch.routes.default_url_options" => url_options,
|
417
|
+
"HTTPS" => uri.scheme == "https" ? "on" : "off",
|
418
|
+
"rack.url_scheme" => uri.scheme,
|
419
|
+
"HTTP_HOST" => uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}",
|
420
|
+
"SCRIPT_NAME" => uri.path.chomp("/"),
|
421
|
+
"rack.input" => "",
|
422
|
+
}.freeze
|
423
|
+
end
|
424
|
+
|
425
|
+
@default_env
|
426
|
+
end
|
427
|
+
|
408
428
|
def draw(&block)
|
409
429
|
clear! unless @disable_clear_and_finalize
|
410
430
|
eval_block(block)
|
@@ -574,6 +594,20 @@ module ActionDispatch
|
|
574
594
|
end
|
575
595
|
|
576
596
|
private :_generate_paths_by_default
|
597
|
+
|
598
|
+
# If the module is included more than once (for example, in a subclass
|
599
|
+
# of an ancestor that includes the module), ensure that the `_routes`
|
600
|
+
# singleton and instance methods return the desired route set by
|
601
|
+
# including a new copy of the module (recursively if necessary). Note
|
602
|
+
# that this method is called for each inclusion, whereas the above
|
603
|
+
# `included` block is run only for the initial inclusion of each copy.
|
604
|
+
def self.included(base)
|
605
|
+
super
|
606
|
+
if base.respond_to?(:_routes) && !base._routes.equal?(@_proxy._routes)
|
607
|
+
@dup_for_reinclude ||= self.dup
|
608
|
+
base.include @dup_for_reinclude
|
609
|
+
end
|
610
|
+
end
|
577
611
|
end
|
578
612
|
end
|
579
613
|
|
@@ -596,16 +630,16 @@ module ActionDispatch
|
|
596
630
|
named_routes[name] = route if name
|
597
631
|
|
598
632
|
if route.segment_keys.include?(:controller)
|
599
|
-
|
633
|
+
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
600
634
|
Using a dynamic :controller segment in a route is deprecated and
|
601
|
-
will be removed in Rails 7.
|
635
|
+
will be removed in Rails 7.2.
|
602
636
|
MSG
|
603
637
|
end
|
604
638
|
|
605
639
|
if route.segment_keys.include?(:action)
|
606
|
-
|
640
|
+
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
607
641
|
Using a dynamic :action segment in a route is deprecated and
|
608
|
-
will be removed in Rails 7.
|
642
|
+
will be removed in Rails 7.2.
|
609
643
|
MSG
|
610
644
|
end
|
611
645
|
|
@@ -779,18 +813,14 @@ module ActionDispatch
|
|
779
813
|
|
780
814
|
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
|
781
815
|
:trailing_slash, :anchor, :params, :only_path, :script_name,
|
782
|
-
:original_script_name
|
816
|
+
:original_script_name]
|
783
817
|
|
784
818
|
def optimize_routes_generation?
|
785
819
|
default_url_options.empty?
|
786
820
|
end
|
787
821
|
|
788
822
|
def find_script_name(options)
|
789
|
-
options.delete(:script_name) ||
|
790
|
-
end
|
791
|
-
|
792
|
-
def find_relative_url_root(options)
|
793
|
-
options.delete(:relative_url_root) || relative_url_root
|
823
|
+
options.delete(:script_name) || relative_url_root || ""
|
794
824
|
end
|
795
825
|
|
796
826
|
def path_for(options, route_name = nil, reserved = RESERVED_OPTIONS)
|
@@ -854,7 +884,7 @@ module ActionDispatch
|
|
854
884
|
|
855
885
|
def recognize_path(path, environment = {})
|
856
886
|
method = (environment[:method] || "GET").to_s.upcase
|
857
|
-
path = Journey::Router::Utils.normalize_path(path) unless
|
887
|
+
path = Journey::Router::Utils.normalize_path(path) unless path&.include?("://")
|
858
888
|
extras = environment[:extras] || {}
|
859
889
|
|
860
890
|
begin
|
@@ -29,23 +29,18 @@ module ActionDispatch
|
|
29
29
|
|
30
30
|
def method_missing(method, *args)
|
31
31
|
if @helpers.respond_to?(method)
|
32
|
-
|
33
|
-
|
34
|
-
options = args.extract_options!
|
35
|
-
options = url_options.merge((options || {}).symbolize_keys)
|
32
|
+
options = args.extract_options!
|
33
|
+
options = url_options.merge((options || {}).symbolize_keys)
|
36
34
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
if @script_namer
|
36
|
+
options[:script_name] = merge_script_names(
|
37
|
+
options[:script_name],
|
38
|
+
@script_namer.call(options)
|
39
|
+
)
|
40
|
+
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
RUBY
|
48
|
-
public_send(method, *args)
|
42
|
+
args << options
|
43
|
+
@helpers.public_send(method, *args)
|
49
44
|
else
|
50
45
|
super
|
51
46
|
end
|
@@ -2,20 +2,22 @@
|
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Routing
|
5
|
+
# = Action Dispatch Routing \UrlFor
|
6
|
+
#
|
5
7
|
# In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
|
6
8
|
# is also possible: a URL can be generated from one of your routing definitions.
|
7
9
|
# URL generation functionality is centralized in this module.
|
8
10
|
#
|
9
|
-
# See ActionDispatch::Routing for general information about routing and routes.rb
|
11
|
+
# See ActionDispatch::Routing for general information about routing and <tt>config/routes.rb</tt>.
|
10
12
|
#
|
11
13
|
# <b>Tip:</b> If you need to generate URLs from your models or some other place,
|
12
|
-
# then
|
14
|
+
# then ActionDispatch::Routing::UrlFor is what you're looking for. Read on for
|
13
15
|
# an introduction. In general, this module should not be included on its own,
|
14
|
-
# as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
|
16
|
+
# as it is usually included by +url_helpers+ (as in <tt>Rails.application.routes.url_helpers</tt>).
|
15
17
|
#
|
16
18
|
# == URL generation from parameters
|
17
19
|
#
|
18
|
-
# As you may know, some functions, such as ActionController::Base#url_for
|
20
|
+
# As you may know, some functions, such as <tt>ActionController::Base#url_for</tt>
|
19
21
|
# and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
|
20
22
|
# of parameters. For example, you've probably had the chance to write code
|
21
23
|
# like this in one of your views:
|
@@ -24,12 +26,12 @@ module ActionDispatch
|
|
24
26
|
# action: 'new', message: 'Welcome!') %>
|
25
27
|
# # => <a href="/users/new?message=Welcome%21">Click here</a>
|
26
28
|
#
|
27
|
-
# link_to
|
28
|
-
# actually use
|
29
|
-
# they use the
|
29
|
+
# +link_to+, and all other functions that require URL generation functionality,
|
30
|
+
# actually use ActionDispatch::Routing::UrlFor under the hood. And in particular,
|
31
|
+
# they use the ActionDispatch::Routing::UrlFor#url_for method. One can generate
|
30
32
|
# the same path as the above example by using the following code:
|
31
33
|
#
|
32
|
-
# include UrlFor
|
34
|
+
# include ActionDispatch::Routing::UrlFor
|
33
35
|
# url_for(controller: 'users',
|
34
36
|
# action: 'new',
|
35
37
|
# message: 'Welcome!',
|
@@ -37,7 +39,7 @@ module ActionDispatch
|
|
37
39
|
# # => "/users/new?message=Welcome%21"
|
38
40
|
#
|
39
41
|
# Notice the <tt>only_path: true</tt> part. This is because UrlFor has no
|
40
|
-
# information about the website hostname that your Rails app is serving. So if you
|
42
|
+
# information about the website hostname that your \Rails app is serving. So if you
|
41
43
|
# want to include the hostname as well, then you must also pass the <tt>:host</tt>
|
42
44
|
# argument:
|
43
45
|
#
|
@@ -48,17 +50,17 @@ module ActionDispatch
|
|
48
50
|
# host: 'www.example.com')
|
49
51
|
# # => "http://www.example.com/users/new?message=Welcome%21"
|
50
52
|
#
|
51
|
-
# By default, all controllers and views have access to a special version of url_for
|
52
|
-
# that already knows what the current hostname is. So if you use url_for in your
|
53
|
+
# By default, all controllers and views have access to a special version of +url_for+,
|
54
|
+
# that already knows what the current hostname is. So if you use +url_for+ in your
|
53
55
|
# controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
|
54
56
|
# argument.
|
55
57
|
#
|
56
|
-
# For convenience
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# the +:host+
|
60
|
-
#
|
61
|
-
#
|
58
|
+
# For convenience, mailers also include ActionDispatch::Routing::UrlFor. So
|
59
|
+
# within mailers, you can use url_for. However, mailers cannot access
|
60
|
+
# incoming web requests in order to derive hostname information, so you have
|
61
|
+
# to provide the +:host+ option or set the default host using
|
62
|
+
# +default_url_options+. For more information on url_for in mailers see the
|
63
|
+
# ActionMailer::Base documentation.
|
62
64
|
#
|
63
65
|
#
|
64
66
|
# == URL generation for named routes
|
@@ -72,7 +74,7 @@ module ActionDispatch
|
|
72
74
|
# This generates, among other things, the method <tt>users_path</tt>. By default,
|
73
75
|
# this method is accessible from your controllers, views, and mailers. If you need
|
74
76
|
# to access this auto-generated method from other places (such as a model), then
|
75
|
-
# you can do that by including Rails.application.routes.url_helpers in your class:
|
77
|
+
# you can do that by including <tt>Rails.application.routes.url_helpers</tt> in your class:
|
76
78
|
#
|
77
79
|
# class User < ActiveRecord::Base
|
78
80
|
# include Rails.application.routes.url_helpers
|
@@ -115,11 +117,11 @@ module ActionDispatch
|
|
115
117
|
default_url_options
|
116
118
|
end
|
117
119
|
|
118
|
-
# Generate a URL based on the options provided, default_url_options
|
119
|
-
# routes defined in routes.rb
|
120
|
+
# Generate a URL based on the options provided, +default_url_options+, and the
|
121
|
+
# routes defined in <tt>config/routes.rb</tt>. The following options are supported:
|
120
122
|
#
|
121
123
|
# * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
|
122
|
-
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to
|
124
|
+
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to <tt>"http"</tt>.
|
123
125
|
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
|
124
126
|
# If <tt>:only_path</tt> is false, this option must be
|
125
127
|
# provided either explicitly, or via +default_url_options+.
|
@@ -134,7 +136,9 @@ module ActionDispatch
|
|
134
136
|
# * <tt>:port</tt> - Optionally specify the port to connect to.
|
135
137
|
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
|
136
138
|
# * <tt>:params</tt> - The query parameters to be appended to the path.
|
137
|
-
# * <tt>:
|
139
|
+
# * <tt>:path_params</tt> - The query parameters that will only be used
|
140
|
+
# for the named dynamic segments of path. If unused, they will be discarded.
|
141
|
+
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in <tt>"/archive/2009/"</tt>.
|
138
142
|
# * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
|
139
143
|
#
|
140
144
|
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/string/filters"
|
4
|
-
|
5
3
|
module ActionDispatch
|
6
4
|
# The routing module provides URL rewriting in native Ruby. It's a way to
|
7
5
|
# redirect incoming requests to controllers and actions. This replaces
|
@@ -119,9 +117,9 @@ module ActionDispatch
|
|
119
117
|
#
|
120
118
|
# # In config/routes.rb
|
121
119
|
# controller :blog do
|
122
|
-
# get 'blog/show'
|
123
|
-
# get 'blog/delete'
|
124
|
-
# get 'blog/edit'
|
120
|
+
# get 'blog/show' => :list
|
121
|
+
# get 'blog/delete' => :delete
|
122
|
+
# get 'blog/edit' => :edit
|
125
123
|
# end
|
126
124
|
#
|
127
125
|
# # provides named routes for show, delete, and edit
|
@@ -240,7 +238,7 @@ module ActionDispatch
|
|
240
238
|
#
|
241
239
|
# == View a list of all your routes
|
242
240
|
#
|
243
|
-
# rails routes
|
241
|
+
# $ bin/rails routes
|
244
242
|
#
|
245
243
|
# Target a specific controller with <tt>-c</tt>, or grep routes
|
246
244
|
# using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
|
@@ -250,7 +248,9 @@ module ActionDispatch
|
|
250
248
|
|
251
249
|
autoload :Mapper
|
252
250
|
autoload :RouteSet
|
253
|
-
|
251
|
+
eager_autoload do
|
252
|
+
autoload :RoutesProxy
|
253
|
+
end
|
254
254
|
autoload :UrlFor
|
255
255
|
autoload :PolymorphicRoutes
|
256
256
|
|
@@ -51,7 +51,7 @@ module ActionDispatch
|
|
51
51
|
# driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
# By default,
|
54
|
+
# By default, +ActionDispatch::SystemTestCase+ is driven by the
|
55
55
|
# Selenium driver, with the Chrome browser, and a browser size of 1400x1400.
|
56
56
|
#
|
57
57
|
# Changing the driver configuration options is easy. Let's say you want to use
|
@@ -106,8 +106,8 @@ module ActionDispatch
|
|
106
106
|
# end
|
107
107
|
# end
|
108
108
|
#
|
109
|
-
# Because
|
110
|
-
# and Rails, any driver that is supported by Capybara is supported by system
|
109
|
+
# Because +ActionDispatch::SystemTestCase+ is a shim between Capybara
|
110
|
+
# and \Rails, any driver that is supported by Capybara is supported by system
|
111
111
|
# tests as long as you include the required gems and files.
|
112
112
|
class SystemTestCase < ActiveSupport::TestCase
|
113
113
|
include Capybara::DSL
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module SystemTesting
|
5
5
|
class Browser # :nodoc:
|
6
|
-
attr_reader :name
|
6
|
+
attr_reader :name
|
7
7
|
|
8
8
|
def initialize(name)
|
9
9
|
@name = name
|
@@ -21,35 +21,32 @@ module ActionDispatch
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def options
|
25
|
+
@options ||=
|
26
|
+
case type
|
27
|
+
when :chrome
|
28
|
+
::Selenium::WebDriver::Chrome::Options.new
|
29
|
+
when :firefox
|
30
|
+
::Selenium::WebDriver::Firefox::Options.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
24
34
|
def configure
|
25
|
-
|
26
|
-
yield options if block_given? && options
|
35
|
+
yield options if block_given?
|
27
36
|
end
|
28
37
|
|
29
|
-
# driver_path
|
30
|
-
#
|
31
|
-
# update the webdriver once and avoid race conditions when using
|
32
|
-
# parallel tests.
|
38
|
+
# driver_path is lazily initialized by default. Eagerly set it to
|
39
|
+
# avoid race conditions when using parallel tests.
|
33
40
|
def preload
|
34
41
|
case type
|
35
42
|
when :chrome
|
36
|
-
::Selenium::WebDriver::Chrome
|
43
|
+
resolve_driver_path(::Selenium::WebDriver::Chrome)
|
37
44
|
when :firefox
|
38
|
-
::Selenium::WebDriver::Firefox
|
45
|
+
resolve_driver_path(::Selenium::WebDriver::Firefox)
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
42
49
|
private
|
43
|
-
def initialize_options
|
44
|
-
@options ||=
|
45
|
-
case type
|
46
|
-
when :chrome
|
47
|
-
::Selenium::WebDriver::Chrome::Options.new
|
48
|
-
when :firefox
|
49
|
-
::Selenium::WebDriver::Firefox::Options.new
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
50
|
def set_default_options
|
54
51
|
case name
|
55
52
|
when :headless_chrome
|
@@ -71,6 +68,10 @@ module ActionDispatch
|
|
71
68
|
capabilities.add_argument("-headless")
|
72
69
|
end
|
73
70
|
end
|
71
|
+
|
72
|
+
def resolve_driver_path(namespace)
|
73
|
+
namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.path(options, namespace::Service)
|
74
|
+
end
|
74
75
|
end
|
75
76
|
end
|
76
77
|
end
|
@@ -12,14 +12,6 @@ module ActionDispatch
|
|
12
12
|
@name = @options.delete(:name) || driver_type
|
13
13
|
@capabilities = capabilities
|
14
14
|
|
15
|
-
if [:poltergeist, :webkit].include?(driver_type)
|
16
|
-
ActiveSupport::Deprecation.warn <<~MSG.squish
|
17
|
-
Poltergeist and capybara-webkit are not maintained already.
|
18
|
-
Driver registration of :poltergeist or :webkit is deprecated and will be removed in Rails 7.1.
|
19
|
-
You can still use :selenium, and also :cuprite is available for alternative to Poltergeist.
|
20
|
-
MSG
|
21
|
-
end
|
22
|
-
|
23
15
|
if driver_type == :selenium
|
24
16
|
gem "selenium-webdriver", ">= 4.0.0"
|
25
17
|
require "selenium/webdriver"
|
@@ -38,7 +30,7 @@ module ActionDispatch
|
|
38
30
|
|
39
31
|
private
|
40
32
|
def registerable?
|
41
|
-
[:selenium, :
|
33
|
+
[:selenium, :cuprite, :rack_test, :playwright].include?(@driver_type)
|
42
34
|
end
|
43
35
|
|
44
36
|
def register
|
@@ -47,16 +39,15 @@ module ActionDispatch
|
|
47
39
|
Capybara.register_driver name do |app|
|
48
40
|
case @driver_type
|
49
41
|
when :selenium then register_selenium(app)
|
50
|
-
when :poltergeist then register_poltergeist(app)
|
51
|
-
when :webkit then register_webkit(app)
|
52
42
|
when :cuprite then register_cuprite(app)
|
53
43
|
when :rack_test then register_rack_test(app)
|
44
|
+
when :playwright then register_playwright(app)
|
54
45
|
end
|
55
46
|
end
|
56
47
|
end
|
57
48
|
|
58
49
|
def browser_options
|
59
|
-
@options.merge(
|
50
|
+
@options.merge(options: @browser.options).compact
|
60
51
|
end
|
61
52
|
|
62
53
|
def register_selenium(app)
|
@@ -65,16 +56,6 @@ module ActionDispatch
|
|
65
56
|
end
|
66
57
|
end
|
67
58
|
|
68
|
-
def register_poltergeist(app)
|
69
|
-
Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
|
70
|
-
end
|
71
|
-
|
72
|
-
def register_webkit(app)
|
73
|
-
Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
|
74
|
-
driver.resize_window_to(driver.current_window_handle, *@screen_size)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
59
|
def register_cuprite(app)
|
79
60
|
Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
|
80
61
|
end
|
@@ -83,6 +64,17 @@ module ActionDispatch
|
|
83
64
|
Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
|
84
65
|
end
|
85
66
|
|
67
|
+
def register_playwright(app)
|
68
|
+
screen = { width: @screen_size[0], height: @screen_size[1] } if @screen_size
|
69
|
+
options = {
|
70
|
+
screen: screen,
|
71
|
+
viewport: screen,
|
72
|
+
**@options
|
73
|
+
}.compact
|
74
|
+
|
75
|
+
Capybara::Playwright::Driver.new(app, **options)
|
76
|
+
end
|
77
|
+
|
86
78
|
def setup
|
87
79
|
Capybara.current_driver = name
|
88
80
|
end
|
@@ -13,28 +13,28 @@ 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 screenshot will be displayed in your console, if supported.
|
17
|
-
#
|
18
16
|
# The default screenshots directory is +tmp/screenshots+ but you can set a different
|
19
17
|
# one with +Capybara.save_path+
|
20
18
|
#
|
21
|
-
# You can
|
22
|
-
# save the HTML from the page that is being screenshotted
|
23
|
-
# 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
|
24
22
|
#
|
25
|
-
# You can
|
26
|
-
# 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:
|
27
25
|
# * [+simple+ (default)] Only displays the screenshot path.
|
28
26
|
# This is the default value.
|
29
27
|
# * [+inline+] Display the screenshot in the terminal using the
|
30
28
|
# iTerm image protocol (https://iterm2.com/documentation-images.html).
|
31
29
|
# * [+artifact+] Display the screenshot in the terminal, using the terminal
|
32
30
|
# artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
33
|
-
def take_screenshot
|
31
|
+
def take_screenshot(html: false, screenshot: nil)
|
32
|
+
showing_html = html || html_from_env?
|
33
|
+
|
34
34
|
increment_unique
|
35
|
-
save_html if
|
35
|
+
save_html if showing_html
|
36
36
|
save_image
|
37
|
-
|
37
|
+
show display_image(html: showing_html, screenshot_output: screenshot)
|
38
38
|
end
|
39
39
|
|
40
40
|
# Takes a screenshot of the current page in the browser if the test
|
@@ -42,13 +42,16 @@ module ActionDispatch
|
|
42
42
|
#
|
43
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
|
@@ -87,6 +90,10 @@ module ActionDispatch
|
|
87
90
|
"#{absolute_path}.png"
|
88
91
|
end
|
89
92
|
|
93
|
+
def relative_image_path
|
94
|
+
"#{absolute_path.relative_path_from(Rails.root)}.png"
|
95
|
+
end
|
96
|
+
|
90
97
|
def absolute_html_path
|
91
98
|
"#{absolute_path}.html"
|
92
99
|
end
|
@@ -109,11 +116,15 @@ module ActionDispatch
|
|
109
116
|
output_type
|
110
117
|
end
|
111
118
|
|
112
|
-
def
|
119
|
+
def show(img)
|
120
|
+
puts img
|
121
|
+
end
|
122
|
+
|
123
|
+
def display_image(html:, screenshot_output:)
|
113
124
|
message = +"[Screenshot Image]: #{image_path}\n"
|
114
|
-
message << +"[Screenshot HTML]: #{html_path}\n" if
|
125
|
+
message << +"[Screenshot HTML]: #{html_path}\n" if html
|
115
126
|
|
116
|
-
case output_type
|
127
|
+
case screenshot_output || output_type
|
117
128
|
when "artifact"
|
118
129
|
message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
|
119
130
|
when "inline"
|