actionpack 7.0.8 → 7.1.0.rc1
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 +324 -383
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/base.rb +19 -10
- 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 +61 -18
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +3 -3
- data/lib/abstract_controller/translation.rb +1 -5
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +5 -3
- 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/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +2 -0
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +2 -0
- 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 +8 -6
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +11 -4
- 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 +24 -0
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +3 -1
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +6 -6
- data/lib/action_controller/metal/renderers.rb +2 -2
- data/lib/action_controller/metal/rendering.rb +0 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +138 -50
- data/lib/action_controller/metal/rescue.rb +2 -0
- data/lib/action_controller/metal/streaming.rb +70 -30
- data/lib/action_controller/metal/strong_parameters.rb +122 -52
- data/lib/action_controller/metal/url_for.rb +7 -0
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +22 -9
- data/lib/action_controller/renderer.rb +98 -65
- 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 +1 -3
- data/lib/action_dispatch/http/content_security_policy.rb +9 -8
- data/lib/action_dispatch/http/filter_parameters.rb +11 -5
- data/lib/action_dispatch/http/headers.rb +2 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +21 -21
- 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 +39 -17
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +48 -14
- data/lib/action_dispatch/http/response.rb +78 -59
- data/lib/action_dispatch/http/upload.rb +2 -0
- 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 +81 -98
- 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 +181 -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 +6 -3
- 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 +17 -16
- data/lib/action_dispatch/middleware/request_id.rb +2 -0
- 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 +19 -15
- 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 +12 -8
- 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/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 +46 -37
- 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 +26 -14
- 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 +1 -1
- data/lib/action_dispatch/routing/url_for.rb +5 -1
- data/lib/action_dispatch/routing.rb +4 -4
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +5 -6
- data/lib/action_dispatch/system_testing/driver.rb +13 -21
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- data/lib/action_dispatch/testing/assertions/response.rb +13 -6
- 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 +51 -29
@@ -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._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,7 +29,7 @@ module ActionDispatch
|
|
29
29
|
|
30
30
|
def method_missing(method, *args)
|
31
31
|
if @helpers.respond_to?(method)
|
32
|
-
|
32
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
33
33
|
def #{method}(*args)
|
34
34
|
options = args.extract_options!
|
35
35
|
options = url_options.merge((options || {}).symbolize_keys)
|
@@ -2,6 +2,8 @@
|
|
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.
|
@@ -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
|
#
|
@@ -134,6 +136,8 @@ 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.
|
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.
|
137
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
|
#
|
@@ -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
|
@@ -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
|
@@ -26,16 +26,15 @@ module ActionDispatch
|
|
26
26
|
yield options if block_given? && options
|
27
27
|
end
|
28
28
|
|
29
|
-
# driver_path can be configured as a proc.
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# parallel tests.
|
29
|
+
# driver_path can be configured as a proc. Running this proc early allows
|
30
|
+
# us to only update the webdriver once and avoid race conditions when
|
31
|
+
# using parallel tests.
|
33
32
|
def preload
|
34
33
|
case type
|
35
34
|
when :chrome
|
36
|
-
::Selenium::WebDriver::Chrome::Service.driver_path
|
35
|
+
::Selenium::WebDriver::Chrome::Service.driver_path.try(:call)
|
37
36
|
when :firefox
|
38
|
-
::Selenium::WebDriver::Firefox::Service.driver_path
|
37
|
+
::Selenium::WebDriver::Firefox::Service.driver_path.try(:call)
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
@@ -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,10 +39,9 @@ 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
|
@@ -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"
|
@@ -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
|
@@ -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
|
@@ -190,6 +198,37 @@ module ActionDispatch
|
|
190
198
|
ruby2_keywords(:method_missing)
|
191
199
|
|
192
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
|
+
|
193
232
|
# Recognizes the route for a given path.
|
194
233
|
def recognized_request_for(path, extras = {}, msg)
|
195
234
|
if path.is_a?(Hash)
|
@@ -202,7 +241,7 @@ module ActionDispatch
|
|
202
241
|
controller = @controller if defined?(@controller)
|
203
242
|
request = ActionController::TestRequest.create controller&.class
|
204
243
|
|
205
|
-
if
|
244
|
+
if path.include?("://")
|
206
245
|
fail_on(URI::InvalidURIError, msg) do
|
207
246
|
uri = URI.parse(path)
|
208
247
|
request.env["rack.url_scheme"] = uri.scheme || "http"
|
@@ -6,6 +6,8 @@ require "action_dispatch/testing/assertions/routing"
|
|
6
6
|
|
7
7
|
module ActionDispatch
|
8
8
|
module Assertions
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
9
11
|
include ResponseAssertions
|
10
12
|
include RoutingAssertions
|
11
13
|
include Rails::Dom::Testing::Assertions
|
@@ -14,7 +16,7 @@ module ActionDispatch
|
|
14
16
|
@html_document ||= if @response.media_type&.end_with?("xml")
|
15
17
|
Nokogiri::XML::Document.parse(@response.body)
|
16
18
|
else
|
17
|
-
|
19
|
+
Rails::Dom::Testing.html_document.parse(@response.body)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|