capybara 3.38.0 → 3.39.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 -0
- data/lib/capybara/node/actions.rb +4 -4
- data/lib/capybara/node/finders.rb +2 -0
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/rack_test/node.rb +18 -15
- data/lib/capybara/registrations/drivers.rb +3 -3
- data/lib/capybara/registrations/servers.rb +16 -4
- data/lib/capybara/rspec/matcher_proxies.rb +3 -3
- data/lib/capybara/rspec/matchers/base.rb +8 -6
- data/lib/capybara/rspec/matchers/compound.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +2 -1
- data/lib/capybara/selenium/driver.rb +4 -4
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +8 -4
- data/lib/capybara/selenium/extensions/html5_drag.rb +3 -0
- data/lib/capybara/selenium/node.rb +8 -10
- data/lib/capybara/selenium/nodes/chrome_node.rb +5 -1
- data/lib/capybara/selenium/nodes/edge_node.rb +24 -2
- data/lib/capybara/selenium/patches/action_pauser.rb +3 -3
- data/lib/capybara/selenium/patches/atoms.rb +1 -1
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
- data/lib/capybara/server/animation_disabler.rb +2 -3
- data/lib/capybara/spec/public/test.js +4 -0
- data/lib/capybara/spec/session/all_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_spec.rb +11 -0
- data/lib/capybara/spec/session/find_link_spec.rb +10 -0
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +2 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +1 -1
- data/lib/capybara/spec/session/has_link_spec.rb +5 -1
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -0
- data/lib/capybara/spec/session/node_spec.rb +16 -0
- data/lib/capybara/spec/session/scroll_spec.rb +3 -1
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/views/form.erb +4 -0
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/css_builder_spec.rb +1 -1
- data/spec/css_splitter_spec.rb +1 -1
- data/spec/rack_test_spec.rb +2 -2
- data/spec/rspec/scenarios_spec.rb +1 -1
- data/spec/rspec_matchers_spec.rb +25 -0
- data/spec/sauce_spec_chrome.rb +1 -1
- data/spec/selenium_spec_chrome.rb +5 -6
- data/spec/selenium_spec_chrome_remote.rb +5 -7
- data/spec/selenium_spec_edge.rb +11 -7
- data/spec/selenium_spec_firefox.rb +10 -4
- data/spec/selenium_spec_firefox_remote.rb +16 -3
- data/spec/selenium_spec_ie.rb +1 -1
- data/spec/selenium_spec_safari.rb +1 -1
- data/spec/server_spec.rb +2 -2
- data/spec/shared_selenium_session.rb +2 -1
- data/spec/spec_helper.rb +33 -0
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab39ff6da58cbd3edd267cec393f613a79bbabd1092e7929ad32f352ee46c914
|
4
|
+
data.tar.gz: c61ad6d311f09fba5878e865133adfe5c8f16b65b3f1a184e2ef95655f7f7685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e449ab4b9d7750df25bc5a2c4afb0391f825fa27c2b0cd86b34f1b903245e28268b55265b4d9a066616bcd40ed3f598b96701873e83d7da1ebafb8eb6e8568cd
|
7
|
+
data.tar.gz: beeba4a6862e5e51c93cbf530e2353ac3d5862fe818f192c18411133a17b836e634495d8d58a8a7215c9f153ba3a56c92b7b506b951868012b8661b05eeb5381
|
data/History.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
# Version 3.39.0
|
2
|
+
Release date: 2023-04-02
|
3
|
+
|
4
|
+
### Added
|
5
|
+
|
6
|
+
* Support `:target` filter option on `:link` selector [Yudai Takada]
|
7
|
+
* Experimental Rack 3 support
|
8
|
+
* Text normalization performance improvements [Brandon Weaver]
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
* MS Edge button click [Brian J. Bayer]
|
13
|
+
* Options/Capabilities choosing based on Selenium versions
|
14
|
+
* Support for base versions [Matijs van Zuijlen]
|
15
|
+
* ExpectedError not defined in Selenium 4+
|
16
|
+
* Filter block forwarding to a number of matchers [Christophe Bliard]
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
* Dropped support for rack 1.x
|
20
|
+
|
1
21
|
# Version 3.38.0
|
2
22
|
Release date: 2022-11-03
|
3
23
|
|
@@ -383,7 +383,7 @@ module Capybara
|
|
383
383
|
end
|
384
384
|
end
|
385
385
|
|
386
|
-
UPDATE_STYLE_SCRIPT = <<~
|
386
|
+
UPDATE_STYLE_SCRIPT = <<~JS
|
387
387
|
this.capybara_style_cache = this.style.cssText;
|
388
388
|
var css = arguments[0];
|
389
389
|
for (var prop in css){
|
@@ -393,20 +393,20 @@ module Capybara
|
|
393
393
|
}
|
394
394
|
JS
|
395
395
|
|
396
|
-
RESET_STYLE_SCRIPT = <<~
|
396
|
+
RESET_STYLE_SCRIPT = <<~JS
|
397
397
|
if (this.hasOwnProperty('capybara_style_cache')) {
|
398
398
|
this.style.cssText = this.capybara_style_cache;
|
399
399
|
delete this.capybara_style_cache;
|
400
400
|
}
|
401
401
|
JS
|
402
402
|
|
403
|
-
DATALIST_OPTIONS_SCRIPT = <<~
|
403
|
+
DATALIST_OPTIONS_SCRIPT = <<~JS
|
404
404
|
Array.prototype.slice.call((this.list||{}).options || []).
|
405
405
|
filter(function(el){ return !el.disabled }).
|
406
406
|
map(function(el){ return { "value": el.value, "label": el.label} })
|
407
407
|
JS
|
408
408
|
|
409
|
-
CAPTURE_FILE_ELEMENT_SCRIPT = <<~
|
409
|
+
CAPTURE_FILE_ELEMENT_SCRIPT = <<~JS
|
410
410
|
document.addEventListener('click', function file_catcher(e){
|
411
411
|
if (e.target.matches("input[type='file']")) {
|
412
412
|
window._capybara_clicked_file_input = e.target;
|
@@ -149,6 +149,8 @@ module Capybara
|
|
149
149
|
# @option options [String, Regexp] id Match links with the id provided
|
150
150
|
# @option options [String] title Match links with the title provided
|
151
151
|
# @option options [String] alt Match links with a contained img element whose alt matches
|
152
|
+
# @option options [String, Boolean] download Match links with the download provided
|
153
|
+
# @option options [String] target Match links with the target provided
|
152
154
|
# @option options [String, Array<String>, Regexp] class Match links that match the class(es) provided
|
153
155
|
# @return [Capybara::Node::Element] The found element
|
154
156
|
#
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Node
|
5
|
+
##
|
6
|
+
#
|
7
|
+
# {Capybara::Node::WhitespaceNormalizer} provides methods that
|
8
|
+
# help to normalize the spacing of text content inside of
|
9
|
+
# {Capybara::Node::Element}s by removing various unicode
|
10
|
+
# spacing and directional markings.
|
11
|
+
#
|
12
|
+
module WhitespaceNormalizer
|
13
|
+
# Unicode for NBSP, or
|
14
|
+
NON_BREAKING_SPACE = "\u00a0"
|
15
|
+
LINE_SEPERATOR = "\u2028"
|
16
|
+
PARAGRAPH_SEPERATOR = "\u2029"
|
17
|
+
|
18
|
+
# All spaces except for NBSP
|
19
|
+
BREAKING_SPACES = "[[:space:]&&[^#{NON_BREAKING_SPACE}]]"
|
20
|
+
|
21
|
+
# Whitespace we want to substitute with plain spaces
|
22
|
+
SQUEEZED_SPACES = " \n\f\t\v#{LINE_SEPERATOR}#{PARAGRAPH_SEPERATOR}"
|
23
|
+
|
24
|
+
# Any whitespace at the front of text
|
25
|
+
LEADING_SPACES = /\A#{BREAKING_SPACES}+/.freeze
|
26
|
+
|
27
|
+
# Any whitespace at the end of text
|
28
|
+
TRAILING_SPACES = /#{BREAKING_SPACES}+\z/.freeze
|
29
|
+
|
30
|
+
# "Invisible" space character
|
31
|
+
ZERO_WIDTH_SPACE = "\u200b"
|
32
|
+
|
33
|
+
# Signifies text is read left to right
|
34
|
+
LEFT_TO_RIGHT_MARK = "\u200e"
|
35
|
+
|
36
|
+
# Signifies text is read right to left
|
37
|
+
RIGHT_TO_LEFT_MARK = "\u200f"
|
38
|
+
|
39
|
+
# Characters we want to truncate from text
|
40
|
+
REMOVED_CHARACTERS = [ZERO_WIDTH_SPACE, LEFT_TO_RIGHT_MARK, RIGHT_TO_LEFT_MARK].join
|
41
|
+
|
42
|
+
# Matches multiple empty lines
|
43
|
+
EMPTY_LINES = /[\ \n]*\n[\ \n]*/.freeze
|
44
|
+
|
45
|
+
##
|
46
|
+
#
|
47
|
+
# Normalizes the spacing of a node's text to be similar to
|
48
|
+
# what matchers might expect.
|
49
|
+
#
|
50
|
+
# @param text [String]
|
51
|
+
# @return [String]
|
52
|
+
#
|
53
|
+
def normalize_spacing(text)
|
54
|
+
text
|
55
|
+
.delete(REMOVED_CHARACTERS)
|
56
|
+
.tr(SQUEEZED_SPACES, ' ')
|
57
|
+
.squeeze(' ')
|
58
|
+
.sub(LEADING_SPACES, '')
|
59
|
+
.sub(TRAILING_SPACES, '')
|
60
|
+
.tr(NON_BREAKING_SPACE, ' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
#
|
65
|
+
# Variant on {Capybara::Node::Normalizer#normalize_spacing} that
|
66
|
+
# targets the whitespace of visible elements only.
|
67
|
+
#
|
68
|
+
# @param text [String]
|
69
|
+
# @return [String]
|
70
|
+
#
|
71
|
+
def normalize_visible_spacing(text)
|
72
|
+
text
|
73
|
+
.squeeze(' ')
|
74
|
+
.gsub(EMPTY_LINES, "\n")
|
75
|
+
.sub(LEADING_SPACES, '')
|
76
|
+
.sub(TRAILING_SPACES, '')
|
77
|
+
.tr(NON_BREAKING_SPACE, ' ')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,25 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'capybara/rack_test/errors'
|
4
|
+
require 'capybara/node/whitespace_normalizer'
|
4
5
|
|
5
6
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
7
|
+
include Capybara::Node::WhitespaceNormalizer
|
8
|
+
|
6
9
|
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
7
10
|
|
8
11
|
def all_text
|
9
|
-
native.text
|
10
|
-
.gsub(/[\u200b\u200e\u200f]/, '')
|
11
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
12
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
13
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
14
|
-
.tr("\u00a0", ' ')
|
12
|
+
normalize_spacing(native.text)
|
15
13
|
end
|
16
14
|
|
17
15
|
def visible_text
|
18
|
-
displayed_text
|
19
|
-
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
20
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
21
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
22
|
-
.tr("\u00a0", ' ')
|
16
|
+
normalize_visible_spacing(displayed_text)
|
23
17
|
end
|
24
18
|
|
25
19
|
def [](name)
|
@@ -153,9 +147,11 @@ protected
|
|
153
147
|
if !string_node.visible?(check_ancestor)
|
154
148
|
''
|
155
149
|
elsif native.text?
|
156
|
-
native
|
157
|
-
|
158
|
-
|
150
|
+
native
|
151
|
+
.text
|
152
|
+
.delete(REMOVED_CHARACTERS)
|
153
|
+
.tr(SQUEEZED_SPACES, ' ')
|
154
|
+
.squeeze(' ')
|
159
155
|
elsif native.element?
|
160
156
|
text = native.children.map do |child|
|
161
157
|
Capybara::RackTest::Node.new(driver, child).displayed_text(check_ancestor: false)
|
@@ -235,7 +231,14 @@ private
|
|
235
231
|
end
|
236
232
|
native.remove
|
237
233
|
else
|
238
|
-
|
234
|
+
value.to_s.tap do |set_value|
|
235
|
+
if set_value.end_with?("\n") && form&.css('input, textarea')&.count
|
236
|
+
native['value'] = set_value.to_s.chop
|
237
|
+
Capybara::RackTest::Form.new(driver, form).submit(self)
|
238
|
+
else
|
239
|
+
native['value'] = set_value
|
240
|
+
end
|
241
|
+
end
|
239
242
|
end
|
240
243
|
end
|
241
244
|
|
@@ -11,7 +11,7 @@ end
|
|
11
11
|
Capybara.register_driver :selenium_headless do |app|
|
12
12
|
version = Capybara::Selenium::Driver.load_selenium
|
13
13
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
14
|
-
browser_options =
|
14
|
+
browser_options = Selenium::WebDriver::Firefox::Options.new.tap do |opts|
|
15
15
|
opts.add_argument '-headless'
|
16
16
|
end
|
17
17
|
Capybara::Selenium::Driver.new(app, **{ :browser => :firefox, options_key => browser_options })
|
@@ -20,7 +20,7 @@ end
|
|
20
20
|
Capybara.register_driver :selenium_chrome do |app|
|
21
21
|
version = Capybara::Selenium::Driver.load_selenium
|
22
22
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
23
|
-
browser_options =
|
23
|
+
browser_options = Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
24
24
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
25
25
|
opts.add_argument('--disable-site-isolation-trials')
|
26
26
|
end
|
@@ -31,7 +31,7 @@ end
|
|
31
31
|
Capybara.register_driver :selenium_chrome_headless do |app|
|
32
32
|
version = Capybara::Selenium::Driver.load_selenium
|
33
33
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
34
|
-
browser_options =
|
34
|
+
browser_options = Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
35
35
|
opts.add_argument('--headless')
|
36
36
|
opts.add_argument('--disable-gpu') if Gem.win_platform?
|
37
37
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
@@ -5,12 +5,24 @@ Capybara.register_server :default do |app, port, _host|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
Capybara.register_server :webrick do |app, port, host, **options|
|
8
|
-
|
8
|
+
base_class = begin
|
9
|
+
require 'rack/handler/webrick'
|
10
|
+
Rack
|
11
|
+
rescue LoadError
|
12
|
+
# Rack 3 separated out the webrick handle - no way test currently in Capybaras automated
|
13
|
+
# tests due to Sinatra not yet supporting Rack 3 - experimental
|
14
|
+
require 'rackup/handler/webrick'
|
15
|
+
Rackup
|
16
|
+
end
|
9
17
|
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
|
10
|
-
|
18
|
+
base_class::Handler::WEBrick.run(app, **options)
|
11
19
|
end
|
12
20
|
|
13
21
|
Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable Metrics/BlockLength
|
22
|
+
begin
|
23
|
+
require 'rackup'
|
24
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
25
|
+
end
|
14
26
|
begin
|
15
27
|
require 'rack/handler/puma'
|
16
28
|
rescue LoadError
|
@@ -33,7 +45,7 @@ Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable
|
|
33
45
|
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
34
46
|
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
35
47
|
|
36
|
-
logger = (defined?(
|
48
|
+
logger = (defined?(Puma::LogWriter) ? Puma::LogWriter : Puma::Events).then do |cls|
|
37
49
|
conf.options[:Silent] ? cls.strings : cls.stdio
|
38
50
|
end
|
39
51
|
conf.options[:log_writer] = logger
|
@@ -44,7 +56,7 @@ Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable
|
|
44
56
|
|
45
57
|
Puma::Server.new(
|
46
58
|
conf.app,
|
47
|
-
defined?(
|
59
|
+
defined?(Puma::LogWriter) ? nil : logger,
|
48
60
|
conf.options
|
49
61
|
).tap do |s|
|
50
62
|
s.binder.parse conf.options[:binds], (s.log_writer rescue s.events) # rubocop:disable Style/RescueModifier
|
@@ -36,7 +36,7 @@ if RUBY_ENGINE == 'jruby'
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
if defined?(
|
39
|
+
if defined?(RSpec::Matchers)
|
40
40
|
module ::RSpec::Matchers
|
41
41
|
def self.included(base)
|
42
42
|
base.send(:include, ::Capybara::RSpecMatcherProxies) if base.include?(::Capybara::DSL)
|
@@ -76,7 +76,7 @@ else
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
Capybara::DSL.prepend
|
79
|
+
Capybara::DSL.prepend Capybara::DSLRSpecProxyInstaller
|
80
80
|
|
81
|
-
|
81
|
+
RSpec::Matchers.prepend Capybara::RSpecMatcherProxyInstaller if defined?(RSpec::Matchers)
|
82
82
|
end
|
@@ -47,14 +47,16 @@ module Capybara
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class WrappedElementMatcher < Base
|
50
|
-
def matches?(actual)
|
50
|
+
def matches?(actual, &filter_block)
|
51
|
+
@filter_block ||= filter_block
|
51
52
|
element_matches?(wrap(actual))
|
52
53
|
rescue Capybara::ExpectationNotMet => e
|
53
54
|
@failure_message = e.message
|
54
55
|
false
|
55
56
|
end
|
56
57
|
|
57
|
-
def does_not_match?(actual)
|
58
|
+
def does_not_match?(actual, &filter_block)
|
59
|
+
@filter_block ||= filter_block
|
58
60
|
element_does_not_match?(wrap(actual))
|
59
61
|
rescue Capybara::ExpectationNotMet => e
|
60
62
|
@failure_message_when_negated = e.message
|
@@ -86,12 +88,12 @@ module Capybara
|
|
86
88
|
@matcher = matcher
|
87
89
|
end
|
88
90
|
|
89
|
-
def matches?(actual)
|
90
|
-
@matcher.does_not_match?(actual)
|
91
|
+
def matches?(actual, &filter_block)
|
92
|
+
@matcher.does_not_match?(actual, &filter_block)
|
91
93
|
end
|
92
94
|
|
93
|
-
def does_not_match?(actual)
|
94
|
-
@matcher.matches?(actual)
|
95
|
+
def does_not_match?(actual, &filter_block)
|
96
|
+
@matcher.matches?(actual, &filter_block)
|
95
97
|
end
|
96
98
|
|
97
99
|
def description
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
4
|
-
xpath do |locator, href: true, alt: nil, title: nil, **|
|
4
|
+
xpath do |locator, href: true, alt: nil, title: nil, target: nil, **|
|
5
5
|
xpath = XPath.descendant(:a)
|
6
6
|
xpath = builder(xpath).add_attribute_conditions(href: href) unless href == false
|
7
7
|
|
@@ -25,6 +25,7 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
25
25
|
|
26
26
|
xpath = xpath[find_by_attr(:title, title)]
|
27
27
|
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
|
28
|
+
xpath = xpath[find_by_attr(:target, target)] if target
|
28
29
|
|
29
30
|
xpath
|
30
31
|
end
|
@@ -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', '< 4.8.0')
|
16
16
|
|
17
17
|
attr_reader :app, :options
|
18
18
|
|
@@ -315,16 +315,16 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
315
315
|
].tap do |errors|
|
316
316
|
unless selenium_4?
|
317
317
|
::Selenium::WebDriver.logger.suppress_deprecations do
|
318
|
-
errors.
|
318
|
+
errors.push(
|
319
319
|
::Selenium::WebDriver::Error::UnhandledError,
|
320
320
|
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
321
321
|
::Selenium::WebDriver::Error::InvalidElementStateError,
|
322
322
|
::Selenium::WebDriver::Error::ElementNotSelectableError
|
323
|
-
|
323
|
+
)
|
324
324
|
end
|
325
325
|
end
|
326
326
|
if defined?(::Selenium::WebDriver::Error::DetachedShadowRootError)
|
327
|
-
errors.
|
327
|
+
errors.push(::Selenium::WebDriver::Error::DetachedShadowRootError)
|
328
328
|
end
|
329
329
|
end
|
330
330
|
end
|
@@ -103,9 +103,13 @@ private
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def execute_cdp(cmd, params = {})
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
if browser.respond_to? :execute_cdp
|
107
|
+
browser.execute_cdp(cmd, **params)
|
108
|
+
else
|
109
|
+
args = { cmd: cmd, params: params }
|
110
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/ms/cdp/execute", args)
|
111
|
+
result['value']
|
112
|
+
end
|
109
113
|
end
|
110
114
|
|
111
115
|
def build_node(native_node, initial_cache = {})
|
@@ -115,7 +119,7 @@ private
|
|
115
119
|
def edgedriver_version
|
116
120
|
@edgedriver_version ||= begin
|
117
121
|
caps = browser.capabilities
|
118
|
-
caps['
|
122
|
+
caps['msedge']&.fetch('msedgedriverVersion', nil).to_f
|
119
123
|
end
|
120
124
|
end
|
121
125
|
end
|
@@ -166,6 +166,9 @@ class Capybara::Selenium::Node
|
|
166
166
|
opts[key + 'Key'] = true;
|
167
167
|
}
|
168
168
|
|
169
|
+
var dragEnterEvent = new DragEvent('dragenter', opts);
|
170
|
+
target.dispatchEvent(dragEnterEvent);
|
171
|
+
|
169
172
|
// fire 2 dragover events to simulate dragging with a direction
|
170
173
|
var entryPoint = pointOnRect(sourceCenter, targetRect)
|
171
174
|
var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
|
@@ -4,8 +4,10 @@
|
|
4
4
|
|
5
5
|
require 'capybara/selenium/extensions/find'
|
6
6
|
require 'capybara/selenium/extensions/scroll'
|
7
|
+
require 'capybara/node/whitespace_normalizer'
|
7
8
|
|
8
9
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
10
|
+
include Capybara::Node::WhitespaceNormalizer
|
9
11
|
include Capybara::Selenium::Find
|
10
12
|
include Capybara::Selenium::Scroll
|
11
13
|
|
@@ -17,11 +19,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
17
19
|
|
18
20
|
def all_text
|
19
21
|
text = driver.evaluate_script('arguments[0].textContent', self) || ''
|
20
|
-
text
|
21
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
22
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
23
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
24
|
-
.tr("\u00a0", ' ')
|
22
|
+
normalize_spacing(text)
|
25
23
|
end
|
26
24
|
|
27
25
|
def [](name)
|
@@ -221,7 +219,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
221
219
|
end
|
222
220
|
|
223
221
|
def shadow_root
|
224
|
-
|
222
|
+
raise 'You must be using Selenium 4.1+ for shadow_root support' unless native.respond_to? :shadow_root
|
225
223
|
|
226
224
|
root = native.shadow_root
|
227
225
|
root && build_node(native.shadow_root)
|
@@ -237,7 +235,7 @@ protected
|
|
237
235
|
end
|
238
236
|
|
239
237
|
def scroll_to_center
|
240
|
-
script = <<-
|
238
|
+
script = <<-JS
|
241
239
|
try {
|
242
240
|
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
243
241
|
} catch(e) {
|
@@ -520,7 +518,7 @@ private
|
|
520
518
|
def attrs(*attr_names)
|
521
519
|
return attr_names.map { |name| self[name.to_s] } if ENV['CAPYBARA_THOROUGH']
|
522
520
|
|
523
|
-
driver.evaluate_script <<~
|
521
|
+
driver.evaluate_script <<~JS, self, attr_names.map(&:to_s)
|
524
522
|
(function(el, names){
|
525
523
|
return names.map(function(name){
|
526
524
|
return el[name]
|
@@ -571,7 +569,7 @@ private
|
|
571
569
|
})(arguments[0], document)
|
572
570
|
JS
|
573
571
|
|
574
|
-
OBSCURED_OR_OFFSET_SCRIPT = <<~
|
572
|
+
OBSCURED_OR_OFFSET_SCRIPT = <<~JS
|
575
573
|
(function(el, x, y) {
|
576
574
|
var box = el.getBoundingClientRect();
|
577
575
|
if (x == null) x = box.width/2;
|
@@ -588,7 +586,7 @@ private
|
|
588
586
|
})(arguments[0], arguments[1], arguments[2])
|
589
587
|
JS
|
590
588
|
|
591
|
-
RAPID_APPEND_TEXT = <<~
|
589
|
+
RAPID_APPEND_TEXT = <<~JS
|
592
590
|
(function(el, value) {
|
593
591
|
value = el.value + value;
|
594
592
|
if (el.maxLength && el.maxLength != -1){
|
@@ -106,7 +106,11 @@ private
|
|
106
106
|
|
107
107
|
def file_errors
|
108
108
|
@file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
|
109
|
-
|
109
|
+
if defined? ::Selenium::WebDriver::Error::ExpectedError # Selenium < 4
|
110
|
+
[::Selenium::WebDriver::Error::ExpectedError]
|
111
|
+
else
|
112
|
+
[]
|
113
|
+
end
|
110
114
|
end
|
111
115
|
end
|
112
116
|
|
@@ -38,7 +38,7 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
38
38
|
html5_drop(*args)
|
39
39
|
end
|
40
40
|
|
41
|
-
def click(
|
41
|
+
def click(*, **)
|
42
42
|
super
|
43
43
|
rescue Selenium::WebDriver::Error::InvalidArgumentError => e
|
44
44
|
tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
|
@@ -77,11 +77,33 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
def send_keys(*args)
|
81
|
+
args.chunk { |inp| inp.is_a?(String) && inp.match?(/\p{Emoji Presentation}/) }
|
82
|
+
.each do |contains_emoji, inputs|
|
83
|
+
if contains_emoji
|
84
|
+
inputs.join.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
|
85
|
+
.each do |emoji, clusters|
|
86
|
+
if emoji
|
87
|
+
driver.send(:execute_cdp, 'Input.insertText', text: clusters.join)
|
88
|
+
else
|
89
|
+
super(clusters.join)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
super(*inputs)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
80
98
|
private
|
81
99
|
|
82
100
|
def file_errors
|
83
101
|
@file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
|
84
|
-
|
102
|
+
if defined? ::Selenium::WebDriver::Error::ExpectedError # Selenium < 4
|
103
|
+
[::Selenium::WebDriver::Error::ExpectedError]
|
104
|
+
else
|
105
|
+
[]
|
106
|
+
end
|
85
107
|
end
|
86
108
|
end
|
87
109
|
|
@@ -20,7 +20,7 @@ module ActionPauser
|
|
20
20
|
private_constant :Pauser
|
21
21
|
end
|
22
22
|
|
23
|
-
if defined?(
|
24
|
-
defined?(
|
25
|
-
|
23
|
+
if defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f < 4) &&
|
24
|
+
defined?(Selenium::WebDriver::ActionBuilder)
|
25
|
+
Selenium::WebDriver::ActionBuilder.prepend(ActionPauser)
|
26
26
|
end
|
@@ -18,8 +18,7 @@ module Capybara
|
|
18
18
|
@app = app
|
19
19
|
@disable_css_markup = format(DISABLE_CSS_MARKUP_TEMPLATE,
|
20
20
|
selector: self.class.selector_for(Capybara.disable_animation))
|
21
|
-
@disable_js_markup =
|
22
|
-
selector: self.class.selector_for(Capybara.disable_animation))
|
21
|
+
@disable_js_markup = +DISABLE_JS_MARKUP_TEMPLATE
|
23
22
|
end
|
24
23
|
|
25
24
|
def call(env)
|
@@ -40,7 +39,7 @@ module Capybara
|
|
40
39
|
attr_reader :disable_css_markup, :disable_js_markup
|
41
40
|
|
42
41
|
def html_content?(headers)
|
43
|
-
/html/.match?(headers['Content-Type'])
|
42
|
+
/html/.match?(headers['Content-Type']) # rubocop:todo Performance/StringInclude
|
44
43
|
end
|
45
44
|
|
46
45
|
def insert_disable(html, nonces)
|
@@ -44,6 +44,10 @@ $(function() {
|
|
44
44
|
$(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
|
45
45
|
if ($(this).hasClass('drop')) { ev.preventDefault(); }
|
46
46
|
});
|
47
|
+
$('#drop_html5, #drop_html5_scroll').on('dragenter', function(ev){
|
48
|
+
$(this).after('<div class="log">DragEnter')
|
49
|
+
if ($(this).hasClass('drop')) { ev.preventDefault(); }
|
50
|
+
});
|
47
51
|
$('#drop_html5, #drop_html5_scroll').on('dragleave', function(ev){
|
48
52
|
$(this).after('<div class="log">DragLeave with client position: ' + ev.clientX + ',' + ev.clientY)
|
49
53
|
if ($(this).hasClass('drop')) { ev.preventDefault(); }
|
@@ -31,7 +31,7 @@ Capybara::SpecHelper.spec '#all' do
|
|
31
31
|
it 'should accept an XPath instance', :exact_false do
|
32
32
|
@session.visit('/form')
|
33
33
|
@xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('Name')
|
34
|
-
expect(@xpath).to be_a(
|
34
|
+
expect(@xpath).to be_a(XPath::Union)
|
35
35
|
@result = @session.all(@xpath).map(&:value)
|
36
36
|
expect(@result).to include('Smith', 'John', 'John Smith')
|
37
37
|
end
|
@@ -131,6 +131,17 @@ Capybara::SpecHelper.spec '#click_link' do
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
+
context 'with :target option given' do
|
135
|
+
it 'should find links with valid target' do
|
136
|
+
@session.click_link('labore', target: '_self')
|
137
|
+
expect(@session).to have_content('Bar')
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should raise error if link wasn't found" do
|
141
|
+
expect { @session.click_link('labore', target: '_blank') }.to raise_error(Capybara::ElementNotFound, /Unable to find link "labore"/)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
134
145
|
it 'should follow relative links' do
|
135
146
|
@session.visit('/')
|
136
147
|
@session.click_link('Relative')
|