capybara 3.35.0 → 3.40.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 +168 -5
- data/README.md +199 -39
- data/lib/capybara/config.rb +16 -4
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/driver/node.rb +5 -1
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/helpers.rb +9 -14
- data/lib/capybara/minitest/spec.rb +18 -6
- data/lib/capybara/minitest.rb +14 -1
- data/lib/capybara/node/actions.rb +14 -9
- data/lib/capybara/node/base.rb +2 -1
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/element.rb +13 -2
- data/lib/capybara/node/finders.rb +11 -2
- data/lib/capybara/node/matchers.rb +25 -0
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +2 -1
- data/lib/capybara/queries/base_query.rb +2 -2
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +40 -11
- data/lib/capybara/queries/sibling_query.rb +2 -1
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +64 -8
- data/lib/capybara/rack_test/driver.rb +4 -4
- data/lib/capybara/rack_test/form.rb +29 -7
- data/lib/capybara/rack_test/node.rb +32 -33
- data/lib/capybara/registration_container.rb +2 -5
- data/lib/capybara/registrations/drivers.rb +7 -7
- data/lib/capybara/registrations/servers.rb +37 -16
- data/lib/capybara/result.rb +2 -2
- data/lib/capybara/rspec/matcher_proxies.rb +6 -6
- data/lib/capybara/rspec/matchers/base.rb +8 -6
- data/lib/capybara/rspec/matchers/compound.rb +1 -1
- data/lib/capybara/rspec/matchers/have_selector.rb +9 -17
- data/lib/capybara/rspec/matchers.rb +21 -16
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
- data/lib/capybara/selector/css.rb +6 -6
- data/lib/capybara/selector/definition/button.rb +10 -5
- data/lib/capybara/selector/definition/checkbox.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +2 -1
- data/lib/capybara/selector/definition/radio_button.rb +1 -1
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +2 -2
- data/lib/capybara/selector/definition.rb +4 -2
- data/lib/capybara/selector/filter_set.rb +4 -7
- data/lib/capybara/selector/regexp_disassembler.rb +2 -5
- data/lib/capybara/selector/selector.rb +5 -1
- data/lib/capybara/selector.rb +252 -0
- data/lib/capybara/selenium/driver.rb +31 -54
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -5
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -7
- data/lib/capybara/selenium/extensions/html5_drag.rb +5 -4
- data/lib/capybara/selenium/node.rb +60 -38
- data/lib/capybara/selenium/nodes/chrome_node.rb +4 -16
- data/lib/capybara/selenium/nodes/edge_node.rb +19 -13
- data/lib/capybara/selenium/nodes/firefox_node.rb +3 -3
- data/lib/capybara/selenium/nodes/safari_node.rb +4 -4
- 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 +40 -23
- data/lib/capybara/server/middleware.rb +1 -1
- data/lib/capybara/server.rb +1 -1
- data/lib/capybara/session/config.rb +4 -2
- data/lib/capybara/session.rb +34 -34
- data/lib/capybara/spec/public/test.js +4 -0
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +11 -15
- data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
- data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
- data/lib/capybara/spec/session/check_spec.rb +10 -0
- data/lib/capybara/spec/session/choose_spec.rb +6 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/click_link_spec.rb +12 -1
- data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/find_link_spec.rb +10 -0
- data/lib/capybara/spec/session/find_spec.rb +15 -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_ancestor_spec.rb +2 -2
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
- data/lib/capybara/spec/session/has_button_spec.rb +30 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +3 -3
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +25 -1
- data/lib/capybara/spec/session/has_link_spec.rb +40 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
- data/lib/capybara/spec/session/has_select_spec.rb +10 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
- data/lib/capybara/spec/session/has_table_spec.rb +13 -2
- data/lib/capybara/spec/session/has_text_spec.rb +6 -14
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -0
- data/lib/capybara/spec/session/node_spec.rb +88 -1
- data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
- data/lib/capybara/spec/session/scroll_spec.rb +7 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/session/within_spec.rb +13 -0
- data/lib/capybara/spec/spec_helper.rb +12 -5
- data/lib/capybara/spec/test_app.rb +91 -14
- data/lib/capybara/spec/views/animated.erb +1 -1
- data/lib/capybara/spec/views/form.erb +34 -4
- data/lib/capybara/spec/views/frame_child.erb +1 -1
- data/lib/capybara/spec/views/frame_one.erb +1 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/offset.erb +2 -1
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +2 -2
- data/lib/capybara/spec/views/scroll.erb +2 -1
- data/lib/capybara/spec/views/spatial.erb +1 -1
- data/lib/capybara/spec/views/with_animation.erb +2 -3
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +2 -2
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +2 -2
- data/lib/capybara/spec/views/with_html.erb +5 -3
- data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
- data/lib/capybara/spec/views/with_js.erb +2 -3
- data/lib/capybara/spec/views/with_jstree.erb +1 -1
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +30 -30
- data/spec/basic_node_spec.rb +16 -3
- data/spec/capybara_spec.rb +12 -0
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +1 -1
- data/spec/css_splitter_spec.rb +1 -1
- data/spec/dsl_spec.rb +5 -3
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
- data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
- data/spec/minitest_spec.rb +12 -1
- data/spec/minitest_spec_spec.rb +4 -0
- data/spec/per_session_config_spec.rb +1 -1
- data/spec/rack_test_spec.rb +30 -12
- data/spec/result_spec.rb +41 -35
- data/spec/rspec/features_spec.rb +3 -3
- data/spec/rspec/scenarios_spec.rb +2 -2
- data/spec/rspec/shared_spec_matchers.rb +27 -3
- data/spec/rspec_matchers_spec.rb +25 -0
- data/spec/rspec_spec.rb +3 -3
- data/spec/sauce_spec_chrome.rb +5 -5
- data/spec/selector_spec.rb +4 -4
- data/spec/selenium_spec_chrome.rb +20 -18
- data/spec/selenium_spec_chrome_remote.rb +15 -19
- data/spec/selenium_spec_edge.rb +19 -6
- data/spec/selenium_spec_firefox.rb +26 -8
- data/spec/selenium_spec_firefox_remote.rb +18 -4
- data/spec/selenium_spec_ie.rb +7 -8
- data/spec/selenium_spec_safari.rb +34 -20
- data/spec/server_spec.rb +19 -7
- data/spec/shared_selenium_node.rb +0 -4
- data/spec/shared_selenium_session.rb +22 -14
- data/spec/spec_helper.rb +36 -3
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +1 -1
- metadata +49 -30
- data/lib/capybara/selenium/logger_suppressor.rb +0 -34
- data/lib/capybara/selenium/patches/action_pauser.rb +0 -26
- data/lib/capybara/spec/views/with_title.erb +0 -5
@@ -4,22 +4,22 @@
|
|
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
|
|
12
14
|
def visible_text
|
15
|
+
raise NotImplementedError, 'Getting visible text is not currently supported directly on shadow roots' if shadow_root?
|
16
|
+
|
13
17
|
native.text
|
14
18
|
end
|
15
19
|
|
16
20
|
def all_text
|
17
|
-
text = driver.evaluate_script('arguments[0].textContent', self)
|
18
|
-
text
|
19
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
20
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
21
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
22
|
-
.tr("\u00a0", ' ')
|
21
|
+
text = driver.evaluate_script('arguments[0].textContent', self) || ''
|
22
|
+
normalize_spacing(text)
|
23
23
|
end
|
24
24
|
|
25
25
|
def [](name)
|
@@ -37,9 +37,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def style(styles)
|
40
|
-
styles.
|
41
|
-
result[style] = native.css_value(style)
|
42
|
-
end
|
40
|
+
styles.to_h { |style| [style, native.css_value(style)] }
|
43
41
|
end
|
44
42
|
|
45
43
|
##
|
@@ -53,6 +51,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
53
51
|
# :none => append the new value to the existing value <br/>
|
54
52
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
55
53
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
54
|
+
# @option options [Boolean] :rapid (nil) Whether setting text inputs should use a faster "rapid" mode<br/>
|
55
|
+
# nil => Text inputs with length greater than 30 characters will be set using a faster driver script mode<br/>
|
56
|
+
# true => Rapid mode will be used regardless of input length<br/>
|
57
|
+
# false => Sends keys via conventional mode. This may be required to avoid losing key-presses if you have certain
|
58
|
+
# Javascript interactions on form inputs<br/>
|
56
59
|
def set(value, **options)
|
57
60
|
if value.is_a?(Array) && !multiple?
|
58
61
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
@@ -110,11 +113,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
110
113
|
action.click(target)
|
111
114
|
else
|
112
115
|
action.click_and_hold(target)
|
113
|
-
|
114
|
-
action.pause(action.pointer_inputs.first, click_options.delay)
|
115
|
-
else
|
116
|
-
action.pause(click_options.delay)
|
117
|
-
end
|
116
|
+
action_pause(action, click_options.delay)
|
118
117
|
action.release
|
119
118
|
end
|
120
119
|
end
|
@@ -133,13 +132,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
133
132
|
target = click_options.coords? ? nil : native
|
134
133
|
if click_options.delay.zero?
|
135
134
|
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
135
|
else
|
142
|
-
|
136
|
+
action.move_to(target) if target
|
137
|
+
action.pointer_down(:right).then do |act|
|
138
|
+
action_pause(act, click_options.delay)
|
139
|
+
end.pointer_up(:right)
|
143
140
|
end
|
144
141
|
end
|
145
142
|
end
|
@@ -179,7 +176,12 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
179
176
|
end
|
180
177
|
|
181
178
|
def tag_name
|
182
|
-
@tag_name ||=
|
179
|
+
@tag_name ||=
|
180
|
+
if native.respond_to? :tag_name
|
181
|
+
native.tag_name.downcase
|
182
|
+
else
|
183
|
+
shadow_root? ? 'ShadowRoot' : 'Unknown'
|
184
|
+
end
|
183
185
|
end
|
184
186
|
|
185
187
|
def visible?; boolean_attr(native.displayed?); end
|
@@ -199,10 +201,6 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
199
201
|
native.attribute('isContentEditable') == 'true'
|
200
202
|
end
|
201
203
|
|
202
|
-
def ==(other)
|
203
|
-
native == other.native
|
204
|
-
end
|
205
|
-
|
206
204
|
def path
|
207
205
|
driver.evaluate_script GET_XPATH_SCRIPT, self
|
208
206
|
end
|
@@ -218,6 +216,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
218
216
|
native.rect
|
219
217
|
end
|
220
218
|
|
219
|
+
def shadow_root
|
220
|
+
root = native.shadow_root
|
221
|
+
root && build_node(native.shadow_root)
|
222
|
+
end
|
223
|
+
|
221
224
|
protected
|
222
225
|
|
223
226
|
def scroll_if_needed
|
@@ -228,7 +231,7 @@ protected
|
|
228
231
|
end
|
229
232
|
|
230
233
|
def scroll_to_center
|
231
|
-
script = <<-
|
234
|
+
script = <<-JS
|
232
235
|
try {
|
233
236
|
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
234
237
|
} catch(e) {
|
@@ -274,7 +277,7 @@ private
|
|
274
277
|
elsif clear == :backspace
|
275
278
|
# Clear field by sending the correct number of backspace keys.
|
276
279
|
backspaces = [:backspace] * self.value.to_s.length
|
277
|
-
send_keys(
|
280
|
+
send_keys(:end, *backspaces, value)
|
278
281
|
elsif clear.is_a? Array
|
279
282
|
send_keys(*clear, value)
|
280
283
|
else
|
@@ -282,7 +285,7 @@ private
|
|
282
285
|
if rapid == true || ((value.length > auto_rapid_set_length) && rapid != false)
|
283
286
|
send_keys(value[0..3])
|
284
287
|
driver.execute_script RAPID_APPEND_TEXT, self, value[4...-3]
|
285
|
-
send_keys(value[-3
|
288
|
+
send_keys(value[-3..])
|
286
289
|
else
|
287
290
|
send_keys(value)
|
288
291
|
end
|
@@ -298,7 +301,7 @@ private
|
|
298
301
|
|
299
302
|
scroll_if_needed do
|
300
303
|
action_with_modifiers(click_options) do |action|
|
301
|
-
if
|
304
|
+
if block
|
302
305
|
yield action
|
303
306
|
else
|
304
307
|
click_options.coords? ? action.click : action.click(native)
|
@@ -407,10 +410,20 @@ private
|
|
407
410
|
|
408
411
|
def action_with_modifiers(click_options)
|
409
412
|
actions = browser_action.tap do |acts|
|
410
|
-
if click_options.
|
411
|
-
|
413
|
+
if click_options.coords?
|
414
|
+
if click_options.center_offset?
|
415
|
+
acts.move_to(native, *click_options.coords)
|
416
|
+
else
|
417
|
+
right_by, down_by = *click_options.coords
|
418
|
+
size = native.size
|
419
|
+
left_offset = (size[:width] / 2).to_i
|
420
|
+
top_offset = (size[:height] / 2).to_i
|
421
|
+
left = -left_offset + right_by
|
422
|
+
top = -top_offset + down_by
|
423
|
+
acts.move_to(native, left, top)
|
424
|
+
end
|
412
425
|
else
|
413
|
-
acts.move_to(native
|
426
|
+
acts.move_to(native)
|
414
427
|
end
|
415
428
|
end
|
416
429
|
modifiers_down(actions, click_options.keys)
|
@@ -448,9 +461,8 @@ private
|
|
448
461
|
browser.capabilities
|
449
462
|
end
|
450
463
|
|
451
|
-
def
|
452
|
-
(
|
453
|
-
capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
464
|
+
def action_pause(action, duration)
|
465
|
+
action.pause(device: action.pointer_inputs.first, duration: duration)
|
454
466
|
end
|
455
467
|
|
456
468
|
def normalize_keys(keys)
|
@@ -479,7 +491,7 @@ private
|
|
479
491
|
def attrs(*attr_names)
|
480
492
|
return attr_names.map { |name| self[name.to_s] } if ENV['CAPYBARA_THOROUGH']
|
481
493
|
|
482
|
-
driver.evaluate_script <<~
|
494
|
+
driver.evaluate_script <<~JS, self, attr_names.map(&:to_s)
|
483
495
|
(function(el, names){
|
484
496
|
return names.map(function(name){
|
485
497
|
return el[name]
|
@@ -488,6 +500,16 @@ private
|
|
488
500
|
JS
|
489
501
|
end
|
490
502
|
|
503
|
+
def native_id
|
504
|
+
# Selenium 3 -> 4 changed the return of ref
|
505
|
+
type_or_id, id = native.ref
|
506
|
+
id || type_or_id
|
507
|
+
end
|
508
|
+
|
509
|
+
def shadow_root?
|
510
|
+
defined?(::Selenium::WebDriver::ShadowRoot) && native.is_a?(::Selenium::WebDriver::ShadowRoot)
|
511
|
+
end
|
512
|
+
|
491
513
|
GET_XPATH_SCRIPT = <<~'JS'
|
492
514
|
(function(el, xml){
|
493
515
|
var xpath = '';
|
@@ -520,7 +542,7 @@ private
|
|
520
542
|
})(arguments[0], document)
|
521
543
|
JS
|
522
544
|
|
523
|
-
OBSCURED_OR_OFFSET_SCRIPT = <<~
|
545
|
+
OBSCURED_OR_OFFSET_SCRIPT = <<~JS
|
524
546
|
(function(el, x, y) {
|
525
547
|
var box = el.getBoundingClientRect();
|
526
548
|
if (x == null) x = box.width/2;
|
@@ -537,7 +559,7 @@ private
|
|
537
559
|
})(arguments[0], arguments[1], arguments[2])
|
538
560
|
JS
|
539
561
|
|
540
|
-
RAPID_APPEND_TEXT = <<~
|
562
|
+
RAPID_APPEND_TEXT = <<~JS
|
541
563
|
(function(el, value) {
|
542
564
|
value = el.value + value;
|
543
565
|
if (el.maxLength && el.maxLength != -1){
|
@@ -24,12 +24,6 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
24
24
|
JS
|
25
25
|
end
|
26
26
|
super
|
27
|
-
rescue *file_errors => e
|
28
|
-
if e.message.match?(/File not found : .+\n.+/m)
|
29
|
-
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
|
30
|
-
end
|
31
|
-
|
32
|
-
raise
|
33
27
|
end
|
34
28
|
|
35
29
|
def drop(*args)
|
@@ -65,7 +59,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
65
59
|
return super unless native_displayed?
|
66
60
|
|
67
61
|
begin
|
68
|
-
bridge.send(:execute, :is_element_displayed, id:
|
62
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
69
63
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
70
64
|
# If the is_element_displayed command is unknown, no point in trying again
|
71
65
|
driver.options[:native_displayed] = false
|
@@ -94,7 +88,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
94
88
|
private
|
95
89
|
|
96
90
|
def perform_legacy_drag(element, drop_modifiers)
|
97
|
-
return super if chromedriver_fixed_actions_key_state? ||
|
91
|
+
return super if chromedriver_fixed_actions_key_state? || element.obscured?
|
98
92
|
|
99
93
|
raise ArgumentError, 'Modifier keys are not supported while dragging in this version of Chrome.' unless drop_modifiers.empty?
|
100
94
|
|
@@ -104,15 +98,9 @@ private
|
|
104
98
|
browser_action.click_and_hold(native).move_to(element.native).release.perform
|
105
99
|
end
|
106
100
|
|
107
|
-
def file_errors
|
108
|
-
@file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
|
109
|
-
[::Selenium::WebDriver::Error::ExpectedError]
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
101
|
def browser_version(to_float: true)
|
114
102
|
caps = capabilities
|
115
|
-
ver =
|
103
|
+
ver = caps[:browser_version] || caps[:version]
|
116
104
|
ver = ver.to_f if to_float
|
117
105
|
ver
|
118
106
|
end
|
@@ -131,7 +119,7 @@ private
|
|
131
119
|
|
132
120
|
def native_displayed?
|
133
121
|
(driver.options[:native_displayed] != false) &&
|
134
|
-
|
122
|
+
chromedriver_supports_displayed_endpoint? &&
|
135
123
|
(!ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS'])
|
136
124
|
end
|
137
125
|
end
|
@@ -24,12 +24,6 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
24
24
|
JS
|
25
25
|
end
|
26
26
|
super
|
27
|
-
rescue *file_errors => e
|
28
|
-
if e.message.match?(/File not found : .+\n.+/m)
|
29
|
-
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
|
30
|
-
end
|
31
|
-
|
32
|
-
raise
|
33
27
|
end
|
34
28
|
|
35
29
|
def drop(*args)
|
@@ -38,7 +32,7 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
38
32
|
html5_drop(*args)
|
39
33
|
end
|
40
34
|
|
41
|
-
def click(
|
35
|
+
def click(*, **)
|
42
36
|
super
|
43
37
|
rescue Selenium::WebDriver::Error::InvalidArgumentError => e
|
44
38
|
tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
|
@@ -69,7 +63,7 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
69
63
|
return super unless chrome_edge? && native_displayed?
|
70
64
|
|
71
65
|
begin
|
72
|
-
bridge.send(:execute, :is_element_displayed, id:
|
66
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
73
67
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
74
68
|
# If the is_element_displayed command is unknown, no point in trying again
|
75
69
|
driver.options[:native_displayed] = false
|
@@ -77,14 +71,26 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
74
|
+
def send_keys(*args)
|
75
|
+
args.chunk { |inp| inp.is_a?(String) && inp.match?(/\p{Emoji Presentation}/) }
|
76
|
+
.each do |contains_emoji, inputs|
|
77
|
+
if contains_emoji
|
78
|
+
inputs.join.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
|
79
|
+
.each do |emoji, clusters|
|
80
|
+
if emoji
|
81
|
+
driver.send(:execute_cdp, 'Input.insertText', text: clusters.join)
|
82
|
+
else
|
83
|
+
super(clusters.join)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
else
|
87
|
+
super(*inputs)
|
88
|
+
end
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
92
|
+
private
|
93
|
+
|
88
94
|
def browser_version
|
89
95
|
@browser_version ||= begin
|
90
96
|
caps = driver.browser.capabilities
|
@@ -11,8 +11,8 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
11
11
|
super
|
12
12
|
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
13
13
|
if tag_name == 'tr'
|
14
|
-
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
|
15
|
-
'see https://github.com/mozilla/geckodriver/issues/1228
|
14
|
+
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - ' \
|
15
|
+
'see https://github.com/mozilla/geckodriver/issues/1228 - Your test should probably be ' \
|
16
16
|
'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
|
17
17
|
return find_css('th:first-child,td:first-child')[0].click(keys, **options)
|
18
18
|
end
|
@@ -76,7 +76,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
76
76
|
return super unless native_displayed?
|
77
77
|
|
78
78
|
begin
|
79
|
-
bridge.send(:execute, :is_element_displayed, id:
|
79
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
80
80
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
81
81
|
# If the is_element_displayed command is unknown, no point in trying again
|
82
82
|
driver.options[:native_displayed] = false
|
@@ -11,10 +11,10 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
11
11
|
super
|
12
12
|
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
13
13
|
if tag_name == 'tr'
|
14
|
-
warn 'You are attempting to click a table row which has issues in safaridriver - '\
|
15
|
-
'Your test should probably be clicking on a table cell like a user would. '\
|
14
|
+
warn 'You are attempting to click a table row which has issues in safaridriver - ' \
|
15
|
+
'Your test should probably be clicking on a table cell like a user would. ' \
|
16
16
|
'Clicking the first cell in the row instead.'
|
17
|
-
return find_css('th:first-child,td:first-child')[0].click(keys, options)
|
17
|
+
return find_css('th:first-child,td:first-child')[0].click(keys, **options)
|
18
18
|
end
|
19
19
|
raise
|
20
20
|
rescue ::Selenium::WebDriver::Error::WebDriverError => e
|
@@ -74,7 +74,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
74
74
|
if clear == :backspace
|
75
75
|
# Clear field by sending the correct number of backspace keys.
|
76
76
|
backspaces = [:backspace] * self.value.to_s.length
|
77
|
-
send_keys(
|
77
|
+
send_keys([:control, 'e'], *backspaces, value)
|
78
78
|
else
|
79
79
|
super.tap do
|
80
80
|
# React doesn't see the safaridriver element clear
|
@@ -16,48 +16,65 @@ module Capybara
|
|
16
16
|
|
17
17
|
def initialize(app)
|
18
18
|
@app = app
|
19
|
-
@
|
19
|
+
@disable_css_markup = format(DISABLE_CSS_MARKUP_TEMPLATE,
|
20
|
+
selector: self.class.selector_for(Capybara.disable_animation))
|
21
|
+
@disable_js_markup = +DISABLE_JS_MARKUP_TEMPLATE
|
20
22
|
end
|
21
23
|
|
22
24
|
def call(env)
|
23
|
-
|
24
|
-
return [
|
25
|
+
status, headers, body = @app.call(env)
|
26
|
+
return [status, headers, body] unless html_content?(headers)
|
25
27
|
|
26
|
-
|
28
|
+
nonces = directive_nonces(headers).transform_values { |nonce| "nonce=\"#{nonce}\"" if nonce && !nonce.empty? }
|
29
|
+
response = Rack::Response.new([], status, headers)
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
body.each { |html| response.write insert_disable(html, nonces) }
|
32
|
+
body.close if body.respond_to?(:close)
|
30
33
|
|
31
34
|
response.finish
|
32
35
|
end
|
33
36
|
|
34
37
|
private
|
35
38
|
|
36
|
-
attr_reader :
|
39
|
+
attr_reader :disable_css_markup, :disable_js_markup
|
37
40
|
|
38
|
-
def html_content?
|
39
|
-
/html/.match?(
|
41
|
+
def html_content?(headers)
|
42
|
+
/html/.match?(headers['Content-Type']) # rubocop:todo Performance/StringInclude
|
40
43
|
end
|
41
44
|
|
42
|
-
def insert_disable(html)
|
43
|
-
html.sub(%r{(</
|
45
|
+
def insert_disable(html, nonces)
|
46
|
+
html.sub(%r{(</head>)}, "<style #{nonces['style-src']}>#{disable_css_markup}</style>\\1")
|
47
|
+
.sub(%r{(</body>)}, "<script #{nonces['script-src']}>#{disable_js_markup}</script>\\1")
|
44
48
|
end
|
45
49
|
|
46
|
-
|
47
|
-
|
50
|
+
def directive_nonces(headers)
|
51
|
+
headers.fetch('Content-Security-Policy', '')
|
52
|
+
.split(';')
|
53
|
+
.map(&:split) # rubocop:disable Style/MapToHash
|
54
|
+
.to_h do |s|
|
55
|
+
[
|
56
|
+
s[0], s[1..].filter_map do |value|
|
57
|
+
/^'nonce-(?<nonce>.+)'/ =~ value
|
58
|
+
nonce
|
59
|
+
end[0]
|
60
|
+
]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
DISABLE_CSS_MARKUP_TEMPLATE = <<~CSS
|
65
|
+
%<selector>s, %<selector>s::before, %<selector>s::after {
|
66
|
+
transition: none !important;
|
67
|
+
animation-duration: 0s !important;
|
68
|
+
animation-delay: 0s !important;
|
69
|
+
scroll-behavior: auto !important;
|
70
|
+
}
|
71
|
+
CSS
|
72
|
+
|
73
|
+
DISABLE_JS_MARKUP_TEMPLATE = <<~SCRIPT
|
48
74
|
//<![CDATA[
|
49
75
|
(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);
|
50
76
|
//]]>
|
51
|
-
|
52
|
-
<style>
|
53
|
-
%<selector>s, %<selector>s::before, %<selector>s::after {
|
54
|
-
transition: none !important;
|
55
|
-
animation-duration: 0s !important;
|
56
|
-
animation-delay: 0s !important;
|
57
|
-
scroll-behavior: auto !important;
|
58
|
-
}
|
59
|
-
</style>
|
60
|
-
HTML
|
77
|
+
SCRIPT
|
61
78
|
end
|
62
79
|
end
|
63
80
|
end
|
data/lib/capybara/server.rb
CHANGED
@@ -55,7 +55,7 @@ module Capybara
|
|
55
55
|
|
56
56
|
res = @checker.request { |http| http.get('/__identify__') }
|
57
57
|
|
58
|
-
|
58
|
+
res.body == app.object_id.to_s if res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection)
|
59
59
|
rescue SystemCallError, Net::ReadTimeout, OpenSSL::SSL::SSLError
|
60
60
|
false
|
61
61
|
end
|
@@ -8,7 +8,7 @@ module Capybara
|
|
8
8
|
automatic_reload match exact exact_text raise_server_errors visible_text_only
|
9
9
|
automatic_label_click enable_aria_label save_path asset_host default_host app_host
|
10
10
|
server_host server_port server_errors default_set_options disable_animation test_id
|
11
|
-
predicates_wait default_normalize_ws w3c_click_offset enable_aria_role].freeze
|
11
|
+
predicates_wait default_normalize_ws w3c_click_offset enable_aria_role default_retry_interval].freeze
|
12
12
|
|
13
13
|
attr_accessor(*OPTIONS)
|
14
14
|
|
@@ -21,6 +21,8 @@ module Capybara
|
|
21
21
|
# See {Capybara.configure}
|
22
22
|
# @!method default_max_wait_time
|
23
23
|
# See {Capybara.configure}
|
24
|
+
# @!method default_retry_interval
|
25
|
+
# See {Capybara.configure}
|
24
26
|
# @!method ignore_hidden_elements
|
25
27
|
# See {Capybara.configure}
|
26
28
|
# @!method automatic_reload
|
@@ -100,7 +102,7 @@ module Capybara
|
|
100
102
|
remove_method :test_id=
|
101
103
|
##
|
102
104
|
#
|
103
|
-
# Set an
|
105
|
+
# Set an attribute to be optionally matched against the locator for builtin selector types.
|
104
106
|
# This attribute will be checked by builtin selector types whenever id would normally be checked.
|
105
107
|
# If `nil` then it will be ignored.
|
106
108
|
#
|