capybara 2.7.0 → 3.35.3
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 +5 -5
- data/.yardopts +1 -0
- data/History.md +1147 -11
- data/License.txt +1 -1
- data/README.md +252 -131
- data/lib/capybara/config.rb +92 -0
- data/lib/capybara/cucumber.rb +3 -3
- data/lib/capybara/driver/base.rb +52 -21
- data/lib/capybara/driver/node.rb +48 -14
- data/lib/capybara/dsl.rb +16 -9
- data/lib/capybara/helpers.rb +72 -81
- data/lib/capybara/minitest/spec.rb +267 -0
- data/lib/capybara/minitest.rb +385 -0
- data/lib/capybara/node/actions.rb +337 -89
- data/lib/capybara/node/base.rb +50 -32
- data/lib/capybara/node/document.rb +19 -3
- data/lib/capybara/node/document_matchers.rb +22 -24
- data/lib/capybara/node/element.rb +388 -125
- data/lib/capybara/node/finders.rb +231 -121
- data/lib/capybara/node/matchers.rb +503 -217
- data/lib/capybara/node/simple.rb +64 -27
- data/lib/capybara/queries/ancestor_query.rb +27 -0
- data/lib/capybara/queries/base_query.rb +87 -11
- data/lib/capybara/queries/current_path_query.rb +24 -24
- data/lib/capybara/queries/match_query.rb +15 -10
- data/lib/capybara/queries/selector_query.rb +675 -81
- 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 +88 -20
- data/lib/capybara/queries/title_query.rb +9 -11
- data/lib/capybara/rack_test/browser.rb +63 -39
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +26 -16
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +73 -58
- data/lib/capybara/rack_test/node.rb +187 -67
- data/lib/capybara/rails.rb +4 -8
- 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 +142 -14
- data/lib/capybara/rspec/features.rb +17 -42
- data/lib/capybara/rspec/matcher_proxies.rb +82 -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 +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +143 -244
- data/lib/capybara/rspec.rb +10 -12
- 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 +102 -0
- data/lib/capybara/selector/definition/button.rb +63 -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 +278 -0
- data/lib/capybara/selector/filter.rb +3 -46
- 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 +155 -0
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +232 -369
- 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 +380 -142
- 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 +528 -97
- 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 +63 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +71 -0
- data/lib/capybara/server.rb +74 -71
- data/lib/capybara/session/config.rb +126 -0
- data/lib/capybara/session/matchers.rb +44 -27
- data/lib/capybara/session.rb +500 -297
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- 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 +168 -14
- data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
- data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
- data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
- data/lib/capybara/spec/session/all_spec.rb +179 -59
- 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 +154 -48
- data/lib/capybara/spec/session/body_spec.rb +12 -13
- data/lib/capybara/spec/session/check_spec.rb +168 -41
- data/lib/capybara/spec/session/choose_spec.rb +75 -23
- data/lib/capybara/spec/session/click_button_spec.rb +243 -175
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
- data/lib/capybara/spec/session/click_link_spec.rb +100 -53
- data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
- data/lib/capybara/spec/session/current_url_spec.rb +61 -35
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
- data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
- data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
- data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
- data/lib/capybara/spec/session/find_button_spec.rb +37 -18
- data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
- data/lib/capybara/spec/session/find_field_spec.rb +57 -34
- data/lib/capybara/spec/session/find_link_spec.rb +47 -10
- data/lib/capybara/spec/session/find_spec.rb +290 -144
- data/lib/capybara/spec/session/first_spec.rb +91 -48
- 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 +3 -2
- data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
- 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 +76 -19
- data/lib/capybara/spec/session/has_css_spec.rb +277 -131
- data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
- data/lib/capybara/spec/session/has_field_spec.rb +177 -107
- data/lib/capybara/spec/session/has_link_spec.rb +13 -12
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
- data/lib/capybara/spec/session/has_select_spec.rb +191 -95
- data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +172 -5
- data/lib/capybara/spec/session/has_text_spec.rb +126 -60
- data/lib/capybara/spec/session/has_title_spec.rb +35 -12
- data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
- data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
- data/lib/capybara/spec/session/html_spec.rb +14 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/node_spec.rb +1028 -131
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +34 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
- data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
- data/lib/capybara/spec/session/save_page_spec.rb +42 -55
- data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
- 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 +112 -85
- data/lib/capybara/spec/session/selectors_spec.rb +71 -8
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/text_spec.rb +38 -23
- data/lib/capybara/spec/session/title_spec.rb +17 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
- data/lib/capybara/spec/session/unselect_spec.rb +44 -43
- data/lib/capybara/spec/session/visit_spec.rb +99 -32
- data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
- data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
- data/lib/capybara/spec/session/window/window_spec.rb +121 -73
- data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
- data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
- data/lib/capybara/spec/session/within_spec.rb +76 -43
- data/lib/capybara/spec/spec_helper.rb +67 -33
- data/lib/capybara/spec/test_app.rb +85 -36
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/buttons.erb +1 -1
- data/lib/capybara/spec/views/fieldsets.erb +1 -1
- data/lib/capybara/spec/views/form.erb +227 -20
- data/lib/capybara/spec/views/frame_child.erb +10 -2
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +2 -2
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/header_links.erb +1 -1
- data/lib/capybara/spec/views/host_links.erb +1 -1
- 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 +1 -1
- 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/postback.erb +1 -1
- 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 +69 -2
- data/lib/capybara/spec/views/with_animation.erb +82 -0
- data/lib/capybara/spec/views/with_base_tag.erb +1 -1
- data/lib/capybara/spec/views/with_count.erb +1 -1
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +7 -1
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +100 -10
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_html_entities.erb +1 -1
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +49 -3
- 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 +1 -1
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_simple_html.erb +1 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_title.erb +1 -1
- data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
- data/lib/capybara/spec/views/with_windows.erb +7 -1
- data/lib/capybara/spec/views/within_frames.erb +6 -3
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +39 -21
- data/lib/capybara.rb +208 -186
- data/spec/basic_node_spec.rb +52 -39
- data/spec/capybara_spec.rb +72 -50
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +81 -61
- 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 +7 -3
- data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
- data/spec/minitest_spec.rb +164 -0
- data/spec/minitest_spec_spec.rb +162 -0
- data/spec/per_session_config_spec.rb +68 -0
- data/spec/rack_test_spec.rb +189 -96
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +143 -13
- data/spec/rspec/features_spec.rb +38 -32
- data/spec/rspec/scenarios_spec.rb +9 -7
- data/spec/rspec/shared_spec_matchers.rb +959 -0
- data/spec/rspec/views_spec.rb +9 -3
- data/spec/rspec_matchers_spec.rb +62 -0
- data/spec/rspec_spec.rb +127 -30
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +458 -37
- data/spec/selenium_spec_chrome.rb +196 -9
- data/spec/selenium_spec_chrome_remote.rb +100 -0
- data/spec/selenium_spec_edge.rb +47 -0
- data/spec/selenium_spec_firefox.rb +210 -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 +200 -101
- data/spec/session_spec.rb +91 -0
- data/spec/shared_selenium_node.rb +83 -0
- data/spec/shared_selenium_session.rb +558 -0
- data/spec/spec_helper.rb +94 -2
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +420 -60
- data/lib/capybara/query.rb +0 -7
- data/lib/capybara/spec/session/assert_current_path.rb +0 -60
- data/lib/capybara/spec/session/assert_selector.rb +0 -148
- data/lib/capybara/spec/session/assert_text.rb +0 -196
- data/lib/capybara/spec/session/assert_title.rb +0 -70
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
- data/spec/rspec/matchers_spec.rb +0 -827
- data/spec/selenium_spec.rb +0 -151
data/lib/capybara/node/base.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module Capybara
|
|
3
4
|
module Node
|
|
4
|
-
|
|
5
5
|
##
|
|
6
6
|
#
|
|
7
7
|
# A {Capybara::Node::Base} represents either an element on a page through the subclass
|
|
@@ -17,13 +17,13 @@ module Capybara
|
|
|
17
17
|
#
|
|
18
18
|
# session = Capybara::Session.new(:rack_test, my_app)
|
|
19
19
|
# session.visit('/')
|
|
20
|
-
# session.fill_in('Foo', :
|
|
20
|
+
# session.fill_in('Foo', with: 'Bar') # from Capybara::Node::Actions
|
|
21
21
|
# bar = session.find('#bar') # from Capybara::Node::Finders
|
|
22
|
-
# bar.select('Baz', :
|
|
22
|
+
# bar.select('Baz', from: 'Quox') # from Capybara::Node::Actions
|
|
23
23
|
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
|
24
24
|
#
|
|
25
25
|
class Base
|
|
26
|
-
attr_reader :session, :base, :
|
|
26
|
+
attr_reader :session, :base, :query_scope
|
|
27
27
|
|
|
28
28
|
include Capybara::Node::Finders
|
|
29
29
|
include Capybara::Node::Actions
|
|
@@ -67,54 +67,72 @@ module Capybara
|
|
|
67
67
|
# time has passed. On rubies/platforms which don't support access to a monotonic process clock
|
|
68
68
|
# if the return value of `Time.now` is stubbed out, Capybara will raise `Capybara::FrozenInTime`.
|
|
69
69
|
#
|
|
70
|
-
# @param [Integer] seconds
|
|
71
|
-
# @param
|
|
72
|
-
# @option options [Array<Exception>] :errors (driver.invalid_element_errors +
|
|
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 +
|
|
73
72
|
# [Capybara::ElementNotFound]) exception types that cause the block to be rerun
|
|
74
73
|
# @return [Object] The result of the given block
|
|
75
74
|
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
|
|
76
75
|
#
|
|
77
|
-
def synchronize(seconds=
|
|
78
|
-
|
|
76
|
+
def synchronize(seconds = nil, errors: nil)
|
|
77
|
+
return yield if session.synchronized
|
|
79
78
|
|
|
80
|
-
if
|
|
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
|
|
81
83
|
yield
|
|
82
|
-
|
|
83
|
-
session.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
reload if
|
|
94
|
-
|
|
95
|
-
ensure
|
|
96
|
-
session.synchronized = false
|
|
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
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)
|
|
108
|
+
else
|
|
109
|
+
base.find_css(css, **options)
|
|
98
110
|
end
|
|
99
111
|
end
|
|
100
112
|
|
|
101
113
|
# @api private
|
|
102
|
-
def
|
|
103
|
-
base.
|
|
114
|
+
def find_xpath(xpath, **options)
|
|
115
|
+
if base.method(:find_xpath).arity == 1
|
|
116
|
+
base.find_xpath(xpath)
|
|
117
|
+
else
|
|
118
|
+
base.find_xpath(xpath, **options)
|
|
119
|
+
end
|
|
104
120
|
end
|
|
105
121
|
|
|
106
122
|
# @api private
|
|
107
|
-
def
|
|
108
|
-
|
|
123
|
+
def session_options
|
|
124
|
+
session.config
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def to_capybara_node
|
|
128
|
+
self
|
|
109
129
|
end
|
|
110
130
|
|
|
111
131
|
protected
|
|
112
132
|
|
|
113
133
|
def catch_error?(error, errors = nil)
|
|
114
134
|
errors ||= (driver.invalid_element_errors + [Capybara::ElementNotFound])
|
|
115
|
-
errors.any?
|
|
116
|
-
error.is_a?(type)
|
|
117
|
-
end
|
|
135
|
+
errors.any? { |type| error.is_a?(type) }
|
|
118
136
|
end
|
|
119
137
|
|
|
120
138
|
def driver
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module Capybara
|
|
3
4
|
module Node
|
|
4
|
-
|
|
5
5
|
##
|
|
6
6
|
#
|
|
7
7
|
# A {Capybara::Document} represents an HTML document. Any operation
|
|
@@ -20,13 +20,29 @@ module Capybara
|
|
|
20
20
|
#
|
|
21
21
|
# @return [String] The text of the document
|
|
22
22
|
#
|
|
23
|
-
def text(type=nil)
|
|
24
|
-
find(:xpath, '/html').text(type)
|
|
23
|
+
def text(type = nil, normalize_ws: false)
|
|
24
|
+
find(:xpath, '/html').text(type, normalize_ws: normalize_ws)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
##
|
|
28
|
+
#
|
|
29
|
+
# @return [String] The title of the document
|
|
30
|
+
#
|
|
27
31
|
def title
|
|
28
32
|
session.driver.title
|
|
29
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
|
|
30
46
|
end
|
|
31
47
|
end
|
|
32
48
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module Capybara
|
|
3
4
|
module Node
|
|
4
5
|
module DocumentMatchers
|
|
@@ -6,22 +7,19 @@ module Capybara
|
|
|
6
7
|
# Asserts that the page has the given title.
|
|
7
8
|
#
|
|
8
9
|
# @!macro title_query_params
|
|
9
|
-
# @overload $0(string, options
|
|
10
|
+
# @overload $0(string, **options)
|
|
10
11
|
# @param string [String] The string that title should include
|
|
11
|
-
# @overload $0(regexp, options
|
|
12
|
+
# @overload $0(regexp, **options)
|
|
12
13
|
# @param regexp [Regexp] The regexp that title should match to
|
|
13
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
|
|
14
16
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
|
15
17
|
# @return [true]
|
|
16
18
|
#
|
|
17
|
-
def assert_title(title, options
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
unless query.resolves_for?(self)
|
|
21
|
-
raise Capybara::ExpectationNotMet, query.failure_message
|
|
22
|
-
end
|
|
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)
|
|
23
22
|
end
|
|
24
|
-
return true
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
##
|
|
@@ -31,14 +29,10 @@ module Capybara
|
|
|
31
29
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
|
32
30
|
# @return [true]
|
|
33
31
|
#
|
|
34
|
-
def assert_no_title(title, options
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if query.resolves_for?(self)
|
|
38
|
-
raise Capybara::ExpectationNotMet, query.negative_failure_message
|
|
39
|
-
end
|
|
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)
|
|
40
35
|
end
|
|
41
|
-
return true
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
##
|
|
@@ -47,10 +41,8 @@ module Capybara
|
|
|
47
41
|
# @macro title_query_params
|
|
48
42
|
# @return [Boolean]
|
|
49
43
|
#
|
|
50
|
-
def has_title?(title, options
|
|
51
|
-
assert_title(title, options)
|
|
52
|
-
rescue Capybara::ExpectationNotMet
|
|
53
|
-
return false
|
|
44
|
+
def has_title?(title, **options)
|
|
45
|
+
make_predicate(options) { assert_title(title, **options) }
|
|
54
46
|
end
|
|
55
47
|
|
|
56
48
|
##
|
|
@@ -59,10 +51,16 @@ module Capybara
|
|
|
59
51
|
# @macro title_query_params
|
|
60
52
|
# @return [Boolean]
|
|
61
53
|
#
|
|
62
|
-
def has_no_title?(title, options
|
|
63
|
-
assert_no_title(title, options)
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
66
64
|
end
|
|
67
65
|
end
|
|
68
66
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module Capybara
|
|
3
4
|
module Node
|
|
4
|
-
|
|
5
5
|
##
|
|
6
6
|
#
|
|
7
7
|
# A {Capybara::Node::Element} represents a single element on the page. It is possible
|
|
@@ -10,7 +10,7 @@ module Capybara
|
|
|
10
10
|
# session = Capybara::Session.new(:rack_test, my_app)
|
|
11
11
|
#
|
|
12
12
|
# bar = session.find('#bar') # from Capybara::Node::Finders
|
|
13
|
-
# bar.select('Baz', :
|
|
13
|
+
# bar.select('Baz', from: 'Quox') # from Capybara::Node::Actions
|
|
14
14
|
#
|
|
15
15
|
# {Capybara::Node::Element} also has access to HTML attributes and other properties of the
|
|
16
16
|
# element:
|
|
@@ -22,14 +22,16 @@ module Capybara
|
|
|
22
22
|
# @see Capybara::Node
|
|
23
23
|
#
|
|
24
24
|
class Element < Base
|
|
25
|
-
|
|
26
|
-
def initialize(session, base, parent, query)
|
|
25
|
+
def initialize(session, base, query_scope, query)
|
|
27
26
|
super(session, base)
|
|
28
|
-
@
|
|
27
|
+
@query_scope = query_scope
|
|
29
28
|
@query = query
|
|
29
|
+
@allow_reload = false
|
|
30
|
+
@query_idx = nil
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
def allow_reload!
|
|
33
|
+
def allow_reload!(idx = nil)
|
|
34
|
+
@query_idx = idx
|
|
33
35
|
@allow_reload = true
|
|
34
36
|
end
|
|
35
37
|
|
|
@@ -43,30 +45,25 @@ module Capybara
|
|
|
43
45
|
|
|
44
46
|
##
|
|
45
47
|
#
|
|
46
|
-
# Retrieve the text of the element. If
|
|
48
|
+
# Retrieve the text of the element. If {Capybara.configure ignore_hidden_elements}
|
|
47
49
|
# is `true`, which it is by default, then this will return only text
|
|
48
50
|
# which is visible. The exact semantics of this may differ between
|
|
49
51
|
# drivers, but generally any text within elements with `display:none` is
|
|
50
52
|
# ignored. This behaviour can be overridden by passing `:all` to this
|
|
51
53
|
# method.
|
|
52
54
|
#
|
|
53
|
-
# @param [:all, :visible]
|
|
55
|
+
# @param type [:all, :visible] Whether to return only visible or all text
|
|
54
56
|
# @return [String] The text of the element
|
|
55
57
|
#
|
|
56
|
-
def text(type=nil)
|
|
57
|
-
type ||= :all unless
|
|
58
|
-
synchronize
|
|
59
|
-
|
|
60
|
-
base.all_text
|
|
61
|
-
else
|
|
62
|
-
base.visible_text
|
|
63
|
-
end
|
|
64
|
-
end
|
|
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
|
|
65
62
|
end
|
|
66
63
|
|
|
67
64
|
##
|
|
68
65
|
#
|
|
69
|
-
# Retrieve the given attribute
|
|
66
|
+
# Retrieve the given attribute.
|
|
70
67
|
#
|
|
71
68
|
# element[:title] # => HTML title attribute
|
|
72
69
|
#
|
|
@@ -77,6 +74,30 @@ module Capybara
|
|
|
77
74
|
synchronize { base[attribute] }
|
|
78
75
|
end
|
|
79
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
|
+
|
|
80
101
|
##
|
|
81
102
|
#
|
|
82
103
|
# @return [String] The value of the form element
|
|
@@ -90,147 +111,179 @@ module Capybara
|
|
|
90
111
|
# Set the value of the form element to the given value.
|
|
91
112
|
#
|
|
92
113
|
# @param [String] value The new value
|
|
93
|
-
# @param [Hash
|
|
114
|
+
# @param [Hash] options Driver specific options for how to set the value. Take default values from {Capybara.configure default_set_options}.
|
|
94
115
|
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
unless options.empty? || driver_supports_options
|
|
101
|
-
warn "Options passed to Capybara::Node#set but the driver doesn't support them"
|
|
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}"
|
|
102
120
|
end
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
else
|
|
108
|
-
base.set(value)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
122
|
+
options = session_options.default_set_options.to_h.merge(options)
|
|
123
|
+
synchronize { base.set(value, **options) }
|
|
124
|
+
self
|
|
111
125
|
end
|
|
112
126
|
|
|
113
127
|
##
|
|
114
128
|
#
|
|
115
|
-
# Select this node if is an option element inside a select tag
|
|
129
|
+
# Select this node if it is an option element inside a select tag.
|
|
116
130
|
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
120
142
|
end
|
|
121
143
|
|
|
122
144
|
##
|
|
123
145
|
#
|
|
124
|
-
# Unselect this node if is an option element inside a multiple select tag
|
|
146
|
+
# Unselect this node if it is an option element inside a multiple select tag.
|
|
125
147
|
#
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
128
153
|
end
|
|
129
154
|
|
|
130
155
|
##
|
|
131
156
|
#
|
|
132
|
-
# Click the Element
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
136
174
|
end
|
|
137
175
|
|
|
138
176
|
##
|
|
139
177
|
#
|
|
140
|
-
# Right Click the Element
|
|
178
|
+
# Right Click the Element.
|
|
141
179
|
#
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
144
188
|
end
|
|
145
189
|
|
|
146
190
|
##
|
|
147
191
|
#
|
|
148
|
-
# Double Click the Element
|
|
192
|
+
# Double Click the Element.
|
|
149
193
|
#
|
|
150
|
-
|
|
151
|
-
|
|
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
|
|
152
201
|
end
|
|
153
202
|
|
|
154
203
|
##
|
|
155
204
|
#
|
|
156
|
-
# Send Keystrokes to the Element
|
|
205
|
+
# Send Keystrokes to the Element.
|
|
157
206
|
#
|
|
158
207
|
# @overload send_keys(keys, ...)
|
|
159
|
-
# @param [String, Symbol, Array<String,Symbol>]
|
|
208
|
+
# @param keys [String, Symbol, Array<String,Symbol>]
|
|
160
209
|
#
|
|
161
210
|
# Examples:
|
|
162
211
|
#
|
|
163
212
|
# element.send_keys "foo" #=> value: 'foo'
|
|
164
|
-
# element.send_keys "tet", :left, "s"
|
|
213
|
+
# element.send_keys "tet", :left, "s" #=> value: 'test'
|
|
165
214
|
# element.send_keys [:control, 'a'], :space #=> value: ' ' - assuming ctrl-a selects all contents
|
|
166
215
|
#
|
|
167
|
-
# Symbols supported for keys
|
|
168
|
-
# :cancel
|
|
169
|
-
# :help
|
|
170
|
-
# :backspace
|
|
171
|
-
# :tab
|
|
172
|
-
# :clear
|
|
173
|
-
# :return
|
|
174
|
-
# :enter
|
|
175
|
-
# :shift
|
|
176
|
-
# :control
|
|
177
|
-
# :alt
|
|
178
|
-
# :pause
|
|
179
|
-
# :escape
|
|
180
|
-
# :space
|
|
181
|
-
# :page_up
|
|
182
|
-
# :page_down
|
|
183
|
-
# :end
|
|
184
|
-
# :home
|
|
185
|
-
# :left
|
|
186
|
-
# :up
|
|
187
|
-
# :right
|
|
188
|
-
# :down
|
|
189
|
-
# :insert
|
|
190
|
-
# :delete
|
|
191
|
-
# :semicolon
|
|
192
|
-
# :equals
|
|
193
|
-
# :numpad0
|
|
194
|
-
# :numpad1
|
|
195
|
-
# :numpad2
|
|
196
|
-
# :numpad3
|
|
197
|
-
# :numpad4
|
|
198
|
-
# :numpad5
|
|
199
|
-
# :numpad6
|
|
200
|
-
# :numpad7
|
|
201
|
-
# :numpad8
|
|
202
|
-
# :numpad9
|
|
203
|
-
# :multiply - numeric keypad *
|
|
204
|
-
# :add - numeric keypad +
|
|
205
|
-
# :separator - numeric keypad 'separator' key ??
|
|
206
|
-
# :subtract - numeric keypad -
|
|
207
|
-
# :decimal - numeric keypad .
|
|
208
|
-
# :divide - numeric keypad /
|
|
209
|
-
# :f1
|
|
210
|
-
# :f2
|
|
211
|
-
# :f3
|
|
212
|
-
# :f4
|
|
213
|
-
# :f5
|
|
214
|
-
# :f6
|
|
215
|
-
# :f7
|
|
216
|
-
# :f8
|
|
217
|
-
# :f9
|
|
218
|
-
# :f10
|
|
219
|
-
# :f11
|
|
220
|
-
# :f12
|
|
221
|
-
# :meta
|
|
222
|
-
# :command - alias of :meta
|
|
223
|
-
#
|
|
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
|
|
224
274
|
def send_keys(*args)
|
|
225
275
|
synchronize { base.send_keys(*args) }
|
|
276
|
+
self
|
|
226
277
|
end
|
|
227
278
|
|
|
228
279
|
##
|
|
229
280
|
#
|
|
230
|
-
# Hover on the Element
|
|
281
|
+
# Hover on the Element.
|
|
231
282
|
#
|
|
283
|
+
# @return [Capybara::Node::Element] The element
|
|
232
284
|
def hover
|
|
233
285
|
synchronize { base.hover }
|
|
286
|
+
self
|
|
234
287
|
end
|
|
235
288
|
|
|
236
289
|
##
|
|
@@ -238,7 +291,8 @@ module Capybara
|
|
|
238
291
|
# @return [String] The tag name of the element
|
|
239
292
|
#
|
|
240
293
|
def tag_name
|
|
241
|
-
|
|
294
|
+
# Element type is immutable so cache it
|
|
295
|
+
@tag_name ||= initial_cache[:tag_name] || synchronize { base.tag_name }
|
|
242
296
|
end
|
|
243
297
|
|
|
244
298
|
##
|
|
@@ -252,6 +306,17 @@ module Capybara
|
|
|
252
306
|
synchronize { base.visible? }
|
|
253
307
|
end
|
|
254
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
|
+
|
|
255
320
|
##
|
|
256
321
|
#
|
|
257
322
|
# Whether or not the element is checked.
|
|
@@ -284,7 +349,27 @@ module Capybara
|
|
|
284
349
|
|
|
285
350
|
##
|
|
286
351
|
#
|
|
287
|
-
#
|
|
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.
|
|
288
373
|
#
|
|
289
374
|
# @return [String] An XPath expression
|
|
290
375
|
#
|
|
@@ -292,15 +377,23 @@ module Capybara
|
|
|
292
377
|
synchronize { base.path }
|
|
293
378
|
end
|
|
294
379
|
|
|
380
|
+
def rect
|
|
381
|
+
synchronize { base.rect }
|
|
382
|
+
end
|
|
383
|
+
|
|
295
384
|
##
|
|
296
385
|
#
|
|
297
386
|
# Trigger any event on the current element, for example mouseover or focus
|
|
298
|
-
# events.
|
|
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.
|
|
299
390
|
#
|
|
300
391
|
# @param [String] event The name of the event to trigger
|
|
301
392
|
#
|
|
393
|
+
# @return [Capybara::Node::Element] The element
|
|
302
394
|
def trigger(event)
|
|
303
395
|
synchronize { base.trigger(event) }
|
|
396
|
+
self
|
|
304
397
|
end
|
|
305
398
|
|
|
306
399
|
##
|
|
@@ -312,27 +405,197 @@ module Capybara
|
|
|
312
405
|
# source.drag_to(target)
|
|
313
406
|
#
|
|
314
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 { |arg| arg.respond_to?(:to_path) ? arg.to_path : arg }
|
|
439
|
+
synchronize { base.drop(*options) }
|
|
440
|
+
self
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
##
|
|
444
|
+
#
|
|
445
|
+
# Scroll the page or element.
|
|
446
|
+
#
|
|
447
|
+
# @overload scroll_to(position, offset: [0,0])
|
|
448
|
+
# Scroll the page or element to its top, bottom or middle.
|
|
449
|
+
# @param [:top, :bottom, :center, :current] position
|
|
450
|
+
# @param [[Integer, Integer]] offset
|
|
451
|
+
#
|
|
452
|
+
# @overload scroll_to(element, align: :top)
|
|
453
|
+
# Scroll the page or current element until the given element is aligned at the top, bottom, or center of it.
|
|
454
|
+
# @param [Capybara::Node::Element] element The element to be scrolled into view
|
|
455
|
+
# @param [:top, :bottom, :center] align Where to align the element being scrolled into view with relation to the current page/element if possible
|
|
456
|
+
#
|
|
457
|
+
# @overload scroll_to(x,y)
|
|
458
|
+
# @param [Integer] x Horizontal scroll offset
|
|
459
|
+
# @param [Integer] y Vertical scroll offset
|
|
460
|
+
#
|
|
461
|
+
# @return [Capybara::Node::Element] The element
|
|
462
|
+
def scroll_to(pos_or_el_or_x, y = nil, align: :top, offset: nil)
|
|
463
|
+
case pos_or_el_or_x
|
|
464
|
+
when Symbol
|
|
465
|
+
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
|
466
|
+
when Capybara::Node::Element
|
|
467
|
+
synchronize { base.scroll_to(pos_or_el_or_x.base, align) }
|
|
468
|
+
else
|
|
469
|
+
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
|
470
|
+
end
|
|
471
|
+
synchronize { base.scroll_by(*offset) } unless offset.nil?
|
|
472
|
+
self
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
##
|
|
476
|
+
#
|
|
477
|
+
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
|
478
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
|
479
|
+
# {#evaluate_script} whenever a result is not expected or needed. `this` in the script will refer to the element this is called on.
|
|
480
|
+
#
|
|
481
|
+
# @param [String] script A string of JavaScript to execute
|
|
482
|
+
# @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
|
|
483
|
+
#
|
|
484
|
+
def execute_script(script, *args)
|
|
485
|
+
session.execute_script(<<~JS, self, *args)
|
|
486
|
+
(function (){
|
|
487
|
+
#{script}
|
|
488
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
489
|
+
JS
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
##
|
|
493
|
+
#
|
|
494
|
+
# Evaluate the given JS in the context of the element and return the result. Be careful when using this with
|
|
495
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
|
496
|
+
# be a better alternative. `this` in the script will refer to the element this is called on.
|
|
497
|
+
#
|
|
498
|
+
# @param [String] script A string of JavaScript to evaluate
|
|
499
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
315
500
|
#
|
|
316
|
-
def
|
|
317
|
-
|
|
501
|
+
def evaluate_script(script, *args)
|
|
502
|
+
session.evaluate_script(<<~JS, self, *args)
|
|
503
|
+
(function(){
|
|
504
|
+
return #{script.strip}
|
|
505
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
506
|
+
JS
|
|
318
507
|
end
|
|
319
508
|
|
|
509
|
+
##
|
|
510
|
+
#
|
|
511
|
+
# Evaluate the given JavaScript in the context of the element and obtain the result from a
|
|
512
|
+
# callback function which will be passed as the last argument to the script. `this` in the
|
|
513
|
+
# script will refer to the element this is called on.
|
|
514
|
+
#
|
|
515
|
+
# @param [String] script A string of JavaScript to evaluate
|
|
516
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
517
|
+
#
|
|
518
|
+
def evaluate_async_script(script, *args)
|
|
519
|
+
session.evaluate_async_script(<<~JS, self, *args)
|
|
520
|
+
(function (){
|
|
521
|
+
#{script}
|
|
522
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
|
523
|
+
JS
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
##
|
|
527
|
+
#
|
|
528
|
+
# Toggle the elements background color between white and black for a period of time.
|
|
529
|
+
#
|
|
530
|
+
# @return [Capybara::Node::Element] The element
|
|
531
|
+
def flash
|
|
532
|
+
execute_script(<<~JS, 100)
|
|
533
|
+
async function flash(el, delay){
|
|
534
|
+
var old_bg = el.style.backgroundColor;
|
|
535
|
+
var colors = ["black", "white"];
|
|
536
|
+
for(var i=0; i<20; i++){
|
|
537
|
+
el.style.backgroundColor = colors[i % colors.length];
|
|
538
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
539
|
+
}
|
|
540
|
+
el.style.backgroundColor = old_bg;
|
|
541
|
+
}
|
|
542
|
+
flash(this, arguments[0]);
|
|
543
|
+
JS
|
|
544
|
+
|
|
545
|
+
self
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# @api private
|
|
320
549
|
def reload
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
550
|
+
return self unless @allow_reload
|
|
551
|
+
|
|
552
|
+
begin
|
|
553
|
+
reloaded = @query.resolve_for(query_scope.reload)[@query_idx.to_i]
|
|
554
|
+
@base = reloaded.base if reloaded
|
|
555
|
+
rescue StandardError => e
|
|
556
|
+
raise e unless catch_error?(e)
|
|
328
557
|
end
|
|
329
558
|
self
|
|
330
559
|
end
|
|
331
560
|
|
|
561
|
+
##
|
|
562
|
+
#
|
|
563
|
+
# A human-readable representation of the element.
|
|
564
|
+
#
|
|
565
|
+
# @return [String] A string representation
|
|
332
566
|
def inspect
|
|
333
|
-
%(#<Capybara::Node::Element tag="#{tag_name}" path="#{path}">)
|
|
567
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
|
|
334
568
|
rescue NotSupportedByDriverError
|
|
335
|
-
%(#<Capybara::Node::Element tag="#{tag_name}">)
|
|
569
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}">)
|
|
570
|
+
rescue *session.driver.invalid_element_errors
|
|
571
|
+
%(Obsolete #<Capybara::Node::Element>)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# @api private
|
|
575
|
+
def initial_cache
|
|
576
|
+
base.respond_to?(:initial_cache) ? base.initial_cache : {}
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
STYLE_SCRIPT = <<~JS
|
|
580
|
+
(function(){
|
|
581
|
+
var s = window.getComputedStyle(this);
|
|
582
|
+
var result = {};
|
|
583
|
+
for (var i = arguments.length; i--; ) {
|
|
584
|
+
var property_name = arguments[i];
|
|
585
|
+
result[property_name] = s.getPropertyValue(property_name);
|
|
586
|
+
}
|
|
587
|
+
return result;
|
|
588
|
+
}).apply(this, arguments)
|
|
589
|
+
JS
|
|
590
|
+
|
|
591
|
+
private
|
|
592
|
+
|
|
593
|
+
def perform_click_action(keys, wait: nil, **options)
|
|
594
|
+
raise ArgumentError, 'You must specify both x: and y: for a click offset' if nil ^ options[:x] ^ options[:y]
|
|
595
|
+
|
|
596
|
+
options[:offset] ||= :center if session_options.w3c_click_offset
|
|
597
|
+
synchronize(wait) { yield keys, options }
|
|
598
|
+
self
|
|
336
599
|
end
|
|
337
600
|
end
|
|
338
601
|
end
|