capybara 3.36.0 → 3.37.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +20 -1
- data/README.md +1 -1
- data/lib/capybara/driver/node.rb +4 -0
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/minitest/spec.rb +2 -2
- data/lib/capybara/node/element.rb +12 -1
- data/lib/capybara/node/finders.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +20 -7
- data/lib/capybara/rack_test/browser.rb +41 -6
- 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/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/has_current_path_spec.rb +2 -2
- data/lib/capybara/spec/session/has_field_spec.rb +1 -1
- 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 +14 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/test_app.rb +33 -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 +1 -1
- 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: 125837a4db6ae6c9b12ab2339f5daa673397ef9cdb9054689fb1d155a5d6e62f
|
4
|
+
data.tar.gz: 445b064e8ae642678457cbf2739a1a6124b7d35dd6aaf295d3d9edce4f6ee8f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95154994ad904eae4c9eeb73605bcc273daef240a0cbf241afa8cf1bcfef1ca2de52ecd159fe551ee2d863a0a9a07a2fb9a3beab659f334ec5782f50c5af4ba1
|
7
|
+
data.tar.gz: 600e6610c5c48ac5619d1c4cf2ddfc83b86d5315ea259c7d8a6476863a14d4a209bcec0c6334dab5f3ebefac5eaeb1ccaa8a263114a5838f7a49a8483fb44538
|
data/History.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# Version 3.37.0
|
2
|
+
Release date: 2022-05-07
|
3
|
+
|
4
|
+
### Changed
|
5
|
+
|
6
|
+
* Ruby 2.7.0+ is now required
|
7
|
+
|
8
|
+
### Added
|
9
|
+
|
10
|
+
* [Beta] CSP nonces inserted into animation disabler additions - Issue #2542
|
11
|
+
* Support `<base>` element in rack-test driver - ISsue #2544
|
12
|
+
* [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.
|
13
|
+
* Regexp now supported for `exact_text` finder option
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
* Fragments in referer headers in rack-test driver - Issue #2525
|
18
|
+
* Selenium v4.1 deprecation notice
|
19
|
+
|
1
20
|
# Version 3.36.0
|
2
21
|
Release date: 2021-10-24
|
3
22
|
|
@@ -10,7 +29,7 @@ Release date: 2021-10-24
|
|
10
29
|
|
11
30
|
* Support for selenium-webdriver 4.x
|
12
31
|
* `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::
|
32
|
+
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTML5 when available
|
14
33
|
* `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
|
15
34
|
* Support `focused:` filter for finding interactive elements - Not supported by the `RackTest` driver [Sean Doyle]
|
16
35
|
|
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
|
|
@@ -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
|
@@ -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
|
@@ -33,13 +33,13 @@ class Capybara::RackTest::Browser
|
|
33
33
|
path = request_path if path.nil? || path.empty?
|
34
34
|
uri = build_uri(path)
|
35
35
|
uri.query = '' if method.to_s.casecmp('get').zero?
|
36
|
-
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' =>
|
36
|
+
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => referer_url)
|
37
37
|
end
|
38
38
|
|
39
39
|
def follow(method, path, **attributes)
|
40
40
|
return if fragment_or_script?(path)
|
41
41
|
|
42
|
-
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' =>
|
42
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => referer_url)
|
43
43
|
end
|
44
44
|
|
45
45
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
@@ -73,14 +73,18 @@ class Capybara::RackTest::Browser
|
|
73
73
|
end
|
74
74
|
|
75
75
|
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?('/')
|
76
|
+
uri = URI.parse(path)
|
77
|
+
base_uri = base_relative_uri_for(uri)
|
80
78
|
|
79
|
+
uri.path = base_uri.path + uri.path unless uri.absolute? || uri.path.start_with?('/')
|
80
|
+
|
81
|
+
if base_uri.absolute?
|
82
|
+
base_uri.merge(uri)
|
83
|
+
else
|
81
84
|
uri.scheme ||= @current_scheme
|
82
85
|
uri.host ||= @current_host
|
83
86
|
uri.port ||= @current_port unless uri.default_port == @current_port
|
87
|
+
uri
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
@@ -125,6 +129,25 @@ class Capybara::RackTest::Browser
|
|
125
129
|
|
126
130
|
protected
|
127
131
|
|
132
|
+
def base_href
|
133
|
+
find(:css, 'head > base').first&.[](:href).to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
def base_relative_uri_for(uri)
|
137
|
+
base_uri = URI.parse(base_href)
|
138
|
+
current_uri = URI.parse(safe_last_request&.url.to_s).tap do |c|
|
139
|
+
c.path.sub!(%r{/[^/]*$}, '/') unless uri.path.empty?
|
140
|
+
c.path = '/' if c.path.empty?
|
141
|
+
end
|
142
|
+
|
143
|
+
if [current_uri, base_uri].any?(&:absolute?)
|
144
|
+
current_uri.merge(base_uri)
|
145
|
+
else
|
146
|
+
base_uri.path = current_uri.path if base_uri.path.empty?
|
147
|
+
base_uri
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
128
151
|
def build_rack_mock_session
|
129
152
|
reset_host! unless current_host
|
130
153
|
Rack::MockSession.new(app, current_host)
|
@@ -136,9 +159,21 @@ protected
|
|
136
159
|
'/'
|
137
160
|
end
|
138
161
|
|
162
|
+
def safe_last_request
|
163
|
+
last_request
|
164
|
+
rescue Rack::Test::Error
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
|
139
168
|
private
|
140
169
|
|
141
170
|
def fragment_or_script?(path)
|
142
171
|
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
143
172
|
end
|
173
|
+
|
174
|
+
def referer_url
|
175
|
+
build_uri(last_request.url).to_s
|
176
|
+
rescue Rack::Test::Error
|
177
|
+
''
|
178
|
+
end
|
144
179
|
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)
|
@@ -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
|
#
|
data/lib/capybara/session.rb
CHANGED
@@ -765,33 +765,20 @@ module Capybara
|
|
765
765
|
end
|
766
766
|
|
767
767
|
NODE_METHODS.each do |method|
|
768
|
-
|
769
|
-
|
770
|
-
def #{method}(...)
|
771
|
-
@touched = true
|
772
|
-
current_scope.#{method}(...)
|
773
|
-
end
|
774
|
-
METHOD
|
775
|
-
else
|
776
|
-
define_method method do |*args, &block|
|
768
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
769
|
+
def #{method}(...)
|
777
770
|
@touched = true
|
778
|
-
current_scope
|
771
|
+
current_scope.#{method}(...)
|
779
772
|
end
|
780
|
-
|
773
|
+
METHOD
|
781
774
|
end
|
782
775
|
|
783
776
|
DOCUMENT_METHODS.each do |method|
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
document.#{method}(...)
|
788
|
-
end
|
789
|
-
METHOD
|
790
|
-
else
|
791
|
-
define_method method do |*args, &block|
|
792
|
-
document.send(method, *args, &block)
|
777
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
778
|
+
def #{method}(...)
|
779
|
+
document.#{method}(...)
|
793
780
|
end
|
794
|
-
|
781
|
+
METHOD
|
795
782
|
end
|
796
783
|
|
797
784
|
def inspect
|
@@ -132,7 +132,7 @@ Capybara::SpecHelper.spec '#all' do
|
|
132
132
|
it 'should use the sessions ignore_hidden_elements', psc: true do
|
133
133
|
Capybara.ignore_hidden_elements = true
|
134
134
|
@session.config.ignore_hidden_elements = false
|
135
|
-
expect(Capybara.ignore_hidden_elements).to
|
135
|
+
expect(Capybara.ignore_hidden_elements).to be(true)
|
136
136
|
expect(@session.all(:css, 'a.simple').size).to eq(2)
|
137
137
|
@session.config.ignore_hidden_elements = true
|
138
138
|
expect(@session.all(:css, 'a.simple').size).to eq(1)
|
@@ -208,12 +208,10 @@ Capybara::SpecHelper.spec '#all' do
|
|
208
208
|
expect { @session.all(:css, 'h1, p', between: 5..) }.to raise_error(Capybara::ExpectationNotMet)
|
209
209
|
end
|
210
210
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
end
|
216
|
-
TEST
|
211
|
+
it 'treats a beginless range as maximum' do
|
212
|
+
expect { @session.all(:css, 'h1, p', between: ..7) }.not_to raise_error
|
213
|
+
expect { @session.all(:css, 'h1, p', between: ..3) }.to raise_error(Capybara::ExpectationNotMet)
|
214
|
+
end
|
217
215
|
end
|
218
216
|
|
219
217
|
context 'with multiple count filters' do
|