capybara 3.38.0 → 3.39.0
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 +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')
|