actionpack 6.0.5.1 → 6.1.0
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 +248 -344
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/abstract_controller/base.rb +35 -2
- data/lib/abstract_controller/callbacks.rb +2 -2
- data/lib/abstract_controller/helpers.rb +105 -90
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +8 -2
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +2 -2
- data/lib/action_controller/base.rb +4 -2
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/log_subscriber.rb +3 -3
- data/lib/action_controller/metal/conditional_get.rb +10 -2
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +1 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
- data/lib/action_controller/metal/exceptions.rb +33 -0
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +11 -1
- data/lib/action_controller/metal/http_authentication.rb +5 -3
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -9
- data/lib/action_controller/metal/live.rb +1 -1
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +6 -2
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +16 -11
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +1 -1
- data/lib/action_controller/metal/rendering.rb +6 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +103 -15
- data/lib/action_controller/metal.rb +2 -2
- data/lib/action_controller/renderer.rb +23 -13
- data/lib/action_controller/test_case.rb +62 -56
- data/lib/action_controller.rb +2 -3
- data/lib/action_dispatch/http/cache.rb +12 -10
- data/lib/action_dispatch/http/content_security_policy.rb +11 -0
- data/lib/action_dispatch/http/filter_parameters.rb +1 -1
- data/lib/action_dispatch/http/filter_redirect.rb +1 -1
- data/lib/action_dispatch/http/headers.rb +3 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
- data/lib/action_dispatch/http/mime_type.rb +29 -16
- data/lib/action_dispatch/http/parameters.rb +1 -19
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +24 -8
- data/lib/action_dispatch/http/response.rb +17 -16
- data/lib/action_dispatch/http/url.rb +3 -2
- data/lib/action_dispatch/journey/formatter.rb +53 -28
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +4 -3
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +13 -18
- data/lib/action_dispatch/journey/route.rb +7 -18
- data/lib/action_dispatch/journey/router/utils.rb +6 -4
- data/lib/action_dispatch/journey/router.rb +26 -30
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +67 -32
- data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
- data/lib/action_dispatch/middleware/debug_view.rb +1 -1
- data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +35 -35
- data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
- data/lib/action_dispatch/middleware/request_id.rb +4 -5
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
- data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
- data/lib/action_dispatch/middleware/ssl.rb +9 -6
- data/lib/action_dispatch/middleware/stack.rb +18 -0
- data/lib/action_dispatch/middleware/static.rb +154 -93
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +100 -8
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
- data/lib/action_dispatch/railtie.rb +3 -2
- data/lib/action_dispatch/request/session.rb +2 -8
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +8 -7
- data/lib/action_dispatch/routing/mapper.rb +102 -71
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -19
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +49 -41
- data/lib/action_dispatch/system_test_case.rb +29 -24
- data/lib/action_dispatch/system_testing/browser.rb +33 -27
- data/lib/action_dispatch/system_testing/driver.rb +6 -7
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
- data/lib/action_dispatch/testing/assertions/response.rb +2 -4
- data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +38 -27
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch.rb +3 -2
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +21 -23
- data/lib/action_controller/metal/force_ssl.rb +0 -58
- data/lib/action_dispatch/http/parameter_filter.rb +0 -12
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -110,8 +110,10 @@ module ActionDispatch
|
|
110
110
|
@url_helpers_module.undef_method url_name
|
111
111
|
end
|
112
112
|
routes[key] = route
|
113
|
-
|
114
|
-
|
113
|
+
|
114
|
+
helper = UrlHelper.create(route, route.defaults, name)
|
115
|
+
define_url_helper @path_helpers_module, path_name, helper, PATH
|
116
|
+
define_url_helper @url_helpers_module, url_name, helper, UNKNOWN
|
115
117
|
|
116
118
|
@path_helpers << path_name
|
117
119
|
@url_helpers << url_name
|
@@ -169,30 +171,30 @@ module ActionDispatch
|
|
169
171
|
end
|
170
172
|
|
171
173
|
class UrlHelper
|
172
|
-
def self.create(route, options, route_name
|
174
|
+
def self.create(route, options, route_name)
|
173
175
|
if optimize_helper?(route)
|
174
|
-
OptimizedUrlHelper.new(route, options, route_name
|
176
|
+
OptimizedUrlHelper.new(route, options, route_name)
|
175
177
|
else
|
176
|
-
new
|
178
|
+
new(route, options, route_name)
|
177
179
|
end
|
178
180
|
end
|
179
181
|
|
180
182
|
def self.optimize_helper?(route)
|
181
|
-
|
183
|
+
route.path.requirements.empty? && !route.glob?
|
182
184
|
end
|
183
185
|
|
184
|
-
attr_reader :
|
186
|
+
attr_reader :route_name
|
185
187
|
|
186
188
|
class OptimizedUrlHelper < UrlHelper
|
187
189
|
attr_reader :arg_size
|
188
190
|
|
189
|
-
def initialize(route, options, route_name
|
191
|
+
def initialize(route, options, route_name)
|
190
192
|
super
|
191
193
|
@required_parts = @route.required_parts
|
192
194
|
@arg_size = @required_parts.size
|
193
195
|
end
|
194
196
|
|
195
|
-
def call(t, args, inner_options)
|
197
|
+
def call(t, method_name, args, inner_options, url_strategy)
|
196
198
|
if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
|
197
199
|
options = t.url_options.merge @options
|
198
200
|
options[:path] = optimized_helper(args)
|
@@ -249,15 +251,14 @@ module ActionDispatch
|
|
249
251
|
end
|
250
252
|
end
|
251
253
|
|
252
|
-
def initialize(route, options, route_name
|
254
|
+
def initialize(route, options, route_name)
|
253
255
|
@options = options
|
254
256
|
@segment_keys = route.segment_keys.uniq
|
255
257
|
@route = route
|
256
|
-
@url_strategy = url_strategy
|
257
258
|
@route_name = route_name
|
258
259
|
end
|
259
260
|
|
260
|
-
def call(t, args, inner_options)
|
261
|
+
def call(t, method_name, args, inner_options, url_strategy)
|
261
262
|
controller_options = t.url_options
|
262
263
|
options = controller_options.merge @options
|
263
264
|
hash = handle_positional_args(controller_options,
|
@@ -266,7 +267,7 @@ module ActionDispatch
|
|
266
267
|
options,
|
267
268
|
@segment_keys)
|
268
269
|
|
269
|
-
t._routes.url_for(hash, route_name, url_strategy)
|
270
|
+
t._routes.url_for(hash, route_name, url_strategy, method_name)
|
270
271
|
end
|
271
272
|
|
272
273
|
def handle_positional_args(controller_options, inner_options, args, result, path_params)
|
@@ -312,8 +313,7 @@ module ActionDispatch
|
|
312
313
|
#
|
313
314
|
# foo_url(bar, baz, bang, sort_by: 'baz')
|
314
315
|
#
|
315
|
-
def define_url_helper(mod,
|
316
|
-
helper = UrlHelper.create(route, opts, route_key, url_strategy)
|
316
|
+
def define_url_helper(mod, name, helper, url_strategy)
|
317
317
|
mod.define_method(name) do |*args|
|
318
318
|
last = args.last
|
319
319
|
options = \
|
@@ -323,7 +323,7 @@ module ActionDispatch
|
|
323
323
|
when ActionController::Parameters
|
324
324
|
args.pop.to_h
|
325
325
|
end
|
326
|
-
helper.call
|
326
|
+
helper.call(self, name, args, options, url_strategy)
|
327
327
|
end
|
328
328
|
end
|
329
329
|
end
|
@@ -334,7 +334,7 @@ module ActionDispatch
|
|
334
334
|
|
335
335
|
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
|
336
336
|
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
337
|
-
attr_accessor :default_url_options
|
337
|
+
attr_accessor :default_url_options, :draw_paths
|
338
338
|
attr_reader :env_key, :polymorphic_mappings
|
339
339
|
|
340
340
|
alias :routes :set
|
@@ -366,6 +366,7 @@ module ActionDispatch
|
|
366
366
|
self.named_routes = NamedRouteCollection.new
|
367
367
|
self.resources_path_names = self.class.default_resources_path_names
|
368
368
|
self.default_url_options = {}
|
369
|
+
self.draw_paths = []
|
369
370
|
|
370
371
|
@config = config
|
371
372
|
@append = []
|
@@ -476,6 +477,14 @@ module ActionDispatch
|
|
476
477
|
end
|
477
478
|
|
478
479
|
def url_helpers(supports_path = true)
|
480
|
+
if supports_path
|
481
|
+
@url_helpers_with_paths ||= generate_url_helpers(true)
|
482
|
+
else
|
483
|
+
@url_helpers_without_paths ||= generate_url_helpers(false)
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def generate_url_helpers(supports_path)
|
479
488
|
routes = self
|
480
489
|
|
481
490
|
Module.new do
|
@@ -588,14 +597,14 @@ module ActionDispatch
|
|
588
597
|
if route.segment_keys.include?(:controller)
|
589
598
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
590
599
|
Using a dynamic :controller segment in a route is deprecated and
|
591
|
-
will be removed in Rails 6.
|
600
|
+
will be removed in Rails 6.2.
|
592
601
|
MSG
|
593
602
|
end
|
594
603
|
|
595
604
|
if route.segment_keys.include?(:action)
|
596
605
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
597
606
|
Using a dynamic :action segment in a route is deprecated and
|
598
|
-
will be removed in Rails 6.
|
607
|
+
will be removed in Rails 6.2.
|
599
608
|
MSG
|
600
609
|
end
|
601
610
|
|
@@ -641,14 +650,6 @@ module ActionDispatch
|
|
641
650
|
end
|
642
651
|
|
643
652
|
class Generator
|
644
|
-
PARAMETERIZE = lambda do |name, value|
|
645
|
-
if name == :controller
|
646
|
-
value
|
647
|
-
else
|
648
|
-
value.to_param
|
649
|
-
end
|
650
|
-
end
|
651
|
-
|
652
653
|
attr_reader :options, :recall, :set, :named_route
|
653
654
|
|
654
655
|
def initialize(named_route, options, recall, set)
|
@@ -732,10 +733,10 @@ module ActionDispatch
|
|
732
733
|
end
|
733
734
|
end
|
734
735
|
|
735
|
-
# Generates a path from routes, returns
|
736
|
-
#
|
736
|
+
# Generates a path from routes, returns a RouteWithParams or MissingRoute.
|
737
|
+
# MissingRoute will raise ActionController::UrlGenerationError.
|
737
738
|
def generate
|
738
|
-
@set.formatter.generate(named_route, options, recall
|
739
|
+
@set.formatter.generate(named_route, options, recall)
|
739
740
|
end
|
740
741
|
|
741
742
|
def different_controller?
|
@@ -760,13 +761,18 @@ module ActionDispatch
|
|
760
761
|
end
|
761
762
|
|
762
763
|
def generate_extras(options, recall = {})
|
763
|
-
|
764
|
-
|
765
|
-
|
764
|
+
if recall
|
765
|
+
options = options.merge(_recall: recall)
|
766
|
+
end
|
767
|
+
|
768
|
+
route_name = options.delete :use_route
|
769
|
+
generator = generate(route_name, options, recall)
|
770
|
+
path_info = path_for(options, route_name, [])
|
771
|
+
[URI(path_info).path, generator.params.except(:_recall).keys]
|
766
772
|
end
|
767
773
|
|
768
|
-
def generate(
|
769
|
-
Generator.new(
|
774
|
+
def generate(route_name, options, recall = {}, method_name = nil)
|
775
|
+
Generator.new(route_name, options, recall, self).generate
|
770
776
|
end
|
771
777
|
private :generate
|
772
778
|
|
@@ -786,12 +792,12 @@ module ActionDispatch
|
|
786
792
|
options.delete(:relative_url_root) || relative_url_root
|
787
793
|
end
|
788
794
|
|
789
|
-
def path_for(options, route_name = nil)
|
790
|
-
url_for(options, route_name, PATH)
|
795
|
+
def path_for(options, route_name = nil, reserved = RESERVED_OPTIONS)
|
796
|
+
url_for(options, route_name, PATH, nil, reserved)
|
791
797
|
end
|
792
798
|
|
793
799
|
# The +options+ argument must be a hash whose keys are *symbols*.
|
794
|
-
def url_for(options, route_name = nil, url_strategy = UNKNOWN)
|
800
|
+
def url_for(options, route_name = nil, url_strategy = UNKNOWN, method_name = nil, reserved = RESERVED_OPTIONS)
|
795
801
|
options = default_url_options.merge options
|
796
802
|
|
797
803
|
user = password = nil
|
@@ -811,9 +817,11 @@ module ActionDispatch
|
|
811
817
|
end
|
812
818
|
|
813
819
|
path_options = options.dup
|
814
|
-
|
820
|
+
reserved.each { |ro| path_options.delete ro }
|
815
821
|
|
816
|
-
|
822
|
+
route_with_params = generate(route_name, path_options, recall)
|
823
|
+
path = route_with_params.path(method_name)
|
824
|
+
params = route_with_params.params
|
817
825
|
|
818
826
|
if options.key? :params
|
819
827
|
params.merge! options[:params]
|
@@ -855,7 +863,7 @@ module ActionDispatch
|
|
855
863
|
params.each do |key, value|
|
856
864
|
if value.is_a?(String)
|
857
865
|
value = value.dup.force_encoding(Encoding::BINARY)
|
858
|
-
params[key] = URI.
|
866
|
+
params[key] = URI::DEFAULT_PARSER.unescape(value)
|
859
867
|
end
|
860
868
|
end
|
861
869
|
req.path_parameters = params
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
gem "capybara", ">=
|
3
|
+
gem "capybara", ">= 3.26"
|
4
4
|
|
5
5
|
require "capybara/dsl"
|
6
6
|
require "capybara/minitest"
|
7
|
-
require "selenium/webdriver"
|
8
7
|
require "action_controller"
|
9
8
|
require "action_dispatch/system_testing/driver"
|
10
9
|
require "action_dispatch/system_testing/browser"
|
@@ -27,7 +26,7 @@ module ActionDispatch
|
|
27
26
|
#
|
28
27
|
# Here is an example system test:
|
29
28
|
#
|
30
|
-
# require
|
29
|
+
# require "application_system_test_case"
|
31
30
|
#
|
32
31
|
# class Users::CreateTest < ApplicationSystemTestCase
|
33
32
|
# test "adding a new user" do
|
@@ -120,18 +119,6 @@ module ActionDispatch
|
|
120
119
|
super
|
121
120
|
self.class.driven_by(:selenium) unless self.class.driver?
|
122
121
|
self.class.driver.use
|
123
|
-
@proxy_route = if ActionDispatch.test_app
|
124
|
-
Class.new do
|
125
|
-
include ActionDispatch.test_app.routes.url_helpers
|
126
|
-
include ActionDispatch.test_app.routes.mounted_helpers
|
127
|
-
|
128
|
-
def url_options
|
129
|
-
default_url_options.merge(host: Capybara.app_host)
|
130
|
-
end
|
131
|
-
end.new
|
132
|
-
else
|
133
|
-
nil
|
134
|
-
end
|
135
122
|
end
|
136
123
|
|
137
124
|
def self.start_application # :nodoc:
|
@@ -170,16 +157,34 @@ module ActionDispatch
|
|
170
157
|
self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
|
171
158
|
end
|
172
159
|
|
173
|
-
|
174
|
-
|
175
|
-
@
|
176
|
-
|
177
|
-
|
160
|
+
private
|
161
|
+
def url_helpers
|
162
|
+
@url_helpers ||=
|
163
|
+
if ActionDispatch.test_app
|
164
|
+
Class.new do
|
165
|
+
include ActionDispatch.test_app.routes.url_helpers
|
166
|
+
include ActionDispatch.test_app.routes.mounted_helpers
|
167
|
+
|
168
|
+
def url_options
|
169
|
+
default_url_options.reverse_merge(host: Capybara.app_host || Capybara.current_session.server_url)
|
170
|
+
end
|
171
|
+
end.new
|
172
|
+
end
|
178
173
|
end
|
179
|
-
end
|
180
174
|
|
181
|
-
|
182
|
-
|
175
|
+
def method_missing(name, *args, &block)
|
176
|
+
if url_helpers.respond_to?(name)
|
177
|
+
url_helpers.public_send(name, *args, &block)
|
178
|
+
else
|
179
|
+
super
|
180
|
+
end
|
181
|
+
end
|
183
182
|
|
184
|
-
|
183
|
+
def respond_to_missing?(name, include_private = false)
|
184
|
+
url_helpers.respond_to?(name)
|
185
|
+
end
|
186
|
+
end
|
185
187
|
end
|
188
|
+
|
189
|
+
ActiveSupport.run_load_hooks :action_dispatch_system_test_case, ActionDispatch::SystemTestCase
|
190
|
+
ActionDispatch::SystemTestCase.start_application
|
@@ -3,10 +3,11 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module SystemTesting
|
5
5
|
class Browser # :nodoc:
|
6
|
-
attr_reader :name
|
6
|
+
attr_reader :name, :options
|
7
7
|
|
8
8
|
def initialize(name)
|
9
9
|
@name = name
|
10
|
+
set_default_options
|
10
11
|
end
|
11
12
|
|
12
13
|
def type
|
@@ -20,23 +21,9 @@ module ActionDispatch
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
headless_chrome_browser_options
|
27
|
-
when :headless_firefox
|
28
|
-
headless_firefox_browser_options
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def capabilities
|
33
|
-
@option ||=
|
34
|
-
case type
|
35
|
-
when :chrome
|
36
|
-
::Selenium::WebDriver::Chrome::Options.new
|
37
|
-
when :firefox
|
38
|
-
::Selenium::WebDriver::Firefox::Options.new
|
39
|
-
end
|
24
|
+
def configure
|
25
|
+
initialize_options
|
26
|
+
yield options if block_given? && options
|
40
27
|
end
|
41
28
|
|
42
29
|
# driver_path can be configured as a proc. The webdrivers gem uses this
|
@@ -47,14 +34,14 @@ module ActionDispatch
|
|
47
34
|
case type
|
48
35
|
when :chrome
|
49
36
|
if ::Selenium::WebDriver::Service.respond_to? :driver_path=
|
50
|
-
::Selenium::WebDriver::Chrome::Service.driver_path
|
37
|
+
::Selenium::WebDriver::Chrome::Service.driver_path&.call
|
51
38
|
else
|
52
39
|
# Selenium <= v3.141.0
|
53
40
|
::Selenium::WebDriver::Chrome.driver_path
|
54
41
|
end
|
55
42
|
when :firefox
|
56
43
|
if ::Selenium::WebDriver::Service.respond_to? :driver_path=
|
57
|
-
::Selenium::WebDriver::Firefox::Service.driver_path
|
44
|
+
::Selenium::WebDriver::Firefox::Service.driver_path&.call
|
58
45
|
else
|
59
46
|
# Selenium <= v3.141.0
|
60
47
|
::Selenium::WebDriver::Firefox.driver_path
|
@@ -63,17 +50,36 @@ module ActionDispatch
|
|
63
50
|
end
|
64
51
|
|
65
52
|
private
|
66
|
-
def
|
67
|
-
|
68
|
-
|
53
|
+
def initialize_options
|
54
|
+
@options ||=
|
55
|
+
case type
|
56
|
+
when :chrome
|
57
|
+
::Selenium::WebDriver::Chrome::Options.new
|
58
|
+
when :firefox
|
59
|
+
::Selenium::WebDriver::Firefox::Options.new
|
60
|
+
end
|
61
|
+
end
|
69
62
|
|
70
|
-
|
63
|
+
def set_default_options
|
64
|
+
case name
|
65
|
+
when :headless_chrome
|
66
|
+
set_headless_chrome_browser_options
|
67
|
+
when :headless_firefox
|
68
|
+
set_headless_firefox_browser_options
|
69
|
+
end
|
71
70
|
end
|
72
71
|
|
73
|
-
def
|
74
|
-
capabilities
|
72
|
+
def set_headless_chrome_browser_options
|
73
|
+
configure do |capabilities|
|
74
|
+
capabilities.args << "--headless"
|
75
|
+
capabilities.args << "--disable-gpu" if Gem.win_platform?
|
76
|
+
end
|
77
|
+
end
|
75
78
|
|
76
|
-
|
79
|
+
def set_headless_firefox_browser_options
|
80
|
+
configure do |capabilities|
|
81
|
+
capabilities.args << "-headless"
|
82
|
+
end
|
77
83
|
end
|
78
84
|
end
|
79
85
|
end
|
@@ -7,10 +7,13 @@ module ActionDispatch
|
|
7
7
|
@name = name
|
8
8
|
@browser = Browser.new(options[:using])
|
9
9
|
@screen_size = options[:screen_size]
|
10
|
-
@options = options[:options]
|
10
|
+
@options = options[:options] || {}
|
11
11
|
@capabilities = capabilities
|
12
12
|
|
13
|
-
|
13
|
+
if name == :selenium
|
14
|
+
require "selenium/webdriver"
|
15
|
+
@browser.preload
|
16
|
+
end
|
14
17
|
end
|
15
18
|
|
16
19
|
def use
|
@@ -25,7 +28,7 @@ module ActionDispatch
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def register
|
28
|
-
|
31
|
+
@browser.configure(&@capabilities)
|
29
32
|
|
30
33
|
Capybara.register_driver @name do |app|
|
31
34
|
case @name
|
@@ -36,10 +39,6 @@ module ActionDispatch
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
|
-
def define_browser_capabilities(capabilities)
|
40
|
-
@capabilities.call(capabilities) if @capabilities
|
41
|
-
end
|
42
|
-
|
43
42
|
def browser_options
|
44
43
|
@options.merge(options: @browser.options).compact
|
45
44
|
end
|
@@ -9,10 +9,16 @@ module ActionDispatch
|
|
9
9
|
#
|
10
10
|
# +take_screenshot+ can be used at any point in your system tests to take
|
11
11
|
# a screenshot of the current state. This can be useful for debugging or
|
12
|
-
# automating visual testing.
|
12
|
+
# automating visual testing. You can take multiple screenshots per test
|
13
|
+
# to investigate changes at different points during your test. These will be
|
14
|
+
# named with a sequential prefix (or 'failed' for failing tests)
|
13
15
|
#
|
14
16
|
# The screenshot will be displayed in your console, if supported.
|
15
17
|
#
|
18
|
+
# You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+ environment variable to
|
19
|
+
# save the HTML from the page that is being screenhoted so you can investigate the
|
20
|
+
# elements on the page at the time of the screenshot
|
21
|
+
#
|
16
22
|
# You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
|
17
23
|
# control the output. Possible values are:
|
18
24
|
# * [+simple+ (default)] Only displays the screenshot path.
|
@@ -22,6 +28,8 @@ module ActionDispatch
|
|
22
28
|
# * [+artifact+] Display the screenshot in the terminal, using the terminal
|
23
29
|
# artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
24
30
|
def take_screenshot
|
31
|
+
increment_unique
|
32
|
+
save_html if save_html?
|
25
33
|
save_image
|
26
34
|
puts display_image
|
27
35
|
end
|
@@ -38,17 +46,49 @@ module ActionDispatch
|
|
38
46
|
end
|
39
47
|
|
40
48
|
private
|
49
|
+
attr_accessor :_screenshot_counter
|
50
|
+
|
51
|
+
def save_html?
|
52
|
+
ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
|
53
|
+
end
|
54
|
+
|
55
|
+
def increment_unique
|
56
|
+
@_screenshot_counter ||= 0
|
57
|
+
@_screenshot_counter += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def unique
|
61
|
+
failed? ? "failures" : (_screenshot_counter || 0).to_s
|
62
|
+
end
|
63
|
+
|
41
64
|
def image_name
|
42
|
-
|
43
|
-
|
65
|
+
sanitized_method_name = method_name.tr("/\\", "--")
|
66
|
+
name = "#{unique}_#{sanitized_method_name}"
|
67
|
+
name[0...225]
|
44
68
|
end
|
45
69
|
|
46
70
|
def image_path
|
47
|
-
|
71
|
+
absolute_image_path.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def html_path
|
75
|
+
absolute_html_path.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def absolute_path
|
79
|
+
Rails.root.join("tmp/screenshots/#{image_name}")
|
48
80
|
end
|
49
81
|
|
50
82
|
def absolute_image_path
|
51
|
-
|
83
|
+
"#{absolute_path}.png"
|
84
|
+
end
|
85
|
+
|
86
|
+
def absolute_html_path
|
87
|
+
"#{absolute_path}.html"
|
88
|
+
end
|
89
|
+
|
90
|
+
def save_html
|
91
|
+
page.save_page(absolute_html_path)
|
52
92
|
end
|
53
93
|
|
54
94
|
def save_image
|
@@ -66,7 +106,8 @@ module ActionDispatch
|
|
66
106
|
end
|
67
107
|
|
68
108
|
def display_image
|
69
|
-
message = +"[Screenshot]: #{image_path}\n"
|
109
|
+
message = +"[Screenshot Image]: #{image_path}\n"
|
110
|
+
message << +"[Screenshot HTML]: #{html_path}\n" if save_html?
|
70
111
|
|
71
112
|
case output_type
|
72
113
|
when "artifact"
|
@@ -4,15 +4,12 @@ module ActionDispatch
|
|
4
4
|
module SystemTesting
|
5
5
|
module TestHelpers
|
6
6
|
module SetupAndTeardown # :nodoc:
|
7
|
-
DEFAULT_HOST = "http://127.0.0.1"
|
8
|
-
|
9
7
|
def host!(host)
|
10
|
-
|
11
|
-
|
8
|
+
ActiveSupport::Deprecation.warn \
|
9
|
+
"ActionDispatch::SystemTestCase#host! is deprecated with no replacement. " \
|
10
|
+
"Set Capybara.app_host directly or rely on Capybara's default host."
|
12
11
|
|
13
|
-
|
14
|
-
host! DEFAULT_HOST
|
15
|
-
super
|
12
|
+
Capybara.app_host = host
|
16
13
|
end
|
17
14
|
|
18
15
|
def before_teardown
|
@@ -31,15 +31,13 @@ module ActionDispatch
|
|
31
31
|
message ||= generate_response_message(type)
|
32
32
|
|
33
33
|
if RESPONSE_PREDICATES.keys.include?(type)
|
34
|
-
assert @response.
|
34
|
+
assert @response.public_send(RESPONSE_PREDICATES[type]), message
|
35
35
|
else
|
36
36
|
assert_equal AssertionResponse.new(type).code, @response.response_code, message
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
# Asserts that the
|
41
|
-
# This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
|
42
|
-
# match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
|
40
|
+
# Asserts that the response is a redirect to a URL matching the given options.
|
43
41
|
#
|
44
42
|
# # Asserts that the redirection was to the "index" action on the WeblogController
|
45
43
|
# assert_redirected_to controller: "weblog", action: "index"
|
@@ -89,9 +89,8 @@ module ActionDispatch
|
|
89
89
|
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
90
90
|
end
|
91
91
|
else
|
92
|
-
expected_path = "/#{expected_path}" unless expected_path.
|
92
|
+
expected_path = "/#{expected_path}" unless expected_path.start_with?("/")
|
93
93
|
end
|
94
|
-
# Load routes.rb if it hasn't been loaded.
|
95
94
|
|
96
95
|
options = options.clone
|
97
96
|
generated_path, query_string_keys = @routes.generate_extras(options, defaults)
|
@@ -183,7 +182,7 @@ module ActionDispatch
|
|
183
182
|
# ROUTES TODO: These assertions should really work in an integration context
|
184
183
|
def method_missing(selector, *args, &block)
|
185
184
|
if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)
|
186
|
-
@controller.
|
185
|
+
@controller.public_send(selector, *args, &block)
|
187
186
|
else
|
188
187
|
super
|
189
188
|
end
|
@@ -199,7 +198,8 @@ module ActionDispatch
|
|
199
198
|
method = :get
|
200
199
|
end
|
201
200
|
|
202
|
-
|
201
|
+
controller = @controller if defined?(@controller)
|
202
|
+
request = ActionController::TestRequest.create controller&.class
|
203
203
|
|
204
204
|
if %r{://}.match?(path)
|
205
205
|
fail_on(URI::InvalidURIError, msg) do
|
@@ -210,7 +210,7 @@ module ActionDispatch
|
|
210
210
|
request.path = uri.path.to_s.empty? ? "/" : uri.path
|
211
211
|
end
|
212
212
|
else
|
213
|
-
path = "/#{path}" unless path.
|
213
|
+
path = "/#{path}" unless path.start_with?("/")
|
214
214
|
request.path = path
|
215
215
|
end
|
216
216
|
|
@@ -14,7 +14,7 @@ module ActionDispatch
|
|
14
14
|
include Rails::Dom::Testing::Assertions
|
15
15
|
|
16
16
|
def html_document
|
17
|
-
@html_document ||= if @response.media_type
|
17
|
+
@html_document ||= if @response.media_type&.end_with?("xml")
|
18
18
|
Nokogiri::XML::Document.parse(@response.body)
|
19
19
|
else
|
20
20
|
Nokogiri::HTML::Document.parse(@response.body)
|