capybara 3.0.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 -1
- data/History.md +891 -12
- data/License.txt +1 -1
- data/README.md +257 -84
- 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 +38 -9
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +57 -8
- data/lib/capybara/minitest/spec.rb +185 -84
- data/lib/capybara/minitest.rb +264 -145
- data/lib/capybara/node/actions.rb +248 -124
- data/lib/capybara/node/base.rb +35 -20
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +13 -15
- data/lib/capybara/node/element.rb +350 -113
- data/lib/capybara/node/finders.rb +104 -82
- data/lib/capybara/node/matchers.rb +363 -157
- data/lib/capybara/node/simple.rb +54 -15
- 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 +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 +617 -104
- 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 +106 -31
- 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 +74 -49
- data/lib/capybara/rack_test/node.rb +120 -47
- 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 +87 -53
- data/lib/capybara/rspec/features.rb +8 -10
- 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 +142 -315
- 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 +85 -8
- 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 +72 -28
- 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 +211 -0
- data/lib/capybara/selector/selector.rb +89 -200
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -534
- 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 +270 -245
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
- 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 +460 -170
- 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 +80 -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 +40 -6
- data/lib/capybara/session/matchers.rb +26 -19
- data/lib/capybara/session.rb +252 -194
- 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 +126 -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 -44
- 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} +76 -52
- data/lib/capybara/spec/session/{assert_title.rb → 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 +11 -13
- data/lib/capybara/spec/session/check_spec.rb +112 -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 +89 -55
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- 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 +110 -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 +42 -14
- data/lib/capybara/spec/session/find_spec.rb +251 -142
- data/lib/capybara/spec/session/first_spec.rb +45 -44
- 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 +46 -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 +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 +98 -12
- data/lib/capybara/spec/session/has_css_spec.rb +271 -137
- 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 +46 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
- 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.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 +37 -0
- data/lib/capybara/spec/session/node_spec.rb +958 -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 +65 -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 +119 -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 +41 -22
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +79 -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 +11 -8
- data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
- data/lib/capybara/spec/session/within_spec.rb +82 -44
- data/lib/capybara/spec/spec_helper.rb +46 -52
- data/lib/capybara/spec/test_app.rb +148 -41
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +156 -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 +46 -11
- 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.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 +32 -26
- data/lib/capybara.rb +128 -104
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +65 -51
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +84 -55
- 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 +5 -5
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
- data/spec/minitest_spec.rb +49 -5
- data/spec/minitest_spec_spec.rb +92 -62
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +183 -115
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +99 -39
- data/spec/rspec/features_spec.rb +28 -25
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +418 -364
- data/spec/rspec/views_spec.rb +4 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +109 -85
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +392 -62
- data/spec/selenium_spec_chrome.rb +183 -41
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- 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 +192 -81
- data/spec/session_spec.rb +52 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +460 -123
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +344 -45
- 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,30 +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
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
)
|
10
|
+
include Capybara::Node::WhitespaceNormalizer
|
11
|
+
include Capybara::Selenium::Find
|
12
|
+
include Capybara::Selenium::Scroll
|
16
13
|
|
17
14
|
def visible_text
|
15
|
+
raise NotImplementedError, 'Getting visible text is not currently supported directly on shadow roots' if shadow_root?
|
16
|
+
|
18
17
|
native.text
|
19
18
|
end
|
20
19
|
|
21
20
|
def all_text
|
22
|
-
text = driver.
|
23
|
-
text
|
24
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
25
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
|
26
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
|
27
|
-
.tr("\u00a0", ' ')
|
21
|
+
text = driver.evaluate_script('arguments[0].textContent', self) || ''
|
22
|
+
normalize_spacing(text)
|
28
23
|
end
|
29
24
|
|
30
25
|
def [](name)
|
@@ -34,13 +29,17 @@ 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.to_h { |style| [style, native.css_value(style)] }
|
41
|
+
end
|
42
|
+
|
44
43
|
##
|
45
44
|
#
|
46
45
|
# Set the value of the form element to the given value.
|
@@ -52,12 +51,22 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
52
51
|
# :none => append the new value to the existing value <br/>
|
53
52
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
54
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/>
|
55
59
|
def set(value, **options)
|
56
|
-
|
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
|
57
66
|
|
58
67
|
case tag_name
|
59
68
|
when 'input'
|
60
|
-
case
|
69
|
+
case type
|
61
70
|
when 'radio'
|
62
71
|
click
|
63
72
|
when 'checkbox'
|
@@ -70,71 +79,74 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
70
79
|
set_time(value)
|
71
80
|
when 'datetime-local'
|
72
81
|
set_datetime_local(value)
|
82
|
+
when 'color'
|
83
|
+
set_color(value)
|
84
|
+
when 'range'
|
85
|
+
set_range(value)
|
73
86
|
else
|
74
|
-
set_text(value, options)
|
87
|
+
set_text(value, **options)
|
75
88
|
end
|
76
89
|
when 'textarea'
|
77
|
-
set_text(value, options)
|
90
|
+
set_text(value, **options)
|
78
91
|
else
|
79
|
-
set_content_editable(value)
|
92
|
+
set_content_editable(value)
|
80
93
|
end
|
81
94
|
end
|
82
95
|
|
83
96
|
def select_option
|
84
|
-
|
97
|
+
click unless selected? || disabled?
|
85
98
|
end
|
86
99
|
|
87
100
|
def unselect_option
|
88
|
-
raise Capybara::UnselectNotAllowed,
|
89
|
-
|
101
|
+
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
102
|
+
|
103
|
+
click if selected?
|
90
104
|
end
|
91
105
|
|
92
|
-
def click(keys = [], options
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
106
|
+
def click(keys = [], **options)
|
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
|
104
118
|
end
|
105
119
|
end
|
106
|
-
rescue => e
|
120
|
+
rescue StandardError => e
|
107
121
|
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
|
122
|
+
e.message.include?('Other element would receive the click')
|
123
|
+
scroll_to_center
|
113
124
|
end
|
125
|
+
|
114
126
|
raise e
|
115
127
|
end
|
116
128
|
|
117
|
-
def right_click(keys = [], options
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
129
|
+
def right_click(keys = [], **options)
|
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)
|
125
140
|
end
|
126
141
|
end
|
127
142
|
end
|
128
143
|
|
129
|
-
def double_click(keys = [], options
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
a.double_click(native)
|
136
|
-
end
|
137
|
-
end
|
144
|
+
def double_click(keys = [], **options)
|
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)
|
138
150
|
end
|
139
151
|
end
|
140
152
|
|
@@ -143,15 +155,33 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
143
155
|
end
|
144
156
|
|
145
157
|
def hover
|
146
|
-
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
|
147
172
|
end
|
148
173
|
|
149
|
-
def
|
150
|
-
|
174
|
+
def drop(*_)
|
175
|
+
raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
|
151
176
|
end
|
152
177
|
|
153
178
|
def tag_name
|
154
|
-
|
179
|
+
@tag_name ||=
|
180
|
+
if native.respond_to? :tag_name
|
181
|
+
native.tag_name.downcase
|
182
|
+
else
|
183
|
+
shadow_root? ? 'ShadowRoot' : 'Unknown'
|
184
|
+
end
|
155
185
|
end
|
156
186
|
|
157
187
|
def visible?; boolean_attr(native.displayed?); end
|
@@ -163,55 +193,76 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
163
193
|
def disabled?
|
164
194
|
return true unless native.enabled?
|
165
195
|
|
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
|
196
|
+
# WebDriver only defines `disabled?` for form controls but fieldset makes sense too
|
197
|
+
find_xpath('self::fieldset/ancestor-or-self::fieldset[@disabled]').any?
|
176
198
|
end
|
177
199
|
|
178
200
|
def content_editable?
|
179
|
-
native.attribute('isContentEditable')
|
201
|
+
native.attribute('isContentEditable') == 'true'
|
202
|
+
end
|
203
|
+
|
204
|
+
def path
|
205
|
+
driver.evaluate_script GET_XPATH_SCRIPT, self
|
180
206
|
end
|
181
207
|
|
182
|
-
def
|
183
|
-
|
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'])
|
184
213
|
end
|
185
214
|
|
186
|
-
def
|
187
|
-
native.
|
215
|
+
def rect
|
216
|
+
native.rect
|
188
217
|
end
|
189
218
|
|
190
|
-
def
|
191
|
-
|
219
|
+
def shadow_root
|
220
|
+
root = native.shadow_root
|
221
|
+
root && build_node(native.shadow_root)
|
192
222
|
end
|
193
223
|
|
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
|
224
|
+
protected
|
225
|
+
|
226
|
+
def scroll_if_needed
|
227
|
+
yield
|
228
|
+
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
229
|
+
scroll_to_center
|
230
|
+
yield
|
231
|
+
end
|
207
232
|
|
208
|
-
|
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
|
209
246
|
end
|
210
247
|
|
211
248
|
private
|
212
249
|
|
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
|
262
|
+
end
|
263
|
+
|
213
264
|
def boolean_attr(val)
|
214
|
-
val
|
265
|
+
val && (val != 'false')
|
215
266
|
end
|
216
267
|
|
217
268
|
# a reference to the select node if this is an option node
|
@@ -219,71 +270,117 @@ private
|
|
219
270
|
find_xpath(XPath.ancestor(:select)[1]).first
|
220
271
|
end
|
221
272
|
|
222
|
-
def set_text(value, clear: nil, **_unused)
|
223
|
-
|
273
|
+
def set_text(value, clear: nil, rapid: nil, **_unused)
|
274
|
+
value = value.to_s
|
275
|
+
if value.empty? && clear.nil?
|
224
276
|
native.clear
|
225
277
|
elsif clear == :backspace
|
226
278
|
# Clear field by sending the correct number of backspace keys.
|
227
279
|
backspaces = [:backspace] * self.value.to_s.length
|
228
|
-
|
229
|
-
elsif clear == :none
|
230
|
-
native.send_keys(value.to_s)
|
280
|
+
send_keys(:end, *backspaces, value)
|
231
281
|
elsif clear.is_a? Array
|
232
|
-
|
282
|
+
send_keys(*clear, value)
|
233
283
|
else
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
239
292
|
end
|
240
293
|
end
|
241
294
|
|
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
|
295
|
+
def auto_rapid_set_length
|
296
|
+
30
|
254
297
|
end
|
255
298
|
|
256
|
-
def
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|
261
310
|
end
|
262
311
|
end
|
263
312
|
|
313
|
+
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
314
|
+
value = SettableValue.new(value)
|
315
|
+
return set_text(value) unless value.dateable?
|
316
|
+
|
317
|
+
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
318
|
+
update_value_js(value.to_date_str)
|
319
|
+
end
|
320
|
+
|
264
321
|
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
322
|
+
value = SettableValue.new(value)
|
323
|
+
return set_text(value) unless value.timeable?
|
324
|
+
|
325
|
+
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
326
|
+
update_value_js(value.to_time_str)
|
270
327
|
end
|
271
328
|
|
272
329
|
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
330
|
+
value = SettableValue.new(value)
|
331
|
+
return set_text(value) unless value.timeable?
|
332
|
+
|
333
|
+
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
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)
|
343
|
+
end
|
344
|
+
|
345
|
+
def update_value_js(value)
|
346
|
+
driver.execute_script(<<-JS, self, value)
|
347
|
+
if (arguments[0].readOnly) { return };
|
348
|
+
if (document.activeElement !== arguments[0]){
|
349
|
+
arguments[0].focus();
|
350
|
+
}
|
351
|
+
if (arguments[0].value != arguments[1]) {
|
352
|
+
arguments[0].value = arguments[1]
|
353
|
+
arguments[0].dispatchEvent(new InputEvent('input'));
|
354
|
+
arguments[0].dispatchEvent(new Event('change', { bubbles: true }));
|
355
|
+
}
|
356
|
+
JS
|
278
357
|
end
|
279
358
|
|
280
359
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
285
382
|
else
|
286
|
-
|
383
|
+
yield
|
287
384
|
end
|
288
385
|
end
|
289
386
|
|
@@ -291,56 +388,249 @@ private
|
|
291
388
|
# Ensure we are focused on the element
|
292
389
|
click
|
293
390
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
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;
|
301
402
|
JS
|
302
|
-
driver.execute_script script, self
|
303
403
|
|
304
404
|
# The action api has a speed problem but both chrome and firefox 58 raise errors
|
305
405
|
# if we use the faster direct send_keys. For now just send_keys to the element
|
306
406
|
# we've already focused.
|
307
407
|
# native.send_keys(value.to_s)
|
308
|
-
|
408
|
+
browser_action.send_keys(value.to_s).perform if editable
|
309
409
|
end
|
310
410
|
|
311
|
-
def action_with_modifiers(
|
312
|
-
actions =
|
313
|
-
|
314
|
-
|
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)
|
315
430
|
yield actions
|
316
|
-
modifiers_up(actions, keys)
|
431
|
+
modifiers_up(actions, click_options.keys)
|
317
432
|
actions.perform
|
318
433
|
ensure
|
319
|
-
|
320
|
-
|
434
|
+
act = browser_action
|
435
|
+
act.release_actions if act.respond_to?(:release_actions)
|
321
436
|
end
|
322
437
|
|
323
438
|
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
|
439
|
+
each_key(keys) { |key| actions.key_down(key) }
|
440
|
+
actions
|
333
441
|
end
|
334
442
|
|
335
443
|
def modifiers_up(actions, keys)
|
336
|
-
keys
|
337
|
-
|
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
|
338
471
|
when :ctrl then :control
|
339
472
|
when :command, :cmd then :meta
|
340
473
|
else
|
341
474
|
key
|
342
475
|
end
|
343
|
-
actions.key_up(key)
|
344
476
|
end
|
345
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
|
346
636
|
end
|