capybara 3.31.0 → 3.34.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 +93 -13
- data/README.md +9 -4
- data/lib/capybara.rb +18 -8
- data/lib/capybara/config.rb +4 -6
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/helpers.rb +25 -1
- data/lib/capybara/minitest.rb +215 -141
- data/lib/capybara/minitest/spec.rb +156 -97
- data/lib/capybara/node/actions.rb +16 -21
- data/lib/capybara/node/element.rb +3 -5
- data/lib/capybara/node/finders.rb +7 -6
- data/lib/capybara/node/matchers.rb +11 -11
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/current_path_query.rb +14 -4
- data/lib/capybara/queries/selector_query.rb +16 -10
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +7 -1
- data/lib/capybara/rack_test/browser.rb +9 -3
- data/lib/capybara/rack_test/driver.rb +1 -0
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/registration_container.rb +44 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
- data/lib/capybara/registrations/servers.rb +2 -1
- data/lib/capybara/result.rb +8 -8
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +5 -5
- data/lib/capybara/rspec/matchers.rb +7 -6
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/have_text.rb +1 -1
- data/lib/capybara/rspec/matchers/match_style.rb +5 -0
- data/lib/capybara/selector.rb +10 -1
- data/lib/capybara/selector/definition.rb +11 -9
- data/lib/capybara/selector/definition/button.rb +8 -5
- data/lib/capybara/selector/definition/css.rb +1 -1
- data/lib/capybara/selector/definition/datalist_input.rb +1 -1
- data/lib/capybara/selector/definition/element.rb +2 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/label.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/select.rb +1 -1
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +1 -1
- data/lib/capybara/selector/filter_set.rb +2 -2
- data/lib/capybara/selector/selector.rb +9 -1
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
- data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
- data/lib/capybara/selenium/driver.rb +35 -6
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
- data/lib/capybara/selenium/extensions/find.rb +3 -3
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/node.rb +75 -12
- data/lib/capybara/selenium/nodes/chrome_node.rb +20 -11
- data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
- data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
- data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
- data/lib/capybara/selenium/patches/atoms.rb +4 -4
- data/lib/capybara/selenium/patches/logs.rb +7 -9
- data/lib/capybara/server/animation_disabler.rb +3 -2
- data/lib/capybara/server/middleware.rb +4 -2
- data/lib/capybara/session.rb +23 -14
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/test.js +24 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/check_spec.rb +6 -0
- data/lib/capybara/spec/session/click_button_spec.rb +11 -0
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +9 -0
- data/lib/capybara/spec/session/find_spec.rb +11 -8
- data/lib/capybara/spec/session/has_button_spec.rb +18 -0
- data/lib/capybara/spec/session/has_css_spec.rb +11 -7
- data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
- data/lib/capybara/spec/session/has_field_spec.rb +16 -0
- data/lib/capybara/spec/session/has_select_spec.rb +4 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
- data/lib/capybara/spec/session/has_text_spec.rb +0 -11
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
- data/lib/capybara/spec/session/node_spec.rb +76 -29
- data/lib/capybara/spec/session/refresh_spec.rb +1 -0
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/session/window/window_spec.rb +7 -7
- data/lib/capybara/spec/spec_helper.rb +13 -14
- data/lib/capybara/spec/test_app.rb +22 -21
- data/lib/capybara/spec/views/form.erb +25 -1
- data/lib/capybara/spec/views/with_animation.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +3 -1
- data/lib/capybara/spec/views/with_html.erb +2 -2
- data/lib/capybara/spec/views/with_js.erb +2 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +3 -7
- data/spec/basic_node_spec.rb +9 -8
- data/spec/capybara_spec.rb +1 -1
- data/spec/dsl_spec.rb +14 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +3 -2
- data/spec/rack_test_spec.rb +28 -5
- data/spec/regexp_dissassembler_spec.rb +0 -4
- data/spec/result_spec.rb +38 -31
- data/spec/rspec/features_spec.rb +3 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +63 -51
- data/spec/rspec_spec.rb +4 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +4 -2
- data/spec/selenium_spec_chrome_remote.rb +2 -0
- data/spec/server_spec.rb +56 -49
- data/spec/shared_selenium_node.rb +18 -0
- data/spec/shared_selenium_session.rb +84 -7
- data/spec/spec_helper.rb +1 -1
- metadata +25 -24
- data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -7,27 +7,25 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
7
7
|
def self.extended(base)
|
8
8
|
bridge = base.send(:bridge)
|
9
9
|
bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
|
10
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
10
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
11
11
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
12
12
|
end
|
13
13
|
|
14
14
|
def fullscreen_window(handle)
|
15
15
|
within_given_window(handle) do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
result['value']
|
23
|
-
end
|
16
|
+
super
|
17
|
+
rescue NoMethodError => e
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
19
|
+
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
|
+
result['value']
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
def resize_window_to(handle, width, height)
|
28
26
|
super
|
29
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
30
|
-
raise unless e.message.
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
31
29
|
|
32
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
33
31
|
# and raises unnecessary error. Wait a bit and try again.
|
@@ -65,7 +63,7 @@ private
|
|
65
63
|
end
|
66
64
|
|
67
65
|
def clear_all_storage?
|
68
|
-
storage_clears.none?
|
66
|
+
storage_clears.none? false
|
69
67
|
end
|
70
68
|
|
71
69
|
def uniform_storage_clear?
|
@@ -5,7 +5,7 @@ require 'capybara/selenium/nodes/edge_node'
|
|
5
5
|
module Capybara::Selenium::Driver::EdgeDriver
|
6
6
|
def self.extended(base)
|
7
7
|
bridge = base.send(:bridge)
|
8
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
8
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
9
9
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
10
10
|
end
|
11
11
|
|
@@ -13,21 +13,19 @@ module Capybara::Selenium::Driver::EdgeDriver
|
|
13
13
|
return super if edgedriver_version < 75
|
14
14
|
|
15
15
|
within_given_window(handle) do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
result['value']
|
23
|
-
end
|
16
|
+
super
|
17
|
+
rescue NoMethodError => e
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
19
|
+
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
|
+
result['value']
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
def resize_window_to(handle, width, height)
|
28
26
|
super
|
29
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
30
|
-
raise unless e.message.
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
31
29
|
|
32
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
33
31
|
# and raises unnecessary error. Wait a bit and try again.
|
@@ -74,7 +72,7 @@ private
|
|
74
72
|
end
|
75
73
|
|
76
74
|
def clear_all_storage?
|
77
|
-
storage_clears.none?
|
75
|
+
storage_clears.none? false
|
78
76
|
end
|
79
77
|
|
80
78
|
def uniform_storage_clear?
|
@@ -6,7 +6,7 @@ module Capybara::Selenium::Driver::FirefoxDriver
|
|
6
6
|
def self.extended(driver)
|
7
7
|
driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver if w3c?(driver)
|
8
8
|
bridge = driver.send(:bridge)
|
9
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
9
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.w3c?(driver)
|
@@ -46,7 +46,7 @@ module Capybara::Selenium::Driver::W3CFirefoxDriver
|
|
46
46
|
begin
|
47
47
|
# Firefox 68 hangs if we try to switch windows while a modal is visible
|
48
48
|
browser.switch_to.alert&.dismiss
|
49
|
-
rescue Selenium::WebDriver::Error::NoSuchAlertError
|
49
|
+
rescue Selenium::WebDriver::Error::NoSuchAlertError
|
50
50
|
# Swallow
|
51
51
|
end
|
52
52
|
end
|
@@ -61,7 +61,7 @@ module Capybara::Selenium::Driver::W3CFirefoxDriver
|
|
61
61
|
accept_modal :confirm, wait: 0.1 do
|
62
62
|
super
|
63
63
|
end
|
64
|
-
rescue Capybara::ModalNotFound
|
64
|
+
rescue Capybara::ModalNotFound
|
65
65
|
# No modal was opened - page has refreshed - ignore
|
66
66
|
end
|
67
67
|
|
@@ -100,9 +100,9 @@ module Capybara
|
|
100
100
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
101
101
|
@@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
|
102
102
|
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
rescue StandardError
|
104
|
+
# If the atom doesn't exist or other error
|
105
|
+
''
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -45,20 +45,18 @@ module Capybara
|
|
45
45
|
JS
|
46
46
|
end
|
47
47
|
|
48
|
+
SCROLL_POSITIONS = {
|
49
|
+
top: '0',
|
50
|
+
bottom: 'arguments[0].scrollHeight',
|
51
|
+
center: '(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
|
52
|
+
}.freeze
|
53
|
+
|
48
54
|
def scroll_to_location(location)
|
49
|
-
scroll_y = case location
|
50
|
-
when :top
|
51
|
-
'0'
|
52
|
-
when :bottom
|
53
|
-
'arguments[0].scrollHeight'
|
54
|
-
when :center
|
55
|
-
'(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
|
56
|
-
end
|
57
55
|
driver.execute_script <<~JS, self
|
58
56
|
if (arguments[0].scrollTo){
|
59
|
-
arguments[0].scrollTo(0, #{
|
57
|
+
arguments[0].scrollTo(0, #{SCROLL_POSITIONS[location]});
|
60
58
|
} else {
|
61
|
-
arguments[0].scrollTop = #{
|
59
|
+
arguments[0].scrollTop = #{SCROLL_POSITIONS[location]};
|
62
60
|
}
|
63
61
|
JS
|
64
62
|
end
|
@@ -104,10 +104,23 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
104
104
|
click_options = ClickOptions.new(keys, options)
|
105
105
|
return native.click if click_options.empty?
|
106
106
|
|
107
|
-
|
107
|
+
perform_with_options(click_options) do |action|
|
108
|
+
target = click_options.coords? ? nil : native
|
109
|
+
if click_options.delay.zero?
|
110
|
+
action.click(target)
|
111
|
+
else
|
112
|
+
action.click_and_hold(target)
|
113
|
+
if w3c?
|
114
|
+
action.pause(action.pointer_inputs.first, click_options.delay)
|
115
|
+
else
|
116
|
+
action.pause(click_options.delay)
|
117
|
+
end
|
118
|
+
action.release
|
119
|
+
end
|
120
|
+
end
|
108
121
|
rescue StandardError => e
|
109
122
|
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
110
|
-
e.message.
|
123
|
+
e.message.include?('Other element would receive the click')
|
111
124
|
scroll_to_center
|
112
125
|
end
|
113
126
|
|
@@ -116,14 +129,26 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
116
129
|
|
117
130
|
def right_click(keys = [], **options)
|
118
131
|
click_options = ClickOptions.new(keys, options)
|
119
|
-
|
120
|
-
click_options.coords? ?
|
132
|
+
perform_with_options(click_options) do |action|
|
133
|
+
target = click_options.coords? ? nil : native
|
134
|
+
if click_options.delay.zero?
|
135
|
+
action.context_click(target)
|
136
|
+
elsif w3c?
|
137
|
+
action.move_to(target) if target
|
138
|
+
action.pointer_down(:right)
|
139
|
+
.pause(action.pointer_inputs.first, click_options.delay)
|
140
|
+
.pointer_up(:right)
|
141
|
+
else
|
142
|
+
raise ArgumentError, 'Delay is not supported when right clicking with legacy (non-w3c) selenium driver'
|
143
|
+
end
|
121
144
|
end
|
122
145
|
end
|
123
146
|
|
124
147
|
def double_click(keys = [], **options)
|
125
148
|
click_options = ClickOptions.new(keys, options)
|
126
|
-
|
149
|
+
raise ArgumentError, "double_click doesn't support a delay option" unless click_options.delay.zero?
|
150
|
+
|
151
|
+
perform_with_options(click_options) do |action|
|
127
152
|
click_options.coords? ? action.double_click : action.double_click(native)
|
128
153
|
end
|
129
154
|
end
|
@@ -212,7 +237,7 @@ protected
|
|
212
237
|
JS
|
213
238
|
begin
|
214
239
|
driver.execute_script(script, self)
|
215
|
-
rescue StandardError
|
240
|
+
rescue StandardError
|
216
241
|
# Swallow error if scrollIntoView with options isn't supported
|
217
242
|
end
|
218
243
|
end
|
@@ -242,7 +267,7 @@ private
|
|
242
267
|
find_xpath(XPath.ancestor(:select)[1]).first
|
243
268
|
end
|
244
269
|
|
245
|
-
def set_text(value, clear: nil, **_unused)
|
270
|
+
def set_text(value, clear: nil, rapid: nil, **_unused)
|
246
271
|
value = value.to_s
|
247
272
|
if value.empty? && clear.nil?
|
248
273
|
native.clear
|
@@ -254,11 +279,23 @@ private
|
|
254
279
|
send_keys(*clear, value)
|
255
280
|
else
|
256
281
|
driver.execute_script 'arguments[0].select()', self unless clear == :none
|
257
|
-
|
282
|
+
if rapid == true || ((value.length > auto_rapid_set_length) && rapid != false)
|
283
|
+
send_keys(value[0..3])
|
284
|
+
driver.execute_script RAPID_APPEND_TEXT, self, value[4...-3]
|
285
|
+
send_keys(value[-3..-1])
|
286
|
+
else
|
287
|
+
send_keys(value)
|
288
|
+
end
|
258
289
|
end
|
259
290
|
end
|
260
291
|
|
261
|
-
def
|
292
|
+
def auto_rapid_set_length
|
293
|
+
30
|
294
|
+
end
|
295
|
+
|
296
|
+
def perform_with_options(click_options, &block)
|
297
|
+
raise ArgumentError, 'A block must be provided' unless block
|
298
|
+
|
262
299
|
scroll_if_needed do
|
263
300
|
action_with_modifiers(click_options) do |action|
|
264
301
|
if block_given?
|
@@ -407,6 +444,15 @@ private
|
|
407
444
|
browser.action
|
408
445
|
end
|
409
446
|
|
447
|
+
def capabilities
|
448
|
+
browser.capabilities
|
449
|
+
end
|
450
|
+
|
451
|
+
def w3c?
|
452
|
+
(defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)) ||
|
453
|
+
capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
454
|
+
end
|
455
|
+
|
410
456
|
def normalize_keys(keys)
|
411
457
|
keys.map do |key|
|
412
458
|
case key
|
@@ -418,8 +464,8 @@ private
|
|
418
464
|
end
|
419
465
|
end
|
420
466
|
|
421
|
-
def each_key(keys)
|
422
|
-
normalize_keys(keys).each
|
467
|
+
def each_key(keys, &block)
|
468
|
+
normalize_keys(keys).each(&block)
|
423
469
|
end
|
424
470
|
|
425
471
|
def find_context
|
@@ -447,6 +493,9 @@ private
|
|
447
493
|
var xpath = '';
|
448
494
|
var pos, tempitem2;
|
449
495
|
|
496
|
+
if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
|
497
|
+
return "(: Shadow DOM element - no XPath :)";
|
498
|
+
};
|
450
499
|
while(el !== xml.documentElement) {
|
451
500
|
pos = 0;
|
452
501
|
tempitem2 = el;
|
@@ -488,6 +537,16 @@ private
|
|
488
537
|
})(arguments[0], arguments[1], arguments[2])
|
489
538
|
JS
|
490
539
|
|
540
|
+
RAPID_APPEND_TEXT = <<~'JS'
|
541
|
+
(function(el, value) {
|
542
|
+
value = el.value + value;
|
543
|
+
if (el.maxLength && el.maxLength != -1){
|
544
|
+
value = value.slice(0, el.maxLength);
|
545
|
+
}
|
546
|
+
el.value = value;
|
547
|
+
})(arguments[0], arguments[1])
|
548
|
+
JS
|
549
|
+
|
491
550
|
# SettableValue encapsulates time/date field formatting
|
492
551
|
class SettableValue
|
493
552
|
attr_reader :value
|
@@ -544,7 +603,11 @@ private
|
|
544
603
|
end
|
545
604
|
|
546
605
|
def empty?
|
547
|
-
keys.empty? && !coords?
|
606
|
+
keys.empty? && !coords? && delay.zero?
|
607
|
+
end
|
608
|
+
|
609
|
+
def delay
|
610
|
+
options[:delay] || 0
|
548
611
|
end
|
549
612
|
end
|
550
613
|
private_constant :ClickOptions
|
@@ -42,7 +42,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
42
42
|
raise
|
43
43
|
rescue ::Selenium::WebDriver::Error::WebDriverError => e
|
44
44
|
# chromedriver 74 (at least on mac) raises the wrong error for this
|
45
|
-
if e.message.
|
45
|
+
if e.message.include?('element click intercepted')
|
46
46
|
raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message
|
47
47
|
end
|
48
48
|
|
@@ -73,6 +73,24 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
def send_keys(*args)
|
77
|
+
args.chunk { |inp| inp.is_a?(String) && inp.match?(/\p{Emoji Presentation}/) }
|
78
|
+
.each do |contains_emoji, inputs|
|
79
|
+
if contains_emoji
|
80
|
+
inputs.join.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
|
81
|
+
.each do |emoji, clusters|
|
82
|
+
if emoji
|
83
|
+
driver.send(:execute_cdp, 'Input.insertText', text: clusters.join)
|
84
|
+
else
|
85
|
+
super(clusters.join)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
super(*inputs)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
76
94
|
private
|
77
95
|
|
78
96
|
def perform_legacy_drag(element, drop_modifiers)
|
@@ -92,12 +110,7 @@ private
|
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
95
|
-
def
|
96
|
-
(defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)) ||
|
97
|
-
capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
98
|
-
end
|
99
|
-
|
100
|
-
def browser_version(to_float = true)
|
113
|
+
def browser_version(to_float: true)
|
101
114
|
caps = capabilities
|
102
115
|
ver = (caps[:browser_version] || caps[:version])
|
103
116
|
ver = ver.to_f if to_float
|
@@ -116,10 +129,6 @@ private
|
|
116
129
|
capabilities['chrome']['chromedriverVersion'].split(' ')[0]
|
117
130
|
end
|
118
131
|
|
119
|
-
def capabilities
|
120
|
-
driver.browser.capabilities
|
121
|
-
end
|
122
|
-
|
123
132
|
def native_displayed?
|
124
133
|
(driver.options[:native_displayed] != false) &&
|
125
134
|
(w3c? && chromedriver_supports_displayed_endpoint?) &&
|
@@ -42,7 +42,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
42
42
|
|
43
43
|
def send_keys(*args)
|
44
44
|
# https://github.com/mozilla/geckodriver/issues/846
|
45
|
-
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?
|
45
|
+
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?(Array)
|
46
46
|
|
47
47
|
native.click
|
48
48
|
_send_keys(args).perform
|
@@ -85,7 +85,7 @@ private
|
|
85
85
|
(driver.options[:native_displayed] != false) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
|
86
86
|
end
|
87
87
|
|
88
|
-
def
|
88
|
+
def perform_with_options(click_options)
|
89
89
|
# Firefox/marionette has an issue clicking with offset near viewport edge
|
90
90
|
# scroll element to middle just in case
|
91
91
|
scroll_to_center if click_options.coords?
|
@@ -43,7 +43,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
43
43
|
return '' unless visible?
|
44
44
|
|
45
45
|
vis_text = driver.execute_script('return arguments[0].innerText', self)
|
46
|
-
vis_text.
|
46
|
+
vis_text.squeeze(' ')
|
47
47
|
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
48
48
|
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
49
49
|
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionPauser
|
4
|
+
def initialize(mouse, keyboard)
|
5
|
+
super
|
6
|
+
@devices[:pauser] = Pauser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def pause(duration)
|
10
|
+
@actions << [:pauser, :pause, [duration]]
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
class Pauser
|
15
|
+
def pause(duration)
|
16
|
+
sleep duration
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private_constant :Pauser
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined?(::Selenium::WebDriver::VERSION) && (::Selenium::WebDriver::VERSION.to_f < 4) &&
|
24
|
+
defined?(::Selenium::WebDriver::ActionBuilder)
|
25
|
+
::Selenium::WebDriver::ActionBuilder.prepend(ActionPauser)
|
26
|
+
end
|
@@ -6,10 +6,10 @@ private
|
|
6
6
|
def read_atom(function)
|
7
7
|
@atoms ||= Hash.new do |hash, key|
|
8
8
|
hash[key] = begin
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
File.read(File.expand_path("../../atoms/#{key}.min.js", __FILE__))
|
10
|
+
rescue Errno::ENOENT
|
11
|
+
super
|
12
|
+
end
|
13
13
|
end
|
14
14
|
@atoms[function]
|
15
15
|
end
|