capybara 3.36.0 → 3.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +27 -1
- data/README.md +1 -1
- data/lib/capybara/driver/node.rb +4 -0
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/helpers.rb +1 -1
- data/lib/capybara/minitest/spec.rb +2 -2
- data/lib/capybara/node/element.rb +12 -1
- data/lib/capybara/node/finders.rb +8 -1
- data/lib/capybara/queries/selector_query.rb +20 -7
- data/lib/capybara/rack_test/browser.rb +56 -7
- data/lib/capybara/rack_test/driver.rb +4 -4
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/registration_container.rb +0 -3
- data/lib/capybara/rspec/matchers/have_selector.rb +4 -4
- data/lib/capybara/rspec/matchers.rb +14 -14
- data/lib/capybara/selector/definition.rb +2 -1
- data/lib/capybara/selector/selector.rb +5 -1
- data/lib/capybara/selenium/driver.rb +6 -2
- data/lib/capybara/selenium/node.rb +12 -0
- data/lib/capybara/server/animation_disabler.rb +29 -17
- data/lib/capybara/session/config.rb +1 -1
- data/lib/capybara/session.rb +8 -21
- data/lib/capybara/spec/session/all_spec.rb +5 -7
- data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
- data/lib/capybara/spec/session/find_spec.rb +6 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
- data/lib/capybara/spec/session/has_field_spec.rb +1 -1
- data/lib/capybara/spec/session/has_link_spec.rb +6 -0
- data/lib/capybara/spec/session/has_select_spec.rb +4 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -5
- data/lib/capybara/spec/session/node_spec.rb +42 -0
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/test_app.rb +49 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +1 -0
- data/spec/dsl_spec.rb +2 -2
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
- data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
- data/spec/rack_test_spec.rb +6 -0
- data/spec/result_spec.rb +27 -29
- data/spec/rspec/features_spec.rb +2 -2
- data/spec/rspec/scenarios_spec.rb +1 -1
- data/spec/sauce_spec_chrome.rb +3 -3
- data/spec/selector_spec.rb +2 -2
- data/spec/selenium_spec_chrome.rb +1 -1
- data/spec/selenium_spec_firefox.rb +5 -1
- data/spec/selenium_spec_safari.rb +5 -1
- data/spec/server_spec.rb +5 -5
- data/spec/shared_selenium_session.rb +9 -2
- data/spec/spec_helper.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7283f19c4364fa2c19379e8da670105ebf59754937fc80117d975cd89959b87
|
4
|
+
data.tar.gz: b4fe7210ee210667602e9ec0a9882a1a34d8a64ceddae4df888aba0dd6974940
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71de08c12fdd479453fa743e0a7ef858e72ca96d9a32d2ecb0b1721b365a1f1e494309432b52536dcac5669af78fe50e60b1a9f886f0b08a2ac2fd378abfc52e
|
7
|
+
data.tar.gz: 91a2e75a15768ef02e8c9b9f1400c4ee1dd2e1e2562b4733dc520afddc4c71be962fcd5c0379481fa65938d0776cfe5434c75f5faf0e512246abca48b06b0c00
|
data/History.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
# Version 3.37.1
|
2
|
+
Relesae date: 2022-05-09
|
3
|
+
|
4
|
+
### Fixed
|
5
|
+
|
6
|
+
* Regression in rack-test visit - Issue #2548
|
7
|
+
|
8
|
+
# Version 3.37.0
|
9
|
+
Release date: 2022-05-07
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
* Ruby 2.7.0+ is now required
|
14
|
+
|
15
|
+
### Added
|
16
|
+
|
17
|
+
* [Beta] CSP nonces inserted into animation disabler additions - Issue #2542
|
18
|
+
* Support `<base>` element in rack-test driver - ISsue #2544
|
19
|
+
* [Beta] `Element#shadow_root` support. Requires selenium-webdriver 4.1+. Only currently supported with Chrome when using the selenium driver. Note: only CSS can be used to find elements from the shadow root. Therefore you won't be able to use most Capybara helper methods (`fill_in`, `click_link`, `find_field`, etc) directly from the shadow root since those locators are built using XPath. If you first locate a descendant from the shadow root using CSS then you should be able to use all the Capybara methods from there.
|
20
|
+
* Regexp now supported for `exact_text` finder option
|
21
|
+
|
22
|
+
### Fixed
|
23
|
+
|
24
|
+
* Fragments in referer headers in rack-test driver - Issue #2525
|
25
|
+
* Selenium v4.1 deprecation notice
|
26
|
+
|
1
27
|
# Version 3.36.0
|
2
28
|
Release date: 2021-10-24
|
3
29
|
|
@@ -10,7 +36,7 @@ Release date: 2021-10-24
|
|
10
36
|
|
11
37
|
* Support for selenium-webdriver 4.x
|
12
38
|
* `allow_label_click` accepts click options to be used when clicking an associated label
|
13
|
-
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::
|
39
|
+
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTML5 when available
|
14
40
|
* `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
|
15
41
|
* Support `focused:` filter for finding interactive elements - Not supported by the `RackTest` driver [Sean Doyle]
|
16
42
|
|
data/README.md
CHANGED
@@ -74,7 +74,7 @@ GitHub): http://groups.google.com/group/ruby-capybara
|
|
74
74
|
|
75
75
|
## <a name="setup"></a>Setup
|
76
76
|
|
77
|
-
Capybara requires Ruby 2.
|
77
|
+
Capybara requires Ruby 2.7.0 or later. To install, add this line to your
|
78
78
|
`Gemfile` and run `bundle install`:
|
79
79
|
|
80
80
|
```ruby
|
data/lib/capybara/driver/node.rb
CHANGED
@@ -125,6 +125,10 @@ module Capybara
|
|
125
125
|
raise NotSupportedByDriverError, 'Capybara::Driver::Node#trigger'
|
126
126
|
end
|
127
127
|
|
128
|
+
def shadow_root
|
129
|
+
raise NotSupportedByDriverError, 'Capybara::Driver::Node#shadow_root'
|
130
|
+
end
|
131
|
+
|
128
132
|
def inspect
|
129
133
|
%(#<#{self.class} tag="#{tag_name}" path="#{path}">)
|
130
134
|
rescue NotSupportedByDriverError
|
data/lib/capybara/dsl.rb
CHANGED
@@ -47,17 +47,11 @@ module Capybara
|
|
47
47
|
end
|
48
48
|
|
49
49
|
Session::DSL_METHODS.each do |method|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
page.method("#{method}").call(...)
|
54
|
-
end
|
55
|
-
METHOD
|
56
|
-
else
|
57
|
-
define_method method do |*args, &block|
|
58
|
-
page.send method, *args, &block
|
50
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
51
|
+
def #{method}(...)
|
52
|
+
page.method("#{method}").call(...)
|
59
53
|
end
|
60
|
-
|
54
|
+
METHOD
|
61
55
|
end
|
62
56
|
end
|
63
57
|
|
data/lib/capybara/helpers.rb
CHANGED
@@ -73,7 +73,7 @@ module Capybara
|
|
73
73
|
def filter_backtrace(trace)
|
74
74
|
return 'No backtrace' unless trace
|
75
75
|
|
76
|
-
filter = %r{lib/capybara/|lib/rspec/|lib/minitest
|
76
|
+
filter = %r{lib/capybara/|lib/rspec/|lib/minitest/|delegate.rb}
|
77
77
|
new_trace = trace.take_while { |line| line !~ filter }
|
78
78
|
new_trace = trace.grep_v(filter) if new_trace.empty?
|
79
79
|
new_trace = trace.dup if new_trace.empty?
|
@@ -246,9 +246,9 @@ module Capybara
|
|
246
246
|
|
247
247
|
##
|
248
248
|
# @deprecated
|
249
|
-
def must_have_style(
|
249
|
+
def must_have_style(...)
|
250
250
|
warn 'must_have_style is deprecated, please use must_match_style'
|
251
|
-
must_match_style(
|
251
|
+
must_match_style(...)
|
252
252
|
end
|
253
253
|
end
|
254
254
|
end
|
@@ -115,7 +115,7 @@ module Capybara
|
|
115
115
|
#
|
116
116
|
# @return [Capybara::Node::Element] The element
|
117
117
|
def set(value, **options)
|
118
|
-
if ENV
|
118
|
+
if ENV.fetch('CAPYBARA_THOROUGH', nil) && readonly?
|
119
119
|
raise Capybara::ReadOnlyElementError, "Attempt to set readonly element with value: #{value}"
|
120
120
|
end
|
121
121
|
|
@@ -472,6 +472,17 @@ module Capybara
|
|
472
472
|
self
|
473
473
|
end
|
474
474
|
|
475
|
+
##
|
476
|
+
#
|
477
|
+
# Return the shadow_root for the current element
|
478
|
+
#
|
479
|
+
# @return [Capybara::Node::Element] The shadow root
|
480
|
+
|
481
|
+
def shadow_root
|
482
|
+
root = synchronize { base.shadow_root }
|
483
|
+
root && Capybara::Node::Element.new(session, root, nil, nil)
|
484
|
+
end
|
485
|
+
|
475
486
|
##
|
476
487
|
#
|
477
488
|
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
@@ -18,7 +18,7 @@ module Capybara
|
|
18
18
|
#
|
19
19
|
# @!macro system_filters
|
20
20
|
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
|
21
|
-
# @option options [String,
|
21
|
+
# @option options [String, Regexp, String] exact_text
|
22
22
|
# When String the elements contained text must match exactly, when Boolean controls whether the `text` option must match exactly.
|
23
23
|
# Defaults to {Capybara.configure exact_text}.
|
24
24
|
# @option options [Boolean] normalize_ws
|
@@ -50,6 +50,13 @@ module Capybara
|
|
50
50
|
#
|
51
51
|
def find(*args, **options, &optional_filter_block)
|
52
52
|
options[:session_options] = session_options
|
53
|
+
count_options = options.slice(*Capybara::Queries::BaseQuery::COUNT_KEYS)
|
54
|
+
unless count_options.empty?
|
55
|
+
Capybara::Helpers.warn(
|
56
|
+
"'find' does not support count options (#{count_options}) ignoring. " \
|
57
|
+
"Called from: #{Capybara::Helpers.filter_backtrace(caller)}"
|
58
|
+
)
|
59
|
+
end
|
53
60
|
synced_resolve Capybara::Queries::SelectorQuery.new(*args, **options, &optional_filter_block)
|
54
61
|
end
|
55
62
|
|
@@ -27,6 +27,12 @@ module Capybara
|
|
27
27
|
@order = order
|
28
28
|
@filter_cache = Hash.new { |hsh, key| hsh[key] = {} }
|
29
29
|
|
30
|
+
if @options[:text].is_a?(Regexp) && [true, false].include?(@options[:exact_text])
|
31
|
+
Capybara::Helpers.warn(
|
32
|
+
"Boolean 'exact_text' option is not supported when 'text' option is a Regexp - ignoring"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
30
36
|
super(@options)
|
31
37
|
self.session_options = session_options
|
32
38
|
|
@@ -541,16 +547,19 @@ module Capybara
|
|
541
547
|
def matches_text_filter?(node)
|
542
548
|
value = options[:text]
|
543
549
|
return true unless value
|
544
|
-
return matches_text_exactly?(node, value) if exact_text == true
|
550
|
+
return matches_text_exactly?(node, value) if exact_text == true && !value.is_a?(Regexp)
|
545
551
|
|
546
552
|
regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
|
547
553
|
matches_text_regexp?(node, regexp)
|
548
554
|
end
|
549
555
|
|
550
556
|
def matches_exact_text_filter?(node)
|
551
|
-
|
552
|
-
|
553
|
-
|
557
|
+
case exact_text
|
558
|
+
when String, Regexp
|
559
|
+
matches_text_exactly?(node, exact_text)
|
560
|
+
else
|
561
|
+
true
|
562
|
+
end
|
554
563
|
end
|
555
564
|
|
556
565
|
def matches_visibility_filters?(node)
|
@@ -578,17 +587,21 @@ module Capybara
|
|
578
587
|
|
579
588
|
def matches_text_exactly?(node, value)
|
580
589
|
regexp = value.is_a?(Regexp) ? value : /\A#{Regexp.escape(value.to_s)}\z/
|
581
|
-
matches_text_regexp
|
590
|
+
matches_text_regexp(node, regexp).then { |m| m&.pre_match == '' && m&.post_match == '' }
|
582
591
|
end
|
583
592
|
|
584
593
|
def normalize_ws
|
585
594
|
options.fetch(:normalize_ws, session_options.default_normalize_ws)
|
586
595
|
end
|
587
596
|
|
588
|
-
def matches_text_regexp
|
597
|
+
def matches_text_regexp(node, regexp)
|
589
598
|
text_visible = visible
|
590
599
|
text_visible = :all if text_visible == :hidden
|
591
|
-
node.text(text_visible, normalize_ws: normalize_ws).match
|
600
|
+
node.text(text_visible, normalize_ws: normalize_ws).match(regexp)
|
601
|
+
end
|
602
|
+
|
603
|
+
def matches_text_regexp?(node, regexp)
|
604
|
+
!matches_text_regexp(node, regexp).nil?
|
592
605
|
end
|
593
606
|
|
594
607
|
def default_visibility
|
@@ -20,6 +20,8 @@ class Capybara::RackTest::Browser
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def visit(path, **attributes)
|
23
|
+
@new_visit_request = true
|
24
|
+
reset_cache!
|
23
25
|
reset_host!
|
24
26
|
process_and_follow_redirects(:get, path, attributes)
|
25
27
|
end
|
@@ -33,19 +35,18 @@ class Capybara::RackTest::Browser
|
|
33
35
|
path = request_path if path.nil? || path.empty?
|
34
36
|
uri = build_uri(path)
|
35
37
|
uri.query = '' if method.to_s.casecmp('get').zero?
|
36
|
-
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' =>
|
38
|
+
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => referer_url)
|
37
39
|
end
|
38
40
|
|
39
41
|
def follow(method, path, **attributes)
|
40
42
|
return if fragment_or_script?(path)
|
41
43
|
|
42
|
-
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' =>
|
44
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => referer_url)
|
43
45
|
end
|
44
46
|
|
45
47
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
46
48
|
@current_fragment = build_uri(path).fragment
|
47
49
|
process(method, path, attributes, env)
|
48
|
-
|
49
50
|
return unless driver.follow_redirects?
|
50
51
|
|
51
52
|
driver.redirect_limit.times do
|
@@ -69,18 +70,23 @@ class Capybara::RackTest::Browser
|
|
69
70
|
@current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
|
70
71
|
@current_fragment = new_uri.fragment || @current_fragment
|
71
72
|
reset_cache!
|
73
|
+
@new_visit_request = false
|
72
74
|
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
|
73
75
|
end
|
74
76
|
|
75
77
|
def build_uri(path)
|
76
|
-
URI.parse(path)
|
77
|
-
|
78
|
-
uri.path = '/' if uri.path.empty?
|
79
|
-
uri.path = request_path.sub(%r{/[^/]*$}, '/') + uri.path unless uri.path.start_with?('/')
|
78
|
+
uri = URI.parse(path)
|
79
|
+
base_uri = base_relative_uri_for(uri)
|
80
80
|
|
81
|
+
uri.path = base_uri.path + uri.path unless uri.absolute? || uri.path.start_with?('/')
|
82
|
+
|
83
|
+
if base_uri.absolute?
|
84
|
+
base_uri.merge(uri)
|
85
|
+
else
|
81
86
|
uri.scheme ||= @current_scheme
|
82
87
|
uri.host ||= @current_host
|
83
88
|
uri.port ||= @current_port unless uri.default_port == @current_port
|
89
|
+
uri
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
@@ -123,8 +129,39 @@ class Capybara::RackTest::Browser
|
|
123
129
|
dom.title
|
124
130
|
end
|
125
131
|
|
132
|
+
def last_request
|
133
|
+
raise Rack::Test::Error if @new_visit_request
|
134
|
+
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def last_response
|
139
|
+
raise Rack::Test::Error if @new_visit_request
|
140
|
+
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
126
144
|
protected
|
127
145
|
|
146
|
+
def base_href
|
147
|
+
find(:css, 'head > base').first&.[](:href).to_s
|
148
|
+
end
|
149
|
+
|
150
|
+
def base_relative_uri_for(uri)
|
151
|
+
base_uri = URI.parse(base_href)
|
152
|
+
current_uri = URI.parse(safe_last_request&.url.to_s).tap do |c|
|
153
|
+
c.path.sub!(%r{/[^/]*$}, '/') unless uri.path.empty?
|
154
|
+
c.path = '/' if c.path.empty?
|
155
|
+
end
|
156
|
+
|
157
|
+
if [current_uri, base_uri].any?(&:absolute?)
|
158
|
+
current_uri.merge(base_uri)
|
159
|
+
else
|
160
|
+
base_uri.path = current_uri.path if base_uri.path.empty?
|
161
|
+
base_uri
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
128
165
|
def build_rack_mock_session
|
129
166
|
reset_host! unless current_host
|
130
167
|
Rack::MockSession.new(app, current_host)
|
@@ -136,9 +173,21 @@ protected
|
|
136
173
|
'/'
|
137
174
|
end
|
138
175
|
|
176
|
+
def safe_last_request
|
177
|
+
last_request
|
178
|
+
rescue Rack::Test::Error
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
139
182
|
private
|
140
183
|
|
141
184
|
def fragment_or_script?(path)
|
142
185
|
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
143
186
|
end
|
187
|
+
|
188
|
+
def referer_url
|
189
|
+
build_uri(last_request.url).to_s
|
190
|
+
rescue Rack::Test::Error
|
191
|
+
''
|
192
|
+
end
|
144
193
|
end
|
@@ -98,10 +98,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
98
98
|
@browser = nil
|
99
99
|
end
|
100
100
|
|
101
|
-
def get(
|
102
|
-
def post(
|
103
|
-
def put(
|
104
|
-
def delete(
|
101
|
+
def get(...); browser.get(...); end
|
102
|
+
def post(...); browser.post(...); end
|
103
|
+
def put(...); browser.put(...); end
|
104
|
+
def delete(...); browser.delete(...); end
|
105
105
|
def header(key, value); browser.header(key, value); end
|
106
106
|
|
107
107
|
def invalid_element_errors
|
@@ -244,7 +244,7 @@ private
|
|
244
244
|
end
|
245
245
|
|
246
246
|
def follow_link
|
247
|
-
method = self['data-method'] if driver.options[:respect_data_method]
|
247
|
+
method = self['data-method'] || self['data-turbo-method'] if driver.options[:respect_data_method]
|
248
248
|
method ||= :get
|
249
249
|
driver.follow(method, self[:href].to_s)
|
250
250
|
end
|
@@ -19,9 +19,6 @@ module Capybara
|
|
19
19
|
def method_missing(method_name, *args, **options, &block)
|
20
20
|
if @registered.respond_to?(method_name)
|
21
21
|
Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
|
22
|
-
# RUBY 2.6 will send an empty hash rather than nothing with **options so fix that
|
23
|
-
return @registered.public_send(method_name, *args, &block) if options.empty?
|
24
|
-
|
25
22
|
return @registered.public_send(method_name, *args, **options, &block)
|
26
23
|
end
|
27
24
|
super
|
@@ -8,10 +8,10 @@ module Capybara
|
|
8
8
|
class HaveSelector < CountableWrappedElementMatcher
|
9
9
|
def initialize(*args, **kw_args, &filter_block)
|
10
10
|
super
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
return unless (@args.size < 2) && @kw_args.keys.any?(String)
|
12
|
+
|
13
|
+
@args.push(@kw_args)
|
14
|
+
@kw_args = {}
|
15
15
|
end
|
16
16
|
|
17
17
|
def element_matches?(el)
|
@@ -15,36 +15,36 @@ module Capybara
|
|
15
15
|
# RSpec matcher for whether the element(s) matching a given selector exist.
|
16
16
|
#
|
17
17
|
# @see Capybara::Node::Matchers#assert_selector
|
18
|
-
def have_selector(
|
19
|
-
Matchers::HaveSelector.new(
|
18
|
+
def have_selector(...)
|
19
|
+
Matchers::HaveSelector.new(...)
|
20
20
|
end
|
21
21
|
|
22
22
|
# RSpec matcher for whether the element(s) matching a group of selectors exist.
|
23
23
|
#
|
24
24
|
# @see Capybara::Node::Matchers#assert_all_of_selectors
|
25
|
-
def have_all_of_selectors(
|
26
|
-
Matchers::HaveAllSelectors.new(
|
25
|
+
def have_all_of_selectors(...)
|
26
|
+
Matchers::HaveAllSelectors.new(...)
|
27
27
|
end
|
28
28
|
|
29
29
|
# RSpec matcher for whether no element(s) matching a group of selectors exist.
|
30
30
|
#
|
31
31
|
# @see Capybara::Node::Matchers#assert_none_of_selectors
|
32
|
-
def have_none_of_selectors(
|
33
|
-
Matchers::HaveNoSelectors.new(
|
32
|
+
def have_none_of_selectors(...)
|
33
|
+
Matchers::HaveNoSelectors.new(...)
|
34
34
|
end
|
35
35
|
|
36
36
|
# RSpec matcher for whether the element(s) matching any of a group of selectors exist.
|
37
37
|
#
|
38
38
|
# @see Capybara::Node::Matchers#assert_any_of_selectors
|
39
|
-
def have_any_of_selectors(
|
40
|
-
Matchers::HaveAnySelectors.new(
|
39
|
+
def have_any_of_selectors(...)
|
40
|
+
Matchers::HaveAnySelectors.new(...)
|
41
41
|
end
|
42
42
|
|
43
43
|
# RSpec matcher for whether the current element matches a given selector.
|
44
44
|
#
|
45
45
|
# @see Capybara::Node::Matchers#assert_matches_selector
|
46
|
-
def match_selector(
|
47
|
-
Matchers::MatchSelector.new(
|
46
|
+
def match_selector(...)
|
47
|
+
Matchers::MatchSelector.new(...)
|
48
48
|
end
|
49
49
|
|
50
50
|
%i[css xpath].each do |selector|
|
@@ -177,15 +177,15 @@ module Capybara
|
|
177
177
|
# RSpec matcher for whether sibling element(s) matching a given selector exist.
|
178
178
|
#
|
179
179
|
# @see Capybara::Node::Matchers#assert_sibling
|
180
|
-
def have_sibling(
|
181
|
-
Matchers::HaveSibling.new(
|
180
|
+
def have_sibling(...)
|
181
|
+
Matchers::HaveSibling.new(...)
|
182
182
|
end
|
183
183
|
|
184
184
|
# RSpec matcher for whether ancestor element(s) matching a given selector exist.
|
185
185
|
#
|
186
186
|
# @see Capybara::Node::Matchers#assert_ancestor
|
187
|
-
def have_ancestor(
|
188
|
-
Matchers::HaveAncestor.new(
|
187
|
+
def have_ancestor(...)
|
188
|
+
Matchers::HaveAncestor.new(...)
|
189
189
|
end
|
190
190
|
|
191
191
|
##
|
@@ -261,7 +261,8 @@ module Capybara
|
|
261
261
|
def parameter_names(block)
|
262
262
|
key_types = %i[key keyreq]
|
263
263
|
# user filter_map when we drop dupport for 2.6
|
264
|
-
block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_, name)| name }
|
264
|
+
# block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_, name)| name }
|
265
|
+
block.parameters.filter_map { |(type, name)| name if key_types.include? type }
|
265
266
|
end
|
266
267
|
|
267
268
|
def expression(type, allowed_filters, &block)
|
@@ -66,7 +66,11 @@ module Capybara
|
|
66
66
|
end
|
67
67
|
ensure
|
68
68
|
unless locator_valid?(locator)
|
69
|
-
|
69
|
+
Capybara::Helpers.warn(
|
70
|
+
"Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. " \
|
71
|
+
'This will raise an error in a future version of Capybara. ' \
|
72
|
+
"Called from: #{Capybara::Helpers.filter_backtrace(caller)}"
|
73
|
+
)
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
@@ -12,7 +12,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
12
12
|
clear_session_storage: nil
|
13
13
|
}.freeze
|
14
14
|
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout native_displayed].freeze
|
15
|
-
CAPS_VERSION = Gem::Requirement.new('
|
15
|
+
CAPS_VERSION = Gem::Requirement.new('>= 4.0.0.alpha6')
|
16
16
|
|
17
17
|
attr_reader :app, :options
|
18
18
|
|
@@ -473,12 +473,16 @@ private
|
|
473
473
|
end
|
474
474
|
|
475
475
|
def unwrap_script_result(arg)
|
476
|
+
# TODO: move into the case when we drop support for Selenium < 4.1
|
477
|
+
element_types = [Selenium::WebDriver::Element]
|
478
|
+
element_types.push(Selenium::WebDriver::ShadowRoot) if defined?(Selenium::WebDriver::ShadowRoot)
|
479
|
+
|
476
480
|
case arg
|
477
481
|
when Array
|
478
482
|
arg.map { |arr| unwrap_script_result(arr) }
|
479
483
|
when Hash
|
480
484
|
arg.transform_values! { |value| unwrap_script_result(value) }
|
481
|
-
when
|
485
|
+
when *element_types
|
482
486
|
build_node(arg)
|
483
487
|
else
|
484
488
|
arg
|
@@ -53,6 +53,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
53
53
|
# :none => append the new value to the existing value <br/>
|
54
54
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
55
55
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
56
|
+
# @option options [Boolean] :rapid (nil) Whether setting text inputs should use a faster "rapid" mode<br/>
|
57
|
+
# nil => Text inputs with length greater than 30 characters will be set using a faster driver script mode<br/>
|
58
|
+
# true => Rapid mode will be used regardless of input length<br/>
|
59
|
+
# false => Sends keys via conventional mode. This may be required to avoid losing key-presses if you have certain
|
60
|
+
# Javascript interactions on form inputs<br/>
|
56
61
|
def set(value, **options)
|
57
62
|
if value.is_a?(Array) && !multiple?
|
58
63
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
@@ -214,6 +219,13 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
214
219
|
native.rect
|
215
220
|
end
|
216
221
|
|
222
|
+
def shadow_root
|
223
|
+
raise_error 'You must be using Selenium 4.1+ for shadow_root support' unless native.respond_to? :shadow_root
|
224
|
+
|
225
|
+
root = native.shadow_root
|
226
|
+
root && build_node(native.shadow_root)
|
227
|
+
end
|
228
|
+
|
217
229
|
protected
|
218
230
|
|
219
231
|
def scroll_if_needed
|
@@ -26,9 +26,10 @@ module Capybara
|
|
26
26
|
@status, @headers, @body = @app.call(env)
|
27
27
|
return [@status, @headers, @body] unless html_content?
|
28
28
|
|
29
|
+
nonces = directive_nonces.transform_values { |nonce| "nonce=\"#{nonce}\"" if nonce && !nonce.empty? }
|
29
30
|
response = Rack::Response.new([], @status, @headers)
|
30
31
|
|
31
|
-
@body.each { |html| response.write insert_disable(html) }
|
32
|
+
@body.each { |html| response.write insert_disable(html, nonces) }
|
32
33
|
@body.close if @body.respond_to?(:close)
|
33
34
|
|
34
35
|
response.finish
|
@@ -42,28 +43,39 @@ module Capybara
|
|
42
43
|
/html/.match?(@headers['Content-Type'])
|
43
44
|
end
|
44
45
|
|
45
|
-
def insert_disable(html)
|
46
|
-
html.sub(%r{(</head>)}, "#{disable_css_markup}
|
46
|
+
def insert_disable(html, nonces)
|
47
|
+
html.sub(%r{(</head>)}, "<style #{nonces['style-src']}>#{disable_css_markup}</style>\\1")
|
48
|
+
.sub(%r{(</body>)}, "<script #{nonces['script-src']}>#{disable_js_markup}</script>\\1")
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
def directive_nonces
|
52
|
+
@headers.fetch('Content-Security-Policy', '')
|
53
|
+
.split(';')
|
54
|
+
.map(&:split)
|
55
|
+
.to_h do |s|
|
56
|
+
[
|
57
|
+
s[0], s[1..].filter_map do |value|
|
58
|
+
/^'nonce-(?<nonce>.+)'/ =~ value
|
59
|
+
nonce
|
60
|
+
end[0]
|
61
|
+
]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
DISABLE_CSS_MARKUP_TEMPLATE = <<~CSS
|
66
|
+
%<selector>s, %<selector>s::before, %<selector>s::after {
|
67
|
+
transition: none !important;
|
68
|
+
animation-duration: 0s !important;
|
69
|
+
animation-delay: 0s !important;
|
70
|
+
scroll-behavior: auto !important;
|
71
|
+
}
|
72
|
+
CSS
|
59
73
|
|
60
|
-
DISABLE_JS_MARKUP_TEMPLATE = <<~
|
61
|
-
<script>
|
74
|
+
DISABLE_JS_MARKUP_TEMPLATE = <<~SCRIPT
|
62
75
|
//<![CDATA[
|
63
76
|
(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);
|
64
77
|
//]]>
|
65
|
-
|
66
|
-
HTML
|
78
|
+
SCRIPT
|
67
79
|
end
|
68
80
|
end
|
69
81
|
end
|
@@ -100,7 +100,7 @@ module Capybara
|
|
100
100
|
remove_method :test_id=
|
101
101
|
##
|
102
102
|
#
|
103
|
-
# Set an
|
103
|
+
# Set an attribute to be optionally matched against the locator for builtin selector types.
|
104
104
|
# This attribute will be checked by builtin selector types whenever id would normally be checked.
|
105
105
|
# If `nil` then it will be ignored.
|
106
106
|
#
|