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