capybara 3.3.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/.yardopts +1 -0
- data/History.md +803 -13
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +25 -9
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +17 -3
- data/lib/capybara/driver/node.rb +31 -6
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +31 -7
- data/lib/capybara/minitest/spec.rb +180 -88
- data/lib/capybara/minitest.rb +262 -149
- data/lib/capybara/node/actions.rb +202 -116
- data/lib/capybara/node/base.rb +34 -19
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +10 -12
- data/lib/capybara/node/element.rb +269 -115
- data/lib/capybara/node/finders.rb +99 -77
- data/lib/capybara/node/matchers.rb +327 -151
- data/lib/capybara/node/simple.rb +48 -13
- 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 +8 -9
- data/lib/capybara/queries/base_query.rb +23 -16
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +587 -130
- data/lib/capybara/queries/sibling_query.rb +8 -6
- data/lib/capybara/queries/style_query.rb +6 -2
- data/lib/capybara/queries/text_query.rb +28 -14
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +92 -25
- data/lib/capybara/rack_test/driver.rb +16 -7
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +68 -41
- data/lib/capybara/rack_test/node.rb +106 -39
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/registration_container.rb +41 -0
- data/lib/capybara/registrations/drivers.rb +42 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +66 -0
- data/lib/capybara/result.rb +75 -52
- data/lib/capybara/rspec/features.rb +7 -7
- data/lib/capybara/rspec/matcher_proxies.rb +39 -18
- data/lib/capybara/rspec/matchers/base.rb +113 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +141 -339
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
- data/lib/capybara/selector/css.rb +27 -25
- data/lib/capybara/selector/definition/button.rb +68 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +28 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +62 -0
- data/lib/capybara/selector/definition/link.rb +55 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/definition.rb +280 -0
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +73 -25
- data/lib/capybara/selector/filters/base.rb +24 -5
- data/lib/capybara/selector/filters/expression_filter.rb +3 -3
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +16 -2
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +85 -348
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -447
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
- data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
- data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
- data/lib/capybara/selenium/driver.rb +255 -143
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +110 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +76 -0
- data/lib/capybara/selenium/node.rb +436 -134
- data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
- data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
- data/lib/capybara/selenium/patches/atoms.rb +18 -0
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +45 -0
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +56 -19
- data/lib/capybara/server/checker.rb +9 -3
- data/lib/capybara/server/middleware.rb +28 -12
- data/lib/capybara/server.rb +33 -10
- data/lib/capybara/session/config.rb +34 -10
- data/lib/capybara/session/matchers.rb +23 -16
- data/lib/capybara/session.rb +230 -170
- data/lib/capybara/spec/public/jquery.js +5 -5
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +121 -8
- data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +127 -40
- data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
- data/lib/capybara/spec/session/assert_current_path_spec.rb +21 -18
- data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
- data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
- data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
- data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
- data/lib/capybara/spec/session/body_spec.rb +6 -6
- data/lib/capybara/spec/session/check_spec.rb +102 -47
- data/lib/capybara/spec/session/choose_spec.rb +58 -32
- data/lib/capybara/spec/session/click_button_spec.rb +219 -163
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
- data/lib/capybara/spec/session/click_link_spec.rb +77 -54
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +38 -29
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
- data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -56
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +101 -46
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
- data/lib/capybara/spec/session/find_field_spec.rb +32 -30
- data/lib/capybara/spec/session/find_link_spec.rb +31 -21
- data/lib/capybara/spec/session/find_spec.rb +244 -141
- data/lib/capybara/spec/session/first_spec.rb +43 -43
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
- data/lib/capybara/spec/session/go_back_spec.rb +1 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
- data/lib/capybara/spec/session/has_button_spec.rb +94 -13
- data/lib/capybara/spec/session/has_css_spec.rb +272 -132
- data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +137 -58
- data/lib/capybara/spec/session/has_link_spec.rb +44 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +181 -4
- data/lib/capybara/spec/session/has_text_spec.rb +101 -53
- data/lib/capybara/spec/session/has_title_spec.rb +19 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
- data/lib/capybara/spec/session/headers_spec.rb +1 -1
- data/lib/capybara/spec/session/html_spec.rb +13 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
- data/lib/capybara/spec/session/node_spec.rb +894 -142
- data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
- data/lib/capybara/spec/session/response_code_spec.rb +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +37 -37
- data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/scroll_spec.rb +119 -0
- data/lib/capybara/spec/session/select_spec.rb +85 -85
- data/lib/capybara/spec/session/selectors_spec.rb +49 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +25 -24
- data/lib/capybara/spec/session/title_spec.rb +7 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +68 -49
- data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
- data/lib/capybara/spec/session/window/window_spec.rb +62 -63
- data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
- data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
- data/lib/capybara/spec/session/within_spec.rb +79 -42
- data/lib/capybara/spec/spec_helper.rb +41 -53
- data/lib/capybara/spec/test_app.rb +132 -43
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +139 -42
- data/lib/capybara/spec/views/frame_child.erb +4 -3
- data/lib/capybara/spec/views/frame_one.erb +2 -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 +47 -0
- data/lib/capybara/spec/views/offset.erb +33 -0
- 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 +45 -0
- data/lib/capybara/spec/views/scroll.erb +21 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +67 -0
- data/lib/capybara/spec/views/with_animation.erb +39 -4
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +37 -9
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +26 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- 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_scope_other.erb +6 -0
- 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 +21 -0
- 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 +19 -25
- data/lib/capybara.rb +126 -111
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +56 -44
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +8 -8
- data/spec/dsl_spec.rb +79 -52
- data/spec/filter_set_spec.rb +9 -9
- data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
- data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
- data/spec/minitest_spec.rb +45 -7
- data/spec/minitest_spec_spec.rb +87 -64
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +172 -116
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +80 -72
- data/spec/rspec/features_spec.rb +21 -16
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +407 -365
- data/spec/rspec/views_spec.rb +3 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +63 -41
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +334 -89
- data/spec/selenium_spec_chrome.rb +176 -62
- data/spec/selenium_spec_chrome_remote.rb +54 -14
- data/spec/selenium_spec_edge.rb +41 -8
- data/spec/selenium_spec_firefox.rb +228 -0
- data/spec/selenium_spec_firefox_remote.rb +94 -0
- data/spec/selenium_spec_ie.rb +129 -11
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +171 -97
- data/spec/session_spec.rb +34 -18
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +344 -80
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +326 -28
- data/lib/capybara/rspec/compound.rb +0 -94
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
- data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
- data/lib/capybara/spec/session/has_style_spec.rb +0 -25
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
- data/spec/selenium_spec_marionette.rb +0 -167
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Selenium specific implementation of the Capybara::Driver::Node API
|
|
4
|
+
|
|
5
|
+
require 'capybara/selenium/extensions/find'
|
|
6
|
+
require 'capybara/selenium/extensions/scroll'
|
|
7
|
+
require 'capybara/node/whitespace_normalizer'
|
|
8
|
+
|
|
3
9
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
10
|
+
include Capybara::Node::WhitespaceNormalizer
|
|
11
|
+
include Capybara::Selenium::Find
|
|
12
|
+
include Capybara::Selenium::Scroll
|
|
13
|
+
|
|
4
14
|
def visible_text
|
|
15
|
+
raise NotImplementedError, 'Getting visible text is not currently supported directly on shadow roots' if shadow_root?
|
|
16
|
+
|
|
5
17
|
native.text
|
|
6
18
|
end
|
|
7
19
|
|
|
8
20
|
def all_text
|
|
9
|
-
text = driver.
|
|
10
|
-
text
|
|
11
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
|
12
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
|
|
13
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
|
|
14
|
-
.tr("\u00a0", ' ')
|
|
21
|
+
text = driver.evaluate_script('arguments[0].textContent', self) || ''
|
|
22
|
+
normalize_spacing(text)
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def [](name)
|
|
@@ -21,17 +29,15 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
def value
|
|
24
|
-
if tag_name ==
|
|
25
|
-
native.find_elements(:css,
|
|
32
|
+
if tag_name == 'select' && multiple?
|
|
33
|
+
native.find_elements(:css, 'option:checked').map { |el| el[:value] || el.text }
|
|
26
34
|
else
|
|
27
35
|
native[:value]
|
|
28
36
|
end
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
def style(styles)
|
|
32
|
-
styles.
|
|
33
|
-
result[style] = native.css_value(style)
|
|
34
|
-
end
|
|
40
|
+
styles.to_h { |style| [style, native.css_value(style)] }
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
##
|
|
@@ -45,12 +51,22 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
45
51
|
# :none => append the new value to the existing value <br/>
|
|
46
52
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
|
47
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/>
|
|
48
59
|
def set(value, **options)
|
|
49
|
-
|
|
60
|
+
if value.is_a?(Array) && !multiple?
|
|
61
|
+
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
|
|
65
|
+
@tag_name ||= tag_name
|
|
50
66
|
|
|
51
67
|
case tag_name
|
|
52
68
|
when 'input'
|
|
53
|
-
case
|
|
69
|
+
case type
|
|
54
70
|
when 'radio'
|
|
55
71
|
click
|
|
56
72
|
when 'checkbox'
|
|
@@ -63,38 +79,47 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
63
79
|
set_time(value)
|
|
64
80
|
when 'datetime-local'
|
|
65
81
|
set_datetime_local(value)
|
|
82
|
+
when 'color'
|
|
83
|
+
set_color(value)
|
|
84
|
+
when 'range'
|
|
85
|
+
set_range(value)
|
|
66
86
|
else
|
|
67
|
-
set_text(value, options)
|
|
87
|
+
set_text(value, **options)
|
|
68
88
|
end
|
|
69
89
|
when 'textarea'
|
|
70
|
-
set_text(value, options)
|
|
90
|
+
set_text(value, **options)
|
|
71
91
|
else
|
|
72
|
-
set_content_editable(value)
|
|
92
|
+
set_content_editable(value)
|
|
73
93
|
end
|
|
74
94
|
end
|
|
75
95
|
|
|
76
96
|
def select_option
|
|
77
|
-
|
|
97
|
+
click unless selected? || disabled?
|
|
78
98
|
end
|
|
79
99
|
|
|
80
100
|
def unselect_option
|
|
81
|
-
raise Capybara::UnselectNotAllowed,
|
|
82
|
-
|
|
101
|
+
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
|
102
|
+
|
|
103
|
+
click if selected?
|
|
83
104
|
end
|
|
84
105
|
|
|
85
106
|
def click(keys = [], **options)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
click_options = ClickOptions.new(keys, options)
|
|
108
|
+
return native.click if click_options.empty?
|
|
109
|
+
|
|
110
|
+
perform_with_options(click_options) do |action|
|
|
111
|
+
target = click_options.coords? ? nil : native
|
|
112
|
+
if click_options.delay.zero?
|
|
113
|
+
action.click(target)
|
|
114
|
+
else
|
|
115
|
+
action.click_and_hold(target)
|
|
116
|
+
action_pause(action, click_options.delay)
|
|
117
|
+
action.release
|
|
93
118
|
end
|
|
94
119
|
end
|
|
95
120
|
rescue StandardError => e
|
|
96
121
|
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
|
97
|
-
e.message
|
|
122
|
+
e.message.include?('Other element would receive the click')
|
|
98
123
|
scroll_to_center
|
|
99
124
|
end
|
|
100
125
|
|
|
@@ -102,18 +127,26 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
102
127
|
end
|
|
103
128
|
|
|
104
129
|
def right_click(keys = [], **options)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
130
|
+
click_options = ClickOptions.new(keys, options)
|
|
131
|
+
perform_with_options(click_options) do |action|
|
|
132
|
+
target = click_options.coords? ? nil : native
|
|
133
|
+
if click_options.delay.zero?
|
|
134
|
+
action.context_click(target)
|
|
135
|
+
else
|
|
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)
|
|
108
140
|
end
|
|
109
141
|
end
|
|
110
142
|
end
|
|
111
143
|
|
|
112
144
|
def double_click(keys = [], **options)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
145
|
+
click_options = ClickOptions.new(keys, options)
|
|
146
|
+
raise ArgumentError, "double_click doesn't support a delay option" unless click_options.delay.zero?
|
|
147
|
+
|
|
148
|
+
perform_with_options(click_options) do |action|
|
|
149
|
+
click_options.coords? ? action.double_click : action.double_click(native)
|
|
117
150
|
end
|
|
118
151
|
end
|
|
119
152
|
|
|
@@ -122,15 +155,33 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
122
155
|
end
|
|
123
156
|
|
|
124
157
|
def hover
|
|
125
|
-
scroll_if_needed {
|
|
158
|
+
scroll_if_needed { browser_action.move_to(native).perform }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def drag_to(element, drop_modifiers: [], **)
|
|
162
|
+
drop_modifiers = Array(drop_modifiers)
|
|
163
|
+
# Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary
|
|
164
|
+
# which means Seleniums `drag_and_drop` is now broken - do it manually
|
|
165
|
+
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
|
166
|
+
# element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
|
|
167
|
+
element.scroll_if_needed do
|
|
168
|
+
keys_down = modifiers_down(browser_action, drop_modifiers)
|
|
169
|
+
keys_up = modifiers_up(keys_down.move_to(element.native).release, drop_modifiers)
|
|
170
|
+
keys_up.perform
|
|
171
|
+
end
|
|
126
172
|
end
|
|
127
173
|
|
|
128
|
-
def
|
|
129
|
-
|
|
174
|
+
def drop(*_)
|
|
175
|
+
raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
|
|
130
176
|
end
|
|
131
177
|
|
|
132
178
|
def tag_name
|
|
133
|
-
|
|
179
|
+
@tag_name ||=
|
|
180
|
+
if native.respond_to? :tag_name
|
|
181
|
+
native.tag_name.downcase
|
|
182
|
+
else
|
|
183
|
+
shadow_root? ? 'ShadowRoot' : 'Unknown'
|
|
184
|
+
end
|
|
134
185
|
end
|
|
135
186
|
|
|
136
187
|
def visible?; boolean_attr(native.displayed?); end
|
|
@@ -140,56 +191,78 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
140
191
|
alias :checked? :selected?
|
|
141
192
|
|
|
142
193
|
def disabled?
|
|
143
|
-
|
|
194
|
+
return true unless native.enabled?
|
|
195
|
+
|
|
196
|
+
# WebDriver only defines `disabled?` for form controls but fieldset makes sense too
|
|
197
|
+
find_xpath('self::fieldset/ancestor-or-self::fieldset[@disabled]').any?
|
|
144
198
|
end
|
|
145
199
|
|
|
146
200
|
def content_editable?
|
|
147
|
-
native.attribute('isContentEditable')
|
|
201
|
+
native.attribute('isContentEditable') == 'true'
|
|
148
202
|
end
|
|
149
203
|
|
|
150
|
-
def
|
|
151
|
-
|
|
204
|
+
def path
|
|
205
|
+
driver.evaluate_script GET_XPATH_SCRIPT, self
|
|
152
206
|
end
|
|
153
207
|
|
|
154
|
-
def
|
|
155
|
-
|
|
208
|
+
def obscured?(x: nil, y: nil)
|
|
209
|
+
res = driver.evaluate_script(OBSCURED_OR_OFFSET_SCRIPT, self, x, y)
|
|
210
|
+
return true if res == true
|
|
211
|
+
|
|
212
|
+
driver.frame_obscured_at?(x: res['x'], y: res['y'])
|
|
156
213
|
end
|
|
157
214
|
|
|
158
|
-
def
|
|
159
|
-
native
|
|
215
|
+
def rect
|
|
216
|
+
native.rect
|
|
160
217
|
end
|
|
161
218
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
default_ns = path.last[:namespaceURI]
|
|
167
|
-
while (node = path.shift)
|
|
168
|
-
parent = path.first
|
|
169
|
-
selector = node[:tagName]
|
|
170
|
-
if node[:namespaceURI] != default_ns
|
|
171
|
-
selector = XPath.child.where((XPath.local_name == selector) & (XPath.namespace_uri == node[:namespaceURI])).to_s
|
|
172
|
-
selector
|
|
173
|
-
end
|
|
219
|
+
def shadow_root
|
|
220
|
+
root = native.shadow_root
|
|
221
|
+
root && build_node(native.shadow_root)
|
|
222
|
+
end
|
|
174
223
|
|
|
175
|
-
|
|
176
|
-
siblings = parent.find_xpath(selector)
|
|
177
|
-
selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
|
|
178
|
-
end
|
|
179
|
-
result.push selector
|
|
180
|
-
end
|
|
224
|
+
protected
|
|
181
225
|
|
|
182
|
-
|
|
226
|
+
def scroll_if_needed
|
|
227
|
+
yield
|
|
228
|
+
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
|
229
|
+
scroll_to_center
|
|
230
|
+
yield
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def scroll_to_center
|
|
234
|
+
script = <<-JS
|
|
235
|
+
try {
|
|
236
|
+
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
|
237
|
+
} catch(e) {
|
|
238
|
+
arguments[0].scrollIntoView(true);
|
|
239
|
+
}
|
|
240
|
+
JS
|
|
241
|
+
begin
|
|
242
|
+
driver.execute_script(script, self)
|
|
243
|
+
rescue StandardError
|
|
244
|
+
# Swallow error if scrollIntoView with options isn't supported
|
|
245
|
+
end
|
|
183
246
|
end
|
|
184
247
|
|
|
185
248
|
private
|
|
186
249
|
|
|
187
|
-
def
|
|
188
|
-
|
|
250
|
+
def sibling_index(parent, node, selector)
|
|
251
|
+
siblings = parent.find_xpath(selector)
|
|
252
|
+
case siblings.size
|
|
253
|
+
when 0
|
|
254
|
+
'[ERROR]' # IE doesn't support full XPath (namespace-uri, etc)
|
|
255
|
+
when 1
|
|
256
|
+
'' # index not necessary when only one matching element
|
|
257
|
+
else
|
|
258
|
+
idx = siblings.index(node)
|
|
259
|
+
# Element may not be found in the siblings if it has gone away
|
|
260
|
+
idx.nil? ? '[ERROR]' : "[#{idx + 1}]"
|
|
261
|
+
end
|
|
189
262
|
end
|
|
190
263
|
|
|
191
264
|
def boolean_attr(val)
|
|
192
|
-
val && (val !=
|
|
265
|
+
val && (val != 'false')
|
|
193
266
|
end
|
|
194
267
|
|
|
195
268
|
# a reference to the select node if this is an option node
|
|
@@ -197,68 +270,81 @@ private
|
|
|
197
270
|
find_xpath(XPath.ancestor(:select)[1]).first
|
|
198
271
|
end
|
|
199
272
|
|
|
200
|
-
def set_text(value, clear: nil, **_unused)
|
|
201
|
-
|
|
273
|
+
def set_text(value, clear: nil, rapid: nil, **_unused)
|
|
274
|
+
value = value.to_s
|
|
275
|
+
if value.empty? && clear.nil?
|
|
202
276
|
native.clear
|
|
203
277
|
elsif clear == :backspace
|
|
204
278
|
# Clear field by sending the correct number of backspace keys.
|
|
205
279
|
backspaces = [:backspace] * self.value.to_s.length
|
|
206
|
-
|
|
207
|
-
elsif clear == :none
|
|
208
|
-
native.send_keys(value.to_s)
|
|
280
|
+
send_keys(:end, *backspaces, value)
|
|
209
281
|
elsif clear.is_a? Array
|
|
210
|
-
|
|
282
|
+
send_keys(*clear, value)
|
|
211
283
|
else
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
284
|
+
driver.execute_script 'arguments[0].select()', self unless clear == :none
|
|
285
|
+
if rapid == true || ((value.length > auto_rapid_set_length) && rapid != false)
|
|
286
|
+
send_keys(value[0..3])
|
|
287
|
+
driver.execute_script RAPID_APPEND_TEXT, self, value[4...-3]
|
|
288
|
+
send_keys(value[-3..])
|
|
289
|
+
else
|
|
290
|
+
send_keys(value)
|
|
291
|
+
end
|
|
217
292
|
end
|
|
218
293
|
end
|
|
219
294
|
|
|
220
|
-
def
|
|
221
|
-
|
|
222
|
-
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
|
223
|
-
scroll_to_center
|
|
224
|
-
yield
|
|
295
|
+
def auto_rapid_set_length
|
|
296
|
+
30
|
|
225
297
|
end
|
|
226
298
|
|
|
227
|
-
def
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
# Swallow error if scrollIntoView with options isn't supported
|
|
299
|
+
def perform_with_options(click_options, &block)
|
|
300
|
+
raise ArgumentError, 'A block must be provided' unless block
|
|
301
|
+
|
|
302
|
+
scroll_if_needed do
|
|
303
|
+
action_with_modifiers(click_options) do |action|
|
|
304
|
+
if block
|
|
305
|
+
yield action
|
|
306
|
+
else
|
|
307
|
+
click_options.coords? ? action.click : action.click(native)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
239
310
|
end
|
|
240
311
|
end
|
|
241
312
|
|
|
242
313
|
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
|
243
|
-
|
|
314
|
+
value = SettableValue.new(value)
|
|
315
|
+
return set_text(value) unless value.dateable?
|
|
316
|
+
|
|
244
317
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
|
245
|
-
update_value_js(value.
|
|
318
|
+
update_value_js(value.to_date_str)
|
|
246
319
|
end
|
|
247
320
|
|
|
248
321
|
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
|
249
|
-
|
|
322
|
+
value = SettableValue.new(value)
|
|
323
|
+
return set_text(value) unless value.timeable?
|
|
324
|
+
|
|
250
325
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
|
251
|
-
update_value_js(value.
|
|
326
|
+
update_value_js(value.to_time_str)
|
|
252
327
|
end
|
|
253
328
|
|
|
254
329
|
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
|
255
|
-
|
|
330
|
+
value = SettableValue.new(value)
|
|
331
|
+
return set_text(value) unless value.timeable?
|
|
332
|
+
|
|
256
333
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
|
257
|
-
update_value_js(value.
|
|
334
|
+
update_value_js(value.to_datetime_str)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def set_color(value) # rubocop:disable Naming/AccessorMethodName
|
|
338
|
+
update_value_js(value)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def set_range(value) # rubocop:disable Naming/AccessorMethodName
|
|
342
|
+
update_value_js(value)
|
|
258
343
|
end
|
|
259
344
|
|
|
260
345
|
def update_value_js(value)
|
|
261
346
|
driver.execute_script(<<-JS, self, value)
|
|
347
|
+
if (arguments[0].readOnly) { return };
|
|
262
348
|
if (document.activeElement !== arguments[0]){
|
|
263
349
|
arguments[0].focus();
|
|
264
350
|
}
|
|
@@ -271,64 +357,280 @@ private
|
|
|
271
357
|
end
|
|
272
358
|
|
|
273
359
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
|
274
|
-
|
|
275
|
-
|
|
360
|
+
with_file_detector do
|
|
361
|
+
path_names = value.to_s.empty? ? [] : value
|
|
362
|
+
file_names = Array(path_names).map do |pn|
|
|
363
|
+
Pathname.new(pn).absolute? ? pn : File.expand_path(pn)
|
|
364
|
+
end.join("\n")
|
|
365
|
+
native.send_keys(file_names)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def with_file_detector
|
|
370
|
+
if driver.options[:browser] == :remote &&
|
|
371
|
+
bridge.respond_to?(:file_detector) &&
|
|
372
|
+
bridge.file_detector.nil?
|
|
373
|
+
begin
|
|
374
|
+
bridge.file_detector = lambda do |(fn, *)|
|
|
375
|
+
str = fn.to_s
|
|
376
|
+
str if File.exist?(str)
|
|
377
|
+
end
|
|
378
|
+
yield
|
|
379
|
+
ensure
|
|
380
|
+
bridge.file_detector = nil
|
|
381
|
+
end
|
|
382
|
+
else
|
|
383
|
+
yield
|
|
384
|
+
end
|
|
276
385
|
end
|
|
277
386
|
|
|
278
387
|
def set_content_editable(value) # rubocop:disable Naming/AccessorMethodName
|
|
279
388
|
# Ensure we are focused on the element
|
|
280
389
|
click
|
|
281
390
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
391
|
+
editable = driver.execute_script <<-JS, self
|
|
392
|
+
if (arguments[0].isContentEditable) {
|
|
393
|
+
var range = document.createRange();
|
|
394
|
+
var sel = window.getSelection();
|
|
395
|
+
arguments[0].focus();
|
|
396
|
+
range.selectNodeContents(arguments[0]);
|
|
397
|
+
sel.removeAllRanges();
|
|
398
|
+
sel.addRange(range);
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
289
402
|
JS
|
|
290
|
-
driver.execute_script script, self
|
|
291
403
|
|
|
292
404
|
# The action api has a speed problem but both chrome and firefox 58 raise errors
|
|
293
405
|
# if we use the faster direct send_keys. For now just send_keys to the element
|
|
294
406
|
# we've already focused.
|
|
295
407
|
# native.send_keys(value.to_s)
|
|
296
|
-
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def action_with_modifiers(
|
|
300
|
-
actions =
|
|
301
|
-
|
|
302
|
-
|
|
408
|
+
browser_action.send_keys(value.to_s).perform if editable
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def action_with_modifiers(click_options)
|
|
412
|
+
actions = browser_action.tap do |acts|
|
|
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
|
|
425
|
+
else
|
|
426
|
+
acts.move_to(native)
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
modifiers_down(actions, click_options.keys)
|
|
303
430
|
yield actions
|
|
304
|
-
modifiers_up(actions, keys)
|
|
431
|
+
modifiers_up(actions, click_options.keys)
|
|
305
432
|
actions.perform
|
|
306
433
|
ensure
|
|
307
|
-
|
|
308
|
-
|
|
434
|
+
act = browser_action
|
|
435
|
+
act.release_actions if act.respond_to?(:release_actions)
|
|
309
436
|
end
|
|
310
437
|
|
|
311
438
|
def modifiers_down(actions, keys)
|
|
312
|
-
keys
|
|
313
|
-
|
|
314
|
-
when :ctrl then :control
|
|
315
|
-
when :command, :cmd then :meta
|
|
316
|
-
else
|
|
317
|
-
key
|
|
318
|
-
end
|
|
319
|
-
actions.key_down(key)
|
|
320
|
-
end
|
|
439
|
+
each_key(keys) { |key| actions.key_down(key) }
|
|
440
|
+
actions
|
|
321
441
|
end
|
|
322
442
|
|
|
323
443
|
def modifiers_up(actions, keys)
|
|
324
|
-
keys
|
|
325
|
-
|
|
444
|
+
each_key(keys) { |key| actions.key_up(key) }
|
|
445
|
+
actions
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def browser
|
|
449
|
+
driver.browser
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def bridge
|
|
453
|
+
browser.send(:bridge)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def browser_action
|
|
457
|
+
browser.action
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def capabilities
|
|
461
|
+
browser.capabilities
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def action_pause(action, duration)
|
|
465
|
+
action.pause(device: action.pointer_inputs.first, duration: duration)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def normalize_keys(keys)
|
|
469
|
+
keys.map do |key|
|
|
470
|
+
case key
|
|
326
471
|
when :ctrl then :control
|
|
327
472
|
when :command, :cmd then :meta
|
|
328
473
|
else
|
|
329
474
|
key
|
|
330
475
|
end
|
|
331
|
-
actions.key_up(key)
|
|
332
476
|
end
|
|
333
477
|
end
|
|
478
|
+
|
|
479
|
+
def each_key(keys, &block)
|
|
480
|
+
normalize_keys(keys).each(&block)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def find_context
|
|
484
|
+
native
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def build_node(native_node, initial_cache = {})
|
|
488
|
+
self.class.new(driver, native_node, initial_cache)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def attrs(*attr_names)
|
|
492
|
+
return attr_names.map { |name| self[name.to_s] } if ENV['CAPYBARA_THOROUGH']
|
|
493
|
+
|
|
494
|
+
driver.evaluate_script <<~JS, self, attr_names.map(&:to_s)
|
|
495
|
+
(function(el, names){
|
|
496
|
+
return names.map(function(name){
|
|
497
|
+
return el[name]
|
|
498
|
+
});
|
|
499
|
+
})(arguments[0], arguments[1]);
|
|
500
|
+
JS
|
|
501
|
+
end
|
|
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
|
+
|
|
513
|
+
GET_XPATH_SCRIPT = <<~'JS'
|
|
514
|
+
(function(el, xml){
|
|
515
|
+
var xpath = '';
|
|
516
|
+
var pos, tempitem2;
|
|
517
|
+
|
|
518
|
+
if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
|
|
519
|
+
return "(: Shadow DOM element - no XPath :)";
|
|
520
|
+
};
|
|
521
|
+
while(el !== xml.documentElement) {
|
|
522
|
+
pos = 0;
|
|
523
|
+
tempitem2 = el;
|
|
524
|
+
while(tempitem2) {
|
|
525
|
+
if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
|
|
526
|
+
pos += 1;
|
|
527
|
+
}
|
|
528
|
+
tempitem2 = tempitem2.previousSibling;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (el.namespaceURI != xml.documentElement.namespaceURI) {
|
|
532
|
+
xpath = "*[local-name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
|
|
533
|
+
} else {
|
|
534
|
+
xpath = el.nodeName.toUpperCase()+"["+pos+"]/"+xpath;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
el = el.parentNode;
|
|
538
|
+
}
|
|
539
|
+
xpath = '/'+xml.documentElement.nodeName.toUpperCase()+'/'+xpath;
|
|
540
|
+
xpath = xpath.replace(/\/$/, '');
|
|
541
|
+
return xpath;
|
|
542
|
+
})(arguments[0], document)
|
|
543
|
+
JS
|
|
544
|
+
|
|
545
|
+
OBSCURED_OR_OFFSET_SCRIPT = <<~JS
|
|
546
|
+
(function(el, x, y) {
|
|
547
|
+
var box = el.getBoundingClientRect();
|
|
548
|
+
if (x == null) x = box.width/2;
|
|
549
|
+
if (y == null) y = box.height/2 ;
|
|
550
|
+
|
|
551
|
+
var px = box.left + x,
|
|
552
|
+
py = box.top + y,
|
|
553
|
+
e = document.elementFromPoint(px, py);
|
|
554
|
+
|
|
555
|
+
if (!el.contains(e))
|
|
556
|
+
return true;
|
|
557
|
+
|
|
558
|
+
return { x: px, y: py };
|
|
559
|
+
})(arguments[0], arguments[1], arguments[2])
|
|
560
|
+
JS
|
|
561
|
+
|
|
562
|
+
RAPID_APPEND_TEXT = <<~JS
|
|
563
|
+
(function(el, value) {
|
|
564
|
+
value = el.value + value;
|
|
565
|
+
if (el.maxLength && el.maxLength != -1){
|
|
566
|
+
value = value.slice(0, el.maxLength);
|
|
567
|
+
}
|
|
568
|
+
el.value = value;
|
|
569
|
+
})(arguments[0], arguments[1])
|
|
570
|
+
JS
|
|
571
|
+
|
|
572
|
+
# SettableValue encapsulates time/date field formatting
|
|
573
|
+
class SettableValue
|
|
574
|
+
attr_reader :value
|
|
575
|
+
|
|
576
|
+
def initialize(value)
|
|
577
|
+
@value = value
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def to_s
|
|
581
|
+
value.to_s
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def dateable?
|
|
585
|
+
!value.is_a?(String) && value.respond_to?(:to_date)
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def to_date_str
|
|
589
|
+
value.to_date.iso8601
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def timeable?
|
|
593
|
+
!value.is_a?(String) && value.respond_to?(:to_time)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def to_time_str
|
|
597
|
+
value.to_time.strftime('%H:%M')
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def to_datetime_str
|
|
601
|
+
value.to_time.strftime('%Y-%m-%dT%H:%M')
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
private_constant :SettableValue
|
|
605
|
+
|
|
606
|
+
# ClickOptions encapsulates click option logic
|
|
607
|
+
class ClickOptions
|
|
608
|
+
attr_reader :keys, :options
|
|
609
|
+
|
|
610
|
+
def initialize(keys, options)
|
|
611
|
+
@keys = keys
|
|
612
|
+
@options = options
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def coords?
|
|
616
|
+
options[:x] && options[:y]
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def coords
|
|
620
|
+
[options[:x], options[:y]]
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def center_offset?
|
|
624
|
+
options[:offset] == :center
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def empty?
|
|
628
|
+
keys.empty? && !coords? && delay.zero?
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def delay
|
|
632
|
+
options[:delay] || 0
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
private_constant :ClickOptions
|
|
334
636
|
end
|