capybara 3.32.2
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 +7 -0
- data/.yardopts +1 -0
- data/History.md +1813 -0
- data/License.txt +22 -0
- data/README.md +1099 -0
- data/lib/capybara.rb +511 -0
- data/lib/capybara/config.rb +94 -0
- data/lib/capybara/cucumber.rb +27 -0
- data/lib/capybara/driver/base.rb +170 -0
- data/lib/capybara/driver/node.rb +139 -0
- data/lib/capybara/dsl.rb +65 -0
- data/lib/capybara/helpers.rb +108 -0
- data/lib/capybara/minitest.rb +386 -0
- data/lib/capybara/minitest/spec.rb +264 -0
- data/lib/capybara/node/actions.rb +420 -0
- data/lib/capybara/node/base.rb +143 -0
- data/lib/capybara/node/document.rb +48 -0
- data/lib/capybara/node/document_matchers.rb +67 -0
- data/lib/capybara/node/element.rb +606 -0
- data/lib/capybara/node/finders.rb +325 -0
- data/lib/capybara/node/matchers.rb +883 -0
- data/lib/capybara/node/simple.rb +208 -0
- data/lib/capybara/queries/ancestor_query.rb +27 -0
- data/lib/capybara/queries/base_query.rb +106 -0
- data/lib/capybara/queries/current_path_query.rb +51 -0
- data/lib/capybara/queries/match_query.rb +26 -0
- data/lib/capybara/queries/selector_query.rb +710 -0
- data/lib/capybara/queries/sibling_query.rb +26 -0
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +110 -0
- data/lib/capybara/queries/title_query.rb +39 -0
- data/lib/capybara/rack_test/browser.rb +140 -0
- data/lib/capybara/rack_test/css_handlers.rb +13 -0
- data/lib/capybara/rack_test/driver.rb +109 -0
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +127 -0
- data/lib/capybara/rack_test/node.rb +325 -0
- data/lib/capybara/rails.rb +16 -0
- data/lib/capybara/registrations/drivers.rb +36 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
- data/lib/capybara/registrations/servers.rb +44 -0
- data/lib/capybara/result.rb +190 -0
- data/lib/capybara/rspec.rb +29 -0
- data/lib/capybara/rspec/features.rb +23 -0
- data/lib/capybara/rspec/matcher_proxies.rb +82 -0
- data/lib/capybara/rspec/matchers.rb +201 -0
- 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 +38 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/selector.rb +233 -0
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
- data/lib/capybara/selector/css.rb +102 -0
- data/lib/capybara/selector/definition.rb +276 -0
- data/lib/capybara/selector/definition/button.rb +51 -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 +27 -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 +46 -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/filter.rb +5 -0
- data/lib/capybara/selector/filter_set.rb +124 -0
- data/lib/capybara/selector/filters/base.rb +77 -0
- data/lib/capybara/selector/filters/expression_filter.rb +22 -0
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +31 -0
- data/lib/capybara/selector/regexp_disassembler.rb +214 -0
- data/lib/capybara/selector/selector.rb +147 -0
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- 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 +496 -0
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +119 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +126 -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 +78 -0
- data/lib/capybara/selenium/logger_suppressor.rb +34 -0
- data/lib/capybara/selenium/node.rb +610 -0
- data/lib/capybara/selenium/nodes/chrome_node.rb +119 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +131 -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 +47 -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.rb +126 -0
- data/lib/capybara/server/animation_disabler.rb +58 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +69 -0
- data/lib/capybara/session.rb +942 -0
- data/lib/capybara/session/config.rb +124 -0
- data/lib/capybara/session/matchers.rb +87 -0
- data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
- data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/fixtures/test_file.txt +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +13 -0
- data/lib/capybara/spec/public/jquery.js +5 -0
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +268 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +81 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +32 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +78 -0
- data/lib/capybara/spec/session/all_spec.rb +278 -0
- data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
- 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_spec.rb +258 -0
- data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +216 -0
- data/lib/capybara/spec/session/body_spec.rb +23 -0
- data/lib/capybara/spec/session/check_spec.rb +235 -0
- data/lib/capybara/spec/session/choose_spec.rb +121 -0
- data/lib/capybara/spec/session/click_button_spec.rb +506 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +129 -0
- data/lib/capybara/spec/session/click_link_spec.rb +229 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +31 -0
- data/lib/capybara/spec/session/current_url_spec.rb +115 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +36 -0
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +21 -0
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +38 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +31 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +25 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +120 -0
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +49 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +28 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +286 -0
- data/lib/capybara/spec/session/find_button_spec.rb +74 -0
- data/lib/capybara/spec/session/find_by_id_spec.rb +33 -0
- data/lib/capybara/spec/session/find_field_spec.rb +113 -0
- data/lib/capybara/spec/session/find_link_spec.rb +70 -0
- data/lib/capybara/spec/session/find_spec.rb +531 -0
- data/lib/capybara/spec/session/first_spec.rb +156 -0
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
- data/lib/capybara/spec/session/go_back_spec.rb +12 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +14 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
- data/lib/capybara/spec/session/has_button_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +374 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +138 -0
- data/lib/capybara/spec/session/has_field_spec.rb +349 -0
- data/lib/capybara/spec/session/has_link_spec.rb +39 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
- data/lib/capybara/spec/session/has_select_spec.rb +310 -0
- data/lib/capybara/spec/session/has_selector_spec.rb +202 -0
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +198 -0
- data/lib/capybara/spec/session/has_text_spec.rb +394 -0
- data/lib/capybara/spec/session/has_title_spec.rb +71 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +149 -0
- data/lib/capybara/spec/session/headers_spec.rb +8 -0
- data/lib/capybara/spec/session/html_spec.rb +47 -0
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/node_spec.rb +1292 -0
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +33 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +148 -0
- data/lib/capybara/spec/session/response_code_spec.rb +8 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +21 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +43 -0
- data/lib/capybara/spec/session/save_page_spec.rb +110 -0
- data/lib/capybara/spec/session/save_screenshot_spec.rb +55 -0
- data/lib/capybara/spec/session/screenshot_spec.rb +18 -0
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +229 -0
- data/lib/capybara/spec/session/selectors_spec.rb +98 -0
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/session/text_spec.rb +74 -0
- data/lib/capybara/spec/session/title_spec.rb +29 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +100 -0
- data/lib/capybara/spec/session/unselect_spec.rb +116 -0
- data/lib/capybara/spec/session/visit_spec.rb +204 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +89 -0
- data/lib/capybara/spec/session/window/current_window_spec.rb +28 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +31 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +132 -0
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +99 -0
- data/lib/capybara/spec/session/window/window_spec.rb +203 -0
- data/lib/capybara/spec/session/window/windows_spec.rb +34 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +157 -0
- data/lib/capybara/spec/session/within_spec.rb +199 -0
- data/lib/capybara/spec/spec_helper.rb +134 -0
- data/lib/capybara/spec/test_app.rb +226 -0
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/buttons.erb +5 -0
- data/lib/capybara/spec/views/fieldsets.erb +30 -0
- data/lib/capybara/spec/views/form.erb +685 -0
- data/lib/capybara/spec/views/frame_child.erb +18 -0
- data/lib/capybara/spec/views/frame_one.erb +10 -0
- data/lib/capybara/spec/views/frame_parent.erb +9 -0
- data/lib/capybara/spec/views/frame_two.erb +9 -0
- data/lib/capybara/spec/views/header_links.erb +8 -0
- data/lib/capybara/spec/views/host_links.erb +13 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +32 -0
- data/lib/capybara/spec/views/path.erb +13 -0
- data/lib/capybara/spec/views/popup_one.erb +9 -0
- data/lib/capybara/spec/views/popup_two.erb +9 -0
- data/lib/capybara/spec/views/postback.erb +14 -0
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +130 -0
- data/lib/capybara/spec/views/with_animation.erb +74 -0
- data/lib/capybara/spec/views/with_base_tag.erb +11 -0
- data/lib/capybara/spec/views/with_count.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +22 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +24 -0
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +208 -0
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_html_entities.erb +2 -0
- data/lib/capybara/spec/views/with_js.erb +160 -0
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +20 -0
- data/lib/capybara/spec/views/with_scope.erb +42 -0
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_simple_html.erb +2 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_title.erb +5 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
- data/lib/capybara/spec/views/with_windows.erb +54 -0
- data/lib/capybara/spec/views/within_frames.erb +15 -0
- data/lib/capybara/version.rb +5 -0
- data/lib/capybara/window.rb +146 -0
- data/spec/basic_node_spec.rb +154 -0
- data/spec/capybara_spec.rb +112 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +276 -0
- data/spec/filter_set_spec.rb +46 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +13 -0
- data/spec/fixtures/selenium_driver_rspec_success.rb +13 -0
- data/spec/minitest_spec.rb +163 -0
- data/spec/minitest_spec_spec.rb +162 -0
- data/spec/per_session_config_spec.rb +68 -0
- data/spec/rack_test_spec.rb +268 -0
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +196 -0
- data/spec/rspec/features_spec.rb +99 -0
- data/spec/rspec/scenarios_spec.rb +19 -0
- data/spec/rspec/shared_spec_matchers.rb +947 -0
- data/spec/rspec/views_spec.rb +14 -0
- data/spec/rspec_matchers_spec.rb +62 -0
- data/spec/rspec_spec.rb +145 -0
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +513 -0
- data/spec/selenium_spec_chrome.rb +188 -0
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +47 -0
- data/spec/selenium_spec_firefox.rb +208 -0
- data/spec/selenium_spec_firefox_remote.rb +80 -0
- data/spec/selenium_spec_ie.rb +150 -0
- data/spec/selenium_spec_safari.rb +148 -0
- data/spec/server_spec.rb +292 -0
- data/spec/session_spec.rb +91 -0
- data/spec/shared_selenium_node.rb +83 -0
- data/spec/shared_selenium_session.rb +476 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +753 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
module Node
|
|
5
|
+
##
|
|
6
|
+
#
|
|
7
|
+
# A {Capybara::Node::Base} represents either an element on a page through the subclass
|
|
8
|
+
# {Capybara::Node::Element} or a document through {Capybara::Node::Document}.
|
|
9
|
+
#
|
|
10
|
+
# Both types of Node share the same methods, used for interacting with the
|
|
11
|
+
# elements on the page. These methods are divided into three categories,
|
|
12
|
+
# finders, actions and matchers. These are found in the modules
|
|
13
|
+
# {Capybara::Node::Finders}, {Capybara::Node::Actions} and {Capybara::Node::Matchers}
|
|
14
|
+
# respectively.
|
|
15
|
+
#
|
|
16
|
+
# A {Capybara::Session} exposes all methods from {Capybara::Node::Document} directly:
|
|
17
|
+
#
|
|
18
|
+
# session = Capybara::Session.new(:rack_test, my_app)
|
|
19
|
+
# session.visit('/')
|
|
20
|
+
# session.fill_in('Foo', with: 'Bar') # from Capybara::Node::Actions
|
|
21
|
+
# bar = session.find('#bar') # from Capybara::Node::Finders
|
|
22
|
+
# bar.select('Baz', from: 'Quox') # from Capybara::Node::Actions
|
|
23
|
+
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
|
24
|
+
#
|
|
25
|
+
class Base
|
|
26
|
+
attr_reader :session, :base, :query_scope
|
|
27
|
+
|
|
28
|
+
include Capybara::Node::Finders
|
|
29
|
+
include Capybara::Node::Actions
|
|
30
|
+
include Capybara::Node::Matchers
|
|
31
|
+
|
|
32
|
+
def initialize(session, base)
|
|
33
|
+
@session = session
|
|
34
|
+
@base = base
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# overridden in subclasses, e.g. Capybara::Node::Element
|
|
38
|
+
def reload
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
#
|
|
44
|
+
# This method is Capybara's primary defence against asynchronicity
|
|
45
|
+
# problems. It works by attempting to run a given block of code until it
|
|
46
|
+
# succeeds. The exact behaviour of this method depends on a number of
|
|
47
|
+
# factors. Basically there are certain exceptions which, when raised
|
|
48
|
+
# from the block, instead of bubbling up, are caught, and the block is
|
|
49
|
+
# re-run.
|
|
50
|
+
#
|
|
51
|
+
# Certain drivers, such as RackTest, have no support for asynchronous
|
|
52
|
+
# processes, these drivers run the block, and any error raised bubbles up
|
|
53
|
+
# immediately. This allows faster turn around in the case where an
|
|
54
|
+
# expectation fails.
|
|
55
|
+
#
|
|
56
|
+
# Only exceptions that are {Capybara::ElementNotFound} or any subclass
|
|
57
|
+
# thereof cause the block to be rerun. Drivers may specify additional
|
|
58
|
+
# exceptions which also cause reruns. This usually occurs when a node is
|
|
59
|
+
# manipulated which no longer exists on the page. For example, the
|
|
60
|
+
# Selenium driver specifies
|
|
61
|
+
# `Selenium::WebDriver::Error::ObsoleteElementError`.
|
|
62
|
+
#
|
|
63
|
+
# As long as any of these exceptions are thrown, the block is re-run,
|
|
64
|
+
# until a certain amount of time passes. The amount of time defaults to
|
|
65
|
+
# {Capybara.default_max_wait_time} and can be overridden through the `seconds`
|
|
66
|
+
# argument. This time is compared with the system time to see how much
|
|
67
|
+
# time has passed. On rubies/platforms which don't support access to a monotonic process clock
|
|
68
|
+
# if the return value of `Time.now` is stubbed out, Capybara will raise `Capybara::FrozenInTime`.
|
|
69
|
+
#
|
|
70
|
+
# @param [Integer] seconds (current sessions default_max_wait_time) Maximum number of seconds to retry this block
|
|
71
|
+
# @param [Array<Exception>] errors (driver.invalid_element_errors +
|
|
72
|
+
# [Capybara::ElementNotFound]) exception types that cause the block to be rerun
|
|
73
|
+
# @return [Object] The result of the given block
|
|
74
|
+
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
|
|
75
|
+
#
|
|
76
|
+
def synchronize(seconds = nil, errors: nil)
|
|
77
|
+
return yield if session.synchronized
|
|
78
|
+
|
|
79
|
+
seconds = session_options.default_max_wait_time if [nil, true].include? seconds
|
|
80
|
+
session.synchronized = true
|
|
81
|
+
timer = Capybara::Helpers.timer(expire_in: seconds)
|
|
82
|
+
begin
|
|
83
|
+
yield
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
session.raise_server_error!
|
|
86
|
+
raise e unless catch_error?(e, errors)
|
|
87
|
+
|
|
88
|
+
if driver.wait?
|
|
89
|
+
raise e if timer.expired?
|
|
90
|
+
|
|
91
|
+
sleep(0.01)
|
|
92
|
+
reload if session_options.automatic_reload
|
|
93
|
+
else
|
|
94
|
+
old_base = @base
|
|
95
|
+
reload if session_options.automatic_reload
|
|
96
|
+
raise e if old_base == @base
|
|
97
|
+
end
|
|
98
|
+
retry
|
|
99
|
+
ensure
|
|
100
|
+
session.synchronized = false
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @api private
|
|
105
|
+
def find_css(css, **options)
|
|
106
|
+
if base.method(:find_css).arity != 1
|
|
107
|
+
base.find_css(css, **options)
|
|
108
|
+
else
|
|
109
|
+
base.find_css(css)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @api private
|
|
114
|
+
def find_xpath(xpath, **options)
|
|
115
|
+
if base.method(:find_xpath).arity != 1
|
|
116
|
+
base.find_xpath(xpath, **options)
|
|
117
|
+
else
|
|
118
|
+
base.find_xpath(xpath)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @api private
|
|
123
|
+
def session_options
|
|
124
|
+
session.config
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def to_capybara_node
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
protected
|
|
132
|
+
|
|
133
|
+
def catch_error?(error, errors = nil)
|
|
134
|
+
errors ||= (driver.invalid_element_errors + [Capybara::ElementNotFound])
|
|
135
|
+
errors.any? { |type| error.is_a?(type) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def driver
|
|
139
|
+
session.driver
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
module Node
|
|
5
|
+
##
|
|
6
|
+
#
|
|
7
|
+
# A {Capybara::Document} represents an HTML document. Any operation
|
|
8
|
+
# performed on it will be performed on the entire document.
|
|
9
|
+
#
|
|
10
|
+
# @see Capybara::Node
|
|
11
|
+
#
|
|
12
|
+
class Document < Base
|
|
13
|
+
include Capybara::Node::DocumentMatchers
|
|
14
|
+
|
|
15
|
+
def inspect
|
|
16
|
+
%(#<Capybara::Document>)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
#
|
|
21
|
+
# @return [String] The text of the document
|
|
22
|
+
#
|
|
23
|
+
def text(type = nil, normalize_ws: false)
|
|
24
|
+
find(:xpath, '/html').text(type, normalize_ws: normalize_ws)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
#
|
|
29
|
+
# @return [String] The title of the document
|
|
30
|
+
#
|
|
31
|
+
def title
|
|
32
|
+
session.driver.title
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def execute_script(*args)
|
|
36
|
+
find(:xpath, '/html').execute_script(*args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def evaluate_script(*args)
|
|
40
|
+
find(:xpath, '/html').evaluate_script(*args)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def scroll_to(*args, **options)
|
|
44
|
+
find(:xpath, '//body').scroll_to(*args, **options)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
module Node
|
|
5
|
+
module DocumentMatchers
|
|
6
|
+
##
|
|
7
|
+
# Asserts that the page has the given title.
|
|
8
|
+
#
|
|
9
|
+
# @!macro title_query_params
|
|
10
|
+
# @overload $0(string, **options)
|
|
11
|
+
# @param string [String] The string that title should include
|
|
12
|
+
# @overload $0(regexp, **options)
|
|
13
|
+
# @param regexp [Regexp] The regexp that title should match to
|
|
14
|
+
# @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for title to eq/match given string/regexp argument
|
|
15
|
+
# @option options [Boolean] :exact (false) When passed a string should the match be exact or just substring
|
|
16
|
+
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
|
17
|
+
# @return [true]
|
|
18
|
+
#
|
|
19
|
+
def assert_title(title, **options)
|
|
20
|
+
_verify_title(title, options) do |query|
|
|
21
|
+
raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Asserts that the page doesn't have the given title.
|
|
27
|
+
#
|
|
28
|
+
# @macro title_query_params
|
|
29
|
+
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
|
30
|
+
# @return [true]
|
|
31
|
+
#
|
|
32
|
+
def assert_no_title(title, **options)
|
|
33
|
+
_verify_title(title, options) do |query|
|
|
34
|
+
raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Checks if the page has the given title.
|
|
40
|
+
#
|
|
41
|
+
# @macro title_query_params
|
|
42
|
+
# @return [Boolean]
|
|
43
|
+
#
|
|
44
|
+
def has_title?(title, **options)
|
|
45
|
+
make_predicate(options) { assert_title(title, **options) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Checks if the page doesn't have the given title.
|
|
50
|
+
#
|
|
51
|
+
# @macro title_query_params
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
#
|
|
54
|
+
def has_no_title?(title, **options)
|
|
55
|
+
make_predicate(options) { assert_no_title(title, **options) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def _verify_title(title, options)
|
|
61
|
+
query = Capybara::Queries::TitleQuery.new(title, **options)
|
|
62
|
+
synchronize(query.wait) { yield(query) }
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
module Node
|
|
5
|
+
##
|
|
6
|
+
#
|
|
7
|
+
# A {Capybara::Node::Element} represents a single element on the page. It is possible
|
|
8
|
+
# to interact with the contents of this element the same as with a document:
|
|
9
|
+
#
|
|
10
|
+
# session = Capybara::Session.new(:rack_test, my_app)
|
|
11
|
+
#
|
|
12
|
+
# bar = session.find('#bar') # from Capybara::Node::Finders
|
|
13
|
+
# bar.select('Baz', from: 'Quox') # from Capybara::Node::Actions
|
|
14
|
+
#
|
|
15
|
+
# {Capybara::Node::Element} also has access to HTML attributes and other properties of the
|
|
16
|
+
# element:
|
|
17
|
+
#
|
|
18
|
+
# bar.value
|
|
19
|
+
# bar.text
|
|
20
|
+
# bar[:title]
|
|
21
|
+
#
|
|
22
|
+
# @see Capybara::Node
|
|
23
|
+
#
|
|
24
|
+
class Element < Base
|
|
25
|
+
def initialize(session, base, query_scope, query)
|
|
26
|
+
super(session, base)
|
|
27
|
+
@query_scope = query_scope
|
|
28
|
+
@query = query
|
|
29
|
+
@allow_reload = false
|
|
30
|
+
@query_idx = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def allow_reload!(idx = nil)
|
|
34
|
+
@query_idx = idx
|
|
35
|
+
@allow_reload = true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
#
|
|
40
|
+
# @return [Object] The native element from the driver, this allows access to driver specific methods
|
|
41
|
+
#
|
|
42
|
+
def native
|
|
43
|
+
synchronize { base.native }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
#
|
|
48
|
+
# Retrieve the text of the element. If {Capybara.configure ignore_hidden_elements}
|
|
49
|
+
# is `true`, which it is by default, then this will return only text
|
|
50
|
+
# which is visible. The exact semantics of this may differ between
|
|
51
|
+
# drivers, but generally any text within elements with `display:none` is
|
|
52
|
+
# ignored. This behaviour can be overridden by passing `:all` to this
|
|
53
|
+
# method.
|
|
54
|
+
#
|
|
55
|
+
# @param type [:all, :visible] Whether to return only visible or all text
|
|
56
|
+
# @return [String] The text of the element
|
|
57
|
+
#
|
|
58
|
+
def text(type = nil, normalize_ws: false)
|
|
59
|
+
type ||= :all unless session_options.ignore_hidden_elements || session_options.visible_text_only
|
|
60
|
+
txt = synchronize { type == :all ? base.all_text : base.visible_text }
|
|
61
|
+
normalize_ws ? txt.gsub(/[[:space:]]+/, ' ').strip : txt
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
#
|
|
66
|
+
# Retrieve the given attribute.
|
|
67
|
+
#
|
|
68
|
+
# element[:title] # => HTML title attribute
|
|
69
|
+
#
|
|
70
|
+
# @param [Symbol] attribute The attribute to retrieve
|
|
71
|
+
# @return [String] The value of the attribute
|
|
72
|
+
#
|
|
73
|
+
def [](attribute)
|
|
74
|
+
synchronize { base[attribute] }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
#
|
|
79
|
+
# Retrieve the given CSS styles.
|
|
80
|
+
#
|
|
81
|
+
# element.style('color', 'font-size') # => Computed values of CSS 'color' and 'font-size' styles
|
|
82
|
+
#
|
|
83
|
+
# @param [Array<String>] styles Names of the desired CSS properties
|
|
84
|
+
# @return [Hash] Hash of the CSS property names to computed values
|
|
85
|
+
#
|
|
86
|
+
def style(*styles)
|
|
87
|
+
styles = styles.flatten.map(&:to_s)
|
|
88
|
+
raise ArgumentError, 'You must specify at least one CSS style' if styles.empty?
|
|
89
|
+
|
|
90
|
+
begin
|
|
91
|
+
synchronize { base.style(styles) }
|
|
92
|
+
rescue NotImplementedError => e
|
|
93
|
+
begin
|
|
94
|
+
evaluate_script(STYLE_SCRIPT, *styles)
|
|
95
|
+
rescue Capybara::NotSupportedByDriverError
|
|
96
|
+
raise e
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
#
|
|
103
|
+
# @return [String] The value of the form element
|
|
104
|
+
#
|
|
105
|
+
def value
|
|
106
|
+
synchronize { base.value }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
#
|
|
111
|
+
# Set the value of the form element to the given value.
|
|
112
|
+
#
|
|
113
|
+
# @param [String] value The new value
|
|
114
|
+
# @param [Hash] options Driver specific options for how to set the value. Take default values from {Capybara.configure default_set_options}.
|
|
115
|
+
#
|
|
116
|
+
# @return [Capybara::Node::Element] The element
|
|
117
|
+
def set(value, **options)
|
|
118
|
+
if ENV['CAPYBARA_THOROUGH'] && readonly?
|
|
119
|
+
raise Capybara::ReadOnlyElementError, "Attempt to set readonly element with value: #{value}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
options = session_options.default_set_options.to_h.merge(options)
|
|
123
|
+
synchronize { base.set(value, **options) }
|
|
124
|
+
self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
##
|
|
128
|
+
#
|
|
129
|
+
# Select this node if it is an option element inside a select tag.
|
|
130
|
+
#
|
|
131
|
+
# @!macro action_waiting_behavior
|
|
132
|
+
# If the driver dynamic pages (JS) and the element is currently non-interactable, this method will
|
|
133
|
+
# continuously retry the action until either the element becomes interactable or the maximum
|
|
134
|
+
# wait time expires.
|
|
135
|
+
#
|
|
136
|
+
# @param [false, Numeric] wait
|
|
137
|
+
# Maximum time to wait for the action to succeed. Defaults to {Capybara.configure default_max_wait_time}.
|
|
138
|
+
# @return [Capybara::Node::Element] The element
|
|
139
|
+
def select_option(wait: nil)
|
|
140
|
+
synchronize(wait) { base.select_option }
|
|
141
|
+
self
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
##
|
|
145
|
+
#
|
|
146
|
+
# Unselect this node if it is an option element inside a multiple select tag.
|
|
147
|
+
#
|
|
148
|
+
# @macro action_waiting_behavior
|
|
149
|
+
# @return [Capybara::Node::Element] The element
|
|
150
|
+
def unselect_option(wait: nil)
|
|
151
|
+
synchronize(wait) { base.unselect_option }
|
|
152
|
+
self
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
#
|
|
157
|
+
# Click the Element.
|
|
158
|
+
#
|
|
159
|
+
# @macro action_waiting_behavior
|
|
160
|
+
# @!macro click_modifiers
|
|
161
|
+
# Both x: and y: must be specified if an offset is wanted, if not specified the click will occur at the middle of the element.
|
|
162
|
+
# @overload $0(*modifier_keys, wait: nil, **offset)
|
|
163
|
+
# @param *modifier_keys [:alt, :control, :meta, :shift] ([]) Keys to be held down when clicking
|
|
164
|
+
# @option options [Integer] x X coordinate to offset the click location. If {Capybara.configure w3c_click_offset} is `true` the
|
|
165
|
+
# offset will be from the element center, otherwise it will be from the top left corner of the element
|
|
166
|
+
# @option options [Integer] y Y coordinate to offset the click location. If {Capybara.configure w3c_click_offset} is `true` the
|
|
167
|
+
# offset will be from the element center, otherwise it will be from the top left corner of the element
|
|
168
|
+
# @option options [Float] delay Delay between the mouse down and mouse up events in seconds (0)
|
|
169
|
+
# @return [Capybara::Node::Element] The element
|
|
170
|
+
def click(*keys, **options)
|
|
171
|
+
perform_click_action(keys, **options) do |k, opts|
|
|
172
|
+
base.click(k, **opts)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
#
|
|
178
|
+
# Right Click the Element.
|
|
179
|
+
#
|
|
180
|
+
# @macro action_waiting_behavior
|
|
181
|
+
# @macro click_modifiers
|
|
182
|
+
# @option options [Float] delay Delay between the mouse down and mouse up events in seconds (0)
|
|
183
|
+
# @return [Capybara::Node::Element] The element
|
|
184
|
+
def right_click(*keys, **options)
|
|
185
|
+
perform_click_action(keys, **options) do |k, opts|
|
|
186
|
+
base.right_click(k, **opts)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
#
|
|
192
|
+
# Double Click the Element.
|
|
193
|
+
#
|
|
194
|
+
# @macro action_waiting_behavior
|
|
195
|
+
# @macro click_modifiers
|
|
196
|
+
# @return [Capybara::Node::Element] The element
|
|
197
|
+
def double_click(*keys, **options)
|
|
198
|
+
perform_click_action(keys, **options) do |k, opts|
|
|
199
|
+
base.double_click(k, **opts)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
##
|
|
204
|
+
#
|
|
205
|
+
# Send Keystrokes to the Element.
|
|
206
|
+
#
|
|
207
|
+
# @overload send_keys(keys, ...)
|
|
208
|
+
# @param keys [String, Symbol, Array<String,Symbol>]
|
|
209
|
+
#
|
|
210
|
+
# Examples:
|
|
211
|
+
#
|
|
212
|
+
# element.send_keys "foo" #=> value: 'foo'
|
|
213
|
+
# element.send_keys "tet", :left, "s" #=> value: 'test'
|
|
214
|
+
# element.send_keys [:control, 'a'], :space #=> value: ' ' - assuming ctrl-a selects all contents
|
|
215
|
+
#
|
|
216
|
+
# Symbols supported for keys:
|
|
217
|
+
# * :cancel
|
|
218
|
+
# * :help
|
|
219
|
+
# * :backspace
|
|
220
|
+
# * :tab
|
|
221
|
+
# * :clear
|
|
222
|
+
# * :return
|
|
223
|
+
# * :enter
|
|
224
|
+
# * :shift
|
|
225
|
+
# * :control
|
|
226
|
+
# * :alt
|
|
227
|
+
# * :pause
|
|
228
|
+
# * :escape
|
|
229
|
+
# * :space
|
|
230
|
+
# * :page_up
|
|
231
|
+
# * :page_down
|
|
232
|
+
# * :end
|
|
233
|
+
# * :home
|
|
234
|
+
# * :left
|
|
235
|
+
# * :up
|
|
236
|
+
# * :right
|
|
237
|
+
# * :down
|
|
238
|
+
# * :insert
|
|
239
|
+
# * :delete
|
|
240
|
+
# * :semicolon
|
|
241
|
+
# * :equals
|
|
242
|
+
# * :numpad0
|
|
243
|
+
# * :numpad1
|
|
244
|
+
# * :numpad2
|
|
245
|
+
# * :numpad3
|
|
246
|
+
# * :numpad4
|
|
247
|
+
# * :numpad5
|
|
248
|
+
# * :numpad6
|
|
249
|
+
# * :numpad7
|
|
250
|
+
# * :numpad8
|
|
251
|
+
# * :numpad9
|
|
252
|
+
# * :multiply - numeric keypad *
|
|
253
|
+
# * :add - numeric keypad +
|
|
254
|
+
# * :separator - numeric keypad 'separator' key ??
|
|
255
|
+
# * :subtract - numeric keypad -
|
|
256
|
+
# * :decimal - numeric keypad .
|
|
257
|
+
# * :divide - numeric keypad /
|
|
258
|
+
# * :f1
|
|
259
|
+
# * :f2
|
|
260
|
+
# * :f3
|
|
261
|
+
# * :f4
|
|
262
|
+
# * :f5
|
|
263
|
+
# * :f6
|
|
264
|
+
# * :f7
|
|
265
|
+
# * :f8
|
|
266
|
+
# * :f9
|
|
267
|
+
# * :f10
|
|
268
|
+
# * :f11
|
|
269
|
+
# * :f12
|
|
270
|
+
# * :meta
|
|
271
|
+
# * :command - alias of :meta
|
|
272
|
+
#
|
|
273
|
+
# @return [Capybara::Node::Element] The element
|
|
274
|
+
def send_keys(*args)
|
|
275
|
+
synchronize { base.send_keys(*args) }
|
|
276
|
+
self
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
#
|
|
281
|
+
# Hover on the Element.
|
|
282
|
+
#
|
|
283
|
+
# @return [Capybara::Node::Element] The element
|
|
284
|
+
def hover
|
|
285
|
+
synchronize { base.hover }
|
|
286
|
+
self
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
##
|
|
290
|
+
#
|
|
291
|
+
# @return [String] The tag name of the element
|
|
292
|
+
#
|
|
293
|
+
def tag_name
|
|
294
|
+
# Element type is immutable so cache it
|
|
295
|
+
@tag_name ||= initial_cache[:tag_name] || synchronize { base.tag_name }
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
##
|
|
299
|
+
#
|
|
300
|
+
# Whether or not the element is visible. Not all drivers support CSS, so
|
|
301
|
+
# the result may be inaccurate.
|
|
302
|
+
#
|
|
303
|
+
# @return [Boolean] Whether the element is visible
|
|
304
|
+
#
|
|
305
|
+
def visible?
|
|
306
|
+
synchronize { base.visible? }
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
#
|
|
311
|
+
# Whether or not the element is currently in the viewport and it (or descendants)
|
|
312
|
+
# would be considered clickable at the elements center point.
|
|
313
|
+
#
|
|
314
|
+
# @return [Boolean] Whether the elements center is obscured.
|
|
315
|
+
#
|
|
316
|
+
def obscured?
|
|
317
|
+
synchronize { base.obscured? }
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
##
|
|
321
|
+
#
|
|
322
|
+
# Whether or not the element is checked.
|
|
323
|
+
#
|
|
324
|
+
# @return [Boolean] Whether the element is checked
|
|
325
|
+
#
|
|
326
|
+
def checked?
|
|
327
|
+
synchronize { base.checked? }
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
#
|
|
332
|
+
# Whether or not the element is selected.
|
|
333
|
+
#
|
|
334
|
+
# @return [Boolean] Whether the element is selected
|
|
335
|
+
#
|
|
336
|
+
def selected?
|
|
337
|
+
synchronize { base.selected? }
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
##
|
|
341
|
+
#
|
|
342
|
+
# Whether or not the element is disabled.
|
|
343
|
+
#
|
|
344
|
+
# @return [Boolean] Whether the element is disabled
|
|
345
|
+
#
|
|
346
|
+
def disabled?
|
|
347
|
+
synchronize { base.disabled? }
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
##
|
|
351
|
+
#
|
|
352
|
+
# Whether or not the element is readonly.
|
|
353
|
+
#
|
|
354
|
+
# @return [Boolean] Whether the element is readonly
|
|
355
|
+
#
|
|
356
|
+
def readonly?
|
|
357
|
+
synchronize { base.readonly? }
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
##
|
|
361
|
+
#
|
|
362
|
+
# Whether or not the element supports multiple results.
|
|
363
|
+
#
|
|
364
|
+
# @return [Boolean] Whether the element supports multiple results.
|
|
365
|
+
#
|
|
366
|
+
def multiple?
|
|
367
|
+
synchronize { base.multiple? }
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
##
|
|
371
|
+
#
|
|
372
|
+
# An XPath expression describing where on the page the element can be found.
|
|
373
|
+
#
|
|
374
|
+
# @return [String] An XPath expression
|
|
375
|
+
#
|
|
376
|
+
def path
|
|
377
|
+
synchronize { base.path }
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def rect
|
|
381
|
+
synchronize { base.rect }
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
##
|
|
385
|
+
#
|
|
386
|
+
# Trigger any event on the current element, for example mouseover or focus
|
|
387
|
+
# events. Not supported with the Selenium driver, and SHOULDN'T BE USED IN TESTING unless you
|
|
388
|
+
# fully understand why you're using it, that it can allow actions a user could never
|
|
389
|
+
# perform, and that it may completely invalidate your test.
|
|
390
|
+
#
|
|
391
|
+
# @param [String] event The name of the event to trigger
|
|
392
|
+
#
|
|
393
|
+
# @return [Capybara::Node::Element] The element
|
|
394
|
+
def trigger(event)
|
|
395
|
+
synchronize { base.trigger(event) }
|
|
396
|
+
self
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
##
|
|
400
|
+
#
|
|
401
|
+
# Drag the element to the given other element.
|
|
402
|
+
#
|
|
403
|
+
# source = page.find('#foo')
|
|
404
|
+
# target = page.find('#bar')
|
|
405
|
+
# source.drag_to(target)
|
|
406
|
+
#
|
|
407
|
+
# @param [Capybara::Node::Element] node The element to drag to
|
|
408
|
+
# @param [Hash] options Driver specific options for dragging. May not be supported by all drivers.
|
|
409
|
+
# @option options [Numeric] :delay (0.05) When using Chrome/Firefox with Selenium and HTML5 dragging this is the number
|
|
410
|
+
# of seconds between each stage of the drag.
|
|
411
|
+
# @option options [Boolean] :html5 When using Chrome/Firefox with Selenium enables to force the use of HTML5
|
|
412
|
+
# (true) or legacy (false) dragging. If not specified the driver will attempt to
|
|
413
|
+
# detect the correct method to use.
|
|
414
|
+
# @option options [Array<Symbol>,Symbol] :drop_modifiers Modifier keys which should be held while the dragged element is dropped.
|
|
415
|
+
#
|
|
416
|
+
#
|
|
417
|
+
# @return [Capybara::Node::Element] The dragged element
|
|
418
|
+
def drag_to(node, **options)
|
|
419
|
+
synchronize { base.drag_to(node.base, **options) }
|
|
420
|
+
self
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
##
|
|
424
|
+
#
|
|
425
|
+
# Drop items on the current element.
|
|
426
|
+
#
|
|
427
|
+
# target = page.find('#foo')
|
|
428
|
+
# target.drop('/some/path/file.csv')
|
|
429
|
+
#
|
|
430
|
+
# @overload drop(path, ...)
|
|
431
|
+
# @param [String, #to_path] path Location of the file to drop on the element
|
|
432
|
+
#
|
|
433
|
+
# @overload drop(strings, ...)
|
|
434
|
+
# @param [Hash] strings A hash of type to data to be dropped - `{ "text/url" => "https://www.google.com" }`
|
|
435
|
+
#
|
|
436
|
+
# @return [Capybara::Node::Element] The element
|
|
437
|
+
def drop(*args)
|
|
438
|
+
options = args.map do |arg|
|
|
439
|
+
return arg.to_path if arg.respond_to?(:to_path)
|
|
440
|
+
|
|
441
|
+
arg
|
|
442
|
+
end
|
|
443
|
+
synchronize { base.drop(*options) }
|
|
444
|
+
self
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
##
|
|
448
|
+
#
|
|
449
|
+
# Scroll the page or element.
|
|
450
|
+
#
|
|
451
|
+
# @overload scroll_to(position, offset: [0,0])
|
|
452
|
+
# Scroll the page or element to its top, bottom or middle.
|
|
453
|
+
# @param [:top, :bottom, :center, :current] position
|
|
454
|
+
# @param [[Integer, Integer]] offset
|
|
455
|
+
#
|
|
456
|
+
# @overload scroll_to(element, align: :top)
|
|
457
|
+
# Scroll the page or current element until the given element is aligned at the top, bottom, or center of it.
|
|
458
|
+
# @param [Capybara::Node::Element] element The element to be scrolled into view
|
|
459
|
+
# @param [:top, :bottom, :center] align Where to align the element being scrolled into view with relation to the current page/element if possible
|
|
460
|
+
#
|
|
461
|
+
# @overload scroll_to(x,y)
|
|
462
|
+
# @param [Integer] x Horizontal scroll offset
|
|
463
|
+
# @param [Integer] y Vertical scroll offset
|
|
464
|
+
#
|
|
465
|
+
# @return [Capybara::Node::Element] The element
|
|
466
|
+
def scroll_to(pos_or_el_or_x, y = nil, align: :top, offset: nil)
|
|
467
|
+
case pos_or_el_or_x
|
|
468
|
+
when Symbol
|
|
469
|
+
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
|
470
|
+
when Capybara::Node::Element
|
|
471
|
+
synchronize { base.scroll_to(pos_or_el_or_x.base, align) }
|
|
472
|
+
else
|
|
473
|
+
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
|
474
|
+
end
|
|
475
|
+
synchronize { base.scroll_by(*offset) } unless offset.nil?
|
|
476
|
+
self
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
##
|
|
480
|
+
#
|
|
481
|
+
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
|
482
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
|
483
|
+
# {#evaluate_script} whenever a result is not expected or needed. `this` in the script will refer to the element this is called on.
|
|
484
|
+
#
|
|
485
|
+
# @param [String] script A string of JavaScript to execute
|
|
486
|
+
# @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
|
|
487
|
+
#
|
|
488
|
+
def execute_script(script, *args)
|
|
489
|
+
session.execute_script(<<~JS, self, *args)
|
|
490
|
+
(function (){
|
|
491
|
+
#{script}
|
|
492
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
493
|
+
JS
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
##
|
|
497
|
+
#
|
|
498
|
+
# Evaluate the given JS in the context of the element and return the result. Be careful when using this with
|
|
499
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
|
500
|
+
# be a better alternative. `this` in the script will refer to the element this is called on.
|
|
501
|
+
#
|
|
502
|
+
# @param [String] script A string of JavaScript to evaluate
|
|
503
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
504
|
+
#
|
|
505
|
+
def evaluate_script(script, *args)
|
|
506
|
+
session.evaluate_script(<<~JS, self, *args)
|
|
507
|
+
(function(){
|
|
508
|
+
return #{script.strip}
|
|
509
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
510
|
+
JS
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
##
|
|
514
|
+
#
|
|
515
|
+
# Evaluate the given JavaScript in the context of the element and obtain the result from a
|
|
516
|
+
# callback function which will be passed as the last argument to the script. `this` in the
|
|
517
|
+
# script will refer to the element this is called on.
|
|
518
|
+
#
|
|
519
|
+
# @param [String] script A string of JavaScript to evaluate
|
|
520
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
521
|
+
#
|
|
522
|
+
def evaluate_async_script(script, *args)
|
|
523
|
+
session.evaluate_async_script(<<~JS, self, *args)
|
|
524
|
+
(function (){
|
|
525
|
+
#{script}
|
|
526
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
527
|
+
JS
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
##
|
|
531
|
+
#
|
|
532
|
+
# Toggle the elements background color between white and black for a period of time.
|
|
533
|
+
#
|
|
534
|
+
# @return [Capybara::Node::Element] The element
|
|
535
|
+
def flash
|
|
536
|
+
execute_script(<<~JS, 100)
|
|
537
|
+
async function flash(el, delay){
|
|
538
|
+
var old_bg = el.style.backgroundColor;
|
|
539
|
+
var colors = ["black", "white"];
|
|
540
|
+
for(var i=0; i<20; i++){
|
|
541
|
+
el.style.backgroundColor = colors[i % colors.length];
|
|
542
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
543
|
+
}
|
|
544
|
+
el.style.backgroundColor = old_bg;
|
|
545
|
+
}
|
|
546
|
+
flash(this, arguments[0]);
|
|
547
|
+
JS
|
|
548
|
+
|
|
549
|
+
self
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
# @api private
|
|
553
|
+
def reload
|
|
554
|
+
return self unless @allow_reload
|
|
555
|
+
|
|
556
|
+
begin
|
|
557
|
+
reloaded = @query.resolve_for(query_scope.reload)[@query_idx.to_i]
|
|
558
|
+
@base = reloaded.base if reloaded
|
|
559
|
+
rescue StandardError => e
|
|
560
|
+
raise e unless catch_error?(e)
|
|
561
|
+
end
|
|
562
|
+
self
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
##
|
|
566
|
+
#
|
|
567
|
+
# A human-readable representation of the element.
|
|
568
|
+
#
|
|
569
|
+
# @return [String] A string representation
|
|
570
|
+
def inspect
|
|
571
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
|
|
572
|
+
rescue NotSupportedByDriverError
|
|
573
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}">)
|
|
574
|
+
rescue *session.driver.invalid_element_errors
|
|
575
|
+
%(Obsolete #<Capybara::Node::Element>)
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
# @api private
|
|
579
|
+
def initial_cache
|
|
580
|
+
base.respond_to?(:initial_cache) ? base.initial_cache : {}
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
STYLE_SCRIPT = <<~JS
|
|
584
|
+
(function(){
|
|
585
|
+
var s = window.getComputedStyle(this);
|
|
586
|
+
var result = {};
|
|
587
|
+
for (var i = arguments.length; i--; ) {
|
|
588
|
+
var property_name = arguments[i];
|
|
589
|
+
result[property_name] = s.getPropertyValue(property_name);
|
|
590
|
+
}
|
|
591
|
+
return result;
|
|
592
|
+
}).apply(this, arguments)
|
|
593
|
+
JS
|
|
594
|
+
|
|
595
|
+
private
|
|
596
|
+
|
|
597
|
+
def perform_click_action(keys, wait: nil, **options)
|
|
598
|
+
raise ArgumentError, 'You must specify both x: and y: for a click offset' if nil ^ options[:x] ^ options[:y]
|
|
599
|
+
|
|
600
|
+
options[:offset] ||= :center if session_options.w3c_click_offset
|
|
601
|
+
synchronize(wait) { yield keys, options }
|
|
602
|
+
self
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
end
|