capybara 2.18.0 → 3.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/History.md +945 -12
- data/License.txt +1 -1
- data/README.md +264 -90
- data/lib/capybara/config.rb +29 -57
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +35 -18
- data/lib/capybara/driver/node.rb +40 -10
- data/lib/capybara/dsl.rb +10 -7
- data/lib/capybara/helpers.rb +70 -31
- data/lib/capybara/minitest/spec.rb +185 -83
- data/lib/capybara/minitest.rb +232 -112
- data/lib/capybara/node/actions.rb +274 -171
- data/lib/capybara/node/base.rb +42 -34
- data/lib/capybara/node/document.rb +15 -3
- data/lib/capybara/node/document_matchers.rb +19 -21
- data/lib/capybara/node/element.rb +362 -135
- data/lib/capybara/node/finders.rb +151 -137
- data/lib/capybara/node/matchers.rb +394 -209
- data/lib/capybara/node/simple.rb +59 -26
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +12 -9
- data/lib/capybara/queries/base_query.rb +39 -28
- data/lib/capybara/queries/current_path_query.rb +21 -27
- data/lib/capybara/queries/match_query.rb +14 -7
- data/lib/capybara/queries/selector_query.rb +659 -149
- data/lib/capybara/queries/sibling_query.rb +11 -9
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +56 -38
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +114 -42
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +22 -17
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +93 -58
- data/lib/capybara/rack_test/node.rb +184 -81
- data/lib/capybara/rails.rb +3 -7
- data/lib/capybara/registration_container.rb +41 -0
- data/lib/capybara/registrations/drivers.rb +42 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +66 -0
- data/lib/capybara/result.rb +97 -63
- data/lib/capybara/rspec/features.rb +17 -50
- data/lib/capybara/rspec/matcher_proxies.rb +52 -15
- data/lib/capybara/rspec/matchers/base.rb +113 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +146 -310
- data/lib/capybara/rspec.rb +7 -11
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
- data/lib/capybara/selector/css.rb +85 -13
- data/lib/capybara/selector/definition/button.rb +68 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +28 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +62 -0
- data/lib/capybara/selector/definition/link.rb +55 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/definition.rb +280 -0
- data/lib/capybara/selector/filter.rb +2 -17
- data/lib/capybara/selector/filter_set.rb +80 -33
- data/lib/capybara/selector/filters/base.rb +50 -6
- data/lib/capybara/selector/filters/expression_filter.rb +8 -26
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +16 -12
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +93 -210
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +475 -523
- 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 +298 -267
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +110 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +76 -0
- data/lib/capybara/selenium/node.rb +517 -145
- data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
- data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
- data/lib/capybara/selenium/patches/atoms.rb +18 -0
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +45 -0
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +80 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +71 -0
- data/lib/capybara/server.rb +59 -67
- data/lib/capybara/session/config.rb +81 -67
- data/lib/capybara/session/matchers.rb +28 -20
- data/lib/capybara/session.rb +337 -365
- 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 +151 -12
- data/lib/capybara/spec/session/accept_alert_spec.rb +12 -11
- data/lib/capybara/spec/session/accept_confirm_spec.rb +6 -5
- data/lib/capybara/spec/session/accept_prompt_spec.rb +10 -10
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +161 -57
- data/lib/capybara/spec/session/ancestor_spec.rb +27 -24
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +68 -38
- 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.rb → assert_title_spec.rb} +22 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +144 -69
- data/lib/capybara/spec/session/body_spec.rb +12 -13
- data/lib/capybara/spec/session/check_spec.rb +117 -55
- data/lib/capybara/spec/session/choose_spec.rb +64 -31
- data/lib/capybara/spec/session/click_button_spec.rb +231 -173
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +55 -35
- data/lib/capybara/spec/session/click_link_spec.rb +93 -58
- data/lib/capybara/spec/session/current_scope_spec.rb +12 -11
- data/lib/capybara/spec/session/current_url_spec.rb +57 -39
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +4 -4
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +3 -2
- data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
- data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +71 -57
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +8 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +29 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +10 -8
- data/lib/capybara/spec/session/fill_in_spec.rb +134 -43
- data/lib/capybara/spec/session/find_button_spec.rb +25 -24
- data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
- data/lib/capybara/spec/session/find_field_spec.rb +37 -41
- data/lib/capybara/spec/session/find_link_spec.rb +46 -17
- data/lib/capybara/spec/session/find_spec.rb +260 -145
- data/lib/capybara/spec/session/first_spec.rb +80 -52
- 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 +33 -20
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +52 -32
- data/lib/capybara/spec/session/go_back_spec.rb +2 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +31 -31
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
- data/lib/capybara/spec/session/has_button_spec.rb +100 -13
- data/lib/capybara/spec/session/has_css_spec.rb +272 -137
- data/lib/capybara/spec/session/has_current_path_spec.rb +60 -61
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +139 -59
- data/lib/capybara/spec/session/has_link_spec.rb +47 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +42 -40
- data/lib/capybara/spec/session/has_select_spec.rb +107 -72
- data/lib/capybara/spec/session/has_selector_spec.rb +120 -71
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +183 -5
- data/lib/capybara/spec/session/has_text_spec.rb +106 -62
- data/lib/capybara/spec/session/has_title_spec.rb +20 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +57 -38
- 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 +37 -0
- data/lib/capybara/spec/session/node_spec.rb +1024 -153
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +12 -6
- data/lib/capybara/spec/session/reset_session_spec.rb +82 -35
- 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 +8 -12
- 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 +119 -0
- data/lib/capybara/spec/session/select_spec.rb +107 -81
- data/lib/capybara/spec/session/selectors_spec.rb +52 -19
- data/lib/capybara/spec/session/sibling_spec.rb +10 -10
- data/lib/capybara/spec/session/text_spec.rb +37 -21
- data/lib/capybara/spec/session/title_spec.rb +17 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +43 -23
- data/lib/capybara/spec/session/unselect_spec.rb +39 -38
- data/lib/capybara/spec/session/visit_spec.rb +85 -53
- data/lib/capybara/spec/session/window/become_closed_spec.rb +24 -20
- 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 +27 -22
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +12 -6
- data/lib/capybara/spec/session/window/window_spec.rb +97 -63
- data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
- data/lib/capybara/spec/session/window/within_window_spec.rb +31 -86
- data/lib/capybara/spec/session/within_spec.rb +83 -44
- data/lib/capybara/spec/spec_helper.rb +54 -44
- data/lib/capybara/spec/test_app.rb +158 -43
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +163 -42
- data/lib/capybara/spec/views/frame_child.erb +4 -3
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +33 -0
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +21 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +68 -1
- data/lib/capybara/spec/views/with_animation.erb +81 -0
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +69 -12
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +30 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +21 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +5 -2
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +36 -34
- data/lib/capybara.rb +134 -107
- data/spec/basic_node_spec.rb +60 -34
- data/spec/capybara_spec.rb +63 -88
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +85 -64
- data/spec/filter_set_spec.rb +27 -9
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +6 -5
- data/spec/fixtures/selenium_driver_rspec_success.rb +6 -5
- data/spec/minitest_spec.rb +52 -7
- data/spec/minitest_spec_spec.rb +94 -63
- data/spec/per_session_config_spec.rb +14 -13
- data/spec/rack_test_spec.rb +194 -125
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +111 -50
- data/spec/rspec/features_spec.rb +37 -31
- data/spec/rspec/scenarios_spec.rb +10 -8
- data/spec/rspec/shared_spec_matchers.rb +473 -422
- data/spec/rspec/views_spec.rb +5 -3
- data/spec/rspec_matchers_spec.rb +52 -11
- data/spec/rspec_spec.rb +109 -89
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +397 -68
- data/spec/selenium_spec_chrome.rb +187 -40
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +60 -0
- data/spec/selenium_spec_firefox.rb +201 -41
- data/spec/selenium_spec_firefox_remote.rb +94 -0
- data/spec/selenium_spec_ie.rb +149 -0
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +213 -102
- data/spec/session_spec.rb +53 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +473 -122
- data/spec/spec_helper.rb +126 -7
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +355 -73
- data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
- data/.yard/templates_custom/default/class/html/setup.rb +0 -17
- data/.yard/yard_extensions.rb +0 -78
- data/lib/capybara/query.rb +0 -7
- data/lib/capybara/rspec/compound.rb +0 -95
- data/lib/capybara/spec/session/assert_current_path.rb +0 -72
- data/lib/capybara/spec/session/assert_selector.rb +0 -148
- data/lib/capybara/spec/session/assert_text.rb +0 -234
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
- data/spec/selenium_spec_marionette.rb +0 -127
data/lib/capybara/session.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'capybara/session/matchers'
|
|
3
4
|
require 'addressable/uri'
|
|
4
5
|
|
|
5
6
|
module Capybara
|
|
6
|
-
|
|
7
7
|
##
|
|
8
8
|
#
|
|
9
|
-
# The Session class represents a single user's interaction with the system. The Session can use
|
|
9
|
+
# The {Session} class represents a single user's interaction with the system. The {Session} can use
|
|
10
10
|
# any of the underlying drivers. A session can be initialized manually like this:
|
|
11
11
|
#
|
|
12
12
|
# session = Capybara::Session.new(:culerity, MyRackApp)
|
|
@@ -17,85 +17,90 @@ module Capybara
|
|
|
17
17
|
# session = Capybara::Session.new(:culerity)
|
|
18
18
|
# session.visit('http://www.google.com')
|
|
19
19
|
#
|
|
20
|
-
# When Capybara.threadsafe
|
|
20
|
+
# When {Capybara.configure threadsafe} is `true` the sessions options will be initially set to the
|
|
21
21
|
# current values of the global options and a configuration block can be passed to the session initializer.
|
|
22
|
-
# For available options see {Capybara::SessionConfig::OPTIONS}
|
|
22
|
+
# For available options see {Capybara::SessionConfig::OPTIONS}:
|
|
23
23
|
#
|
|
24
24
|
# session = Capybara::Session.new(:driver, MyRackApp) do |config|
|
|
25
25
|
# config.app_host = "http://my_host.dev"
|
|
26
26
|
# end
|
|
27
27
|
#
|
|
28
|
-
# Session provides a number of methods for controlling the navigation of the page, such as
|
|
29
|
-
#
|
|
28
|
+
# The {Session} provides a number of methods for controlling the navigation of the page, such as {#visit},
|
|
29
|
+
# {#current_path}, and so on. It also delegates a number of methods to a {Capybara::Document}, representing
|
|
30
30
|
# the current HTML document. This allows interaction:
|
|
31
31
|
#
|
|
32
32
|
# session.fill_in('q', with: 'Capybara')
|
|
33
33
|
# session.click_button('Search')
|
|
34
34
|
# expect(session).to have_content('Capybara')
|
|
35
35
|
#
|
|
36
|
-
# When using capybara/dsl
|
|
36
|
+
# When using `capybara/dsl`, the {Session} is initialized automatically for you.
|
|
37
37
|
#
|
|
38
38
|
class Session
|
|
39
39
|
include Capybara::SessionMatchers
|
|
40
40
|
|
|
41
|
-
NODE_METHODS = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
NODE_METHODS = %i[
|
|
42
|
+
all first attach_file text check choose scroll_to scroll_by
|
|
43
|
+
click double_click right_click
|
|
44
|
+
click_link_or_button click_button click_link
|
|
45
|
+
fill_in find find_all find_button find_by_id find_field find_link
|
|
46
|
+
has_content? has_text? has_css? has_no_content? has_no_text?
|
|
47
|
+
has_no_css? has_no_xpath? has_xpath? select uncheck
|
|
48
|
+
has_element? has_no_element?
|
|
49
|
+
has_link? has_no_link? has_button? has_no_button? has_field?
|
|
50
|
+
has_no_field? has_checked_field? has_unchecked_field?
|
|
51
|
+
has_no_table? has_table? unselect has_select? has_no_select?
|
|
52
|
+
has_selector? has_no_selector? click_on has_no_checked_field?
|
|
53
|
+
has_no_unchecked_field? query assert_selector assert_no_selector
|
|
54
|
+
assert_all_of_selectors assert_none_of_selectors assert_any_of_selectors
|
|
55
|
+
refute_selector assert_text assert_no_text
|
|
56
|
+
].freeze
|
|
55
57
|
# @api private
|
|
56
|
-
DOCUMENT_METHODS = [
|
|
57
|
-
|
|
58
|
-
]
|
|
59
|
-
SESSION_METHODS = [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
] + DOCUMENT_METHODS
|
|
69
|
-
MODAL_METHODS = [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
]
|
|
58
|
+
DOCUMENT_METHODS = %i[
|
|
59
|
+
title assert_title assert_no_title has_title? has_no_title?
|
|
60
|
+
].freeze
|
|
61
|
+
SESSION_METHODS = %i[
|
|
62
|
+
body html source current_url current_host current_path
|
|
63
|
+
execute_script evaluate_script evaluate_async_script visit refresh go_back go_forward send_keys
|
|
64
|
+
within within_element within_fieldset within_table within_frame switch_to_frame
|
|
65
|
+
current_window windows open_new_window switch_to_window within_window window_opened_by
|
|
66
|
+
save_page save_and_open_page save_screenshot
|
|
67
|
+
save_and_open_screenshot reset_session! response_headers
|
|
68
|
+
status_code current_scope
|
|
69
|
+
assert_current_path assert_no_current_path has_current_path? has_no_current_path?
|
|
70
|
+
].freeze + DOCUMENT_METHODS
|
|
71
|
+
MODAL_METHODS = %i[
|
|
72
|
+
accept_alert accept_confirm dismiss_confirm accept_prompt dismiss_prompt
|
|
73
|
+
].freeze
|
|
73
74
|
DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS
|
|
74
75
|
|
|
75
76
|
attr_reader :mode, :app, :server
|
|
76
77
|
attr_accessor :synchronized
|
|
77
78
|
|
|
78
|
-
def initialize(mode, app=nil)
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
def initialize(mode, app = nil)
|
|
80
|
+
if app && !app.respond_to?(:call)
|
|
81
|
+
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@@instance_created = true # rubocop:disable Style/ClassVars
|
|
81
85
|
@mode = mode
|
|
82
86
|
@app = app
|
|
83
87
|
if block_given?
|
|
84
|
-
raise
|
|
85
|
-
|
|
88
|
+
raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe
|
|
89
|
+
|
|
90
|
+
yield config
|
|
86
91
|
end
|
|
87
|
-
if config.run_server
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@
|
|
92
|
+
@server = if config.run_server && @app && driver.needs_server?
|
|
93
|
+
server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
|
|
94
|
+
server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
|
|
95
|
+
Capybara::Server.new(@app, **server_options).boot
|
|
91
96
|
end
|
|
92
97
|
@touched = false
|
|
93
98
|
end
|
|
94
99
|
|
|
95
100
|
def driver
|
|
96
101
|
@driver ||= begin
|
|
97
|
-
unless Capybara.drivers
|
|
98
|
-
other_drivers = Capybara.drivers.
|
|
102
|
+
unless Capybara.drivers[mode]
|
|
103
|
+
other_drivers = Capybara.drivers.names.map(&:inspect)
|
|
99
104
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
|
100
105
|
end
|
|
101
106
|
driver = Capybara.drivers[mode].call(app)
|
|
@@ -106,28 +111,30 @@ module Capybara
|
|
|
106
111
|
|
|
107
112
|
##
|
|
108
113
|
#
|
|
109
|
-
# Reset the session (i.e. remove cookies and navigate to blank page)
|
|
114
|
+
# Reset the session (i.e. remove cookies and navigate to blank page).
|
|
110
115
|
#
|
|
111
116
|
# This method does not:
|
|
112
117
|
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
118
|
+
# * accept modal dialogs if they are present (Selenium driver now does, others may not)
|
|
119
|
+
# * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
|
|
120
|
+
# * modify state of the driver/underlying browser in any other way
|
|
116
121
|
#
|
|
117
122
|
# as doing so will result in performance downsides and it's not needed to do everything from the list above for most apps.
|
|
118
123
|
#
|
|
119
124
|
# If you want to do anything from the list above on a general basis you can:
|
|
120
125
|
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
126
|
+
# * write RSpec/Cucumber/etc. after hook
|
|
127
|
+
# * monkeypatch this method
|
|
128
|
+
# * use Ruby's `prepend` method
|
|
124
129
|
#
|
|
125
130
|
def reset!
|
|
126
131
|
if @touched
|
|
127
132
|
driver.reset!
|
|
128
133
|
@touched = false
|
|
134
|
+
switch_to_frame(:top) rescue nil # rubocop:disable Style/RescueModifier
|
|
135
|
+
@scopes = [nil]
|
|
129
136
|
end
|
|
130
|
-
@server
|
|
137
|
+
@server&.wait_for_pending_requests
|
|
131
138
|
raise_server_error!
|
|
132
139
|
end
|
|
133
140
|
alias_method :cleanup!, :reset!
|
|
@@ -135,29 +142,39 @@ module Capybara
|
|
|
135
142
|
|
|
136
143
|
##
|
|
137
144
|
#
|
|
138
|
-
#
|
|
145
|
+
# Disconnect from the current driver. A new driver will be instantiated on the next interaction.
|
|
146
|
+
#
|
|
147
|
+
def quit
|
|
148
|
+
@driver.quit if @driver.respond_to? :quit
|
|
149
|
+
@document = @driver = nil
|
|
150
|
+
@touched = false
|
|
151
|
+
@server&.reset_error!
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
##
|
|
155
|
+
#
|
|
156
|
+
# Raise errors encountered in the server.
|
|
139
157
|
#
|
|
140
158
|
def raise_server_error!
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
rescue CapybaraError
|
|
148
|
-
#needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
|
|
149
|
-
raise @server.error, @server.error.message, @server.error.backtrace
|
|
150
|
-
ensure
|
|
151
|
-
@server.reset_error!
|
|
159
|
+
return unless @server&.error
|
|
160
|
+
|
|
161
|
+
# Force an explanation for the error being raised as the exception cause
|
|
162
|
+
begin
|
|
163
|
+
if config.raise_server_errors
|
|
164
|
+
raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
|
|
152
165
|
end
|
|
166
|
+
rescue CapybaraError => capy_error # rubocop:disable Naming/RescuedExceptionsVariableName
|
|
167
|
+
raise @server.error, cause: capy_error
|
|
168
|
+
ensure
|
|
169
|
+
@server.reset_error!
|
|
153
170
|
end
|
|
154
171
|
end
|
|
155
172
|
|
|
156
173
|
##
|
|
157
174
|
#
|
|
158
|
-
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
|
|
175
|
+
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium).
|
|
159
176
|
#
|
|
160
|
-
# @return [Hash
|
|
177
|
+
# @return [Hash<String, String>] A hash of response headers.
|
|
161
178
|
#
|
|
162
179
|
def response_headers
|
|
163
180
|
driver.response_headers
|
|
@@ -165,7 +182,7 @@ module Capybara
|
|
|
165
182
|
|
|
166
183
|
##
|
|
167
184
|
#
|
|
168
|
-
# Returns the current HTTP status code as an
|
|
185
|
+
# Returns the current HTTP status code as an integer. Not supported by all drivers (e.g. Selenium).
|
|
169
186
|
#
|
|
170
187
|
# @return [Integer] Current HTTP status code
|
|
171
188
|
#
|
|
@@ -178,7 +195,7 @@ module Capybara
|
|
|
178
195
|
# @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
|
|
179
196
|
#
|
|
180
197
|
def html
|
|
181
|
-
driver.html
|
|
198
|
+
driver.html || ''
|
|
182
199
|
end
|
|
183
200
|
alias_method :body, :html
|
|
184
201
|
alias_method :source, :html
|
|
@@ -191,13 +208,11 @@ module Capybara
|
|
|
191
208
|
# Addressable parsing is more lenient than URI
|
|
192
209
|
uri = ::Addressable::URI.parse(current_url)
|
|
193
210
|
|
|
194
|
-
# If current_url ends up being nil, won't be able to call .path on a NilClass.
|
|
195
|
-
return nil if uri.nil?
|
|
196
|
-
|
|
197
211
|
# Addressable doesn't support opaque URIs - we want nil here
|
|
198
|
-
return nil if uri
|
|
199
|
-
|
|
200
|
-
path
|
|
212
|
+
return nil if uri&.scheme == 'about'
|
|
213
|
+
|
|
214
|
+
path = uri&.path
|
|
215
|
+
path unless path&.empty?
|
|
201
216
|
end
|
|
202
217
|
|
|
203
218
|
##
|
|
@@ -227,13 +242,13 @@ module Capybara
|
|
|
227
242
|
#
|
|
228
243
|
# For drivers which can run against an external application, such as the selenium driver
|
|
229
244
|
# giving an absolute URL will navigate to that page. This allows testing applications
|
|
230
|
-
# running on remote servers. For these drivers, setting {Capybara.app_host} will make the
|
|
245
|
+
# running on remote servers. For these drivers, setting {Capybara.configure app_host} will make the
|
|
231
246
|
# remote server the default. For example:
|
|
232
247
|
#
|
|
233
248
|
# Capybara.app_host = 'http://google.com'
|
|
234
249
|
# session.visit('/') # visits the google homepage
|
|
235
250
|
#
|
|
236
|
-
# If {Capybara.always_include_port} is set to true and this session is running against
|
|
251
|
+
# If {Capybara.configure always_include_port} is set to `true` and this session is running against
|
|
237
252
|
# a rack application, then the port that the rack application is running on will automatically
|
|
238
253
|
# be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
|
|
239
254
|
#
|
|
@@ -248,27 +263,19 @@ module Capybara
|
|
|
248
263
|
@touched = true
|
|
249
264
|
|
|
250
265
|
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
|
|
266
|
+
base_uri = ::Addressable::URI.parse(config.app_host || server_url)
|
|
251
267
|
|
|
252
|
-
|
|
253
|
-
::Addressable::URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
|
|
254
|
-
else
|
|
255
|
-
config.app_host && ::Addressable::URI.parse(config.app_host)
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
|
268
|
+
if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
|
259
269
|
if visit_uri.relative?
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
visit_uri_parts = visit_uri.to_hash.delete_if { |k,v| v.nil? }
|
|
270
|
+
visit_uri_parts = visit_uri.to_hash.compact
|
|
263
271
|
|
|
264
272
|
# Useful to people deploying to a subdirectory
|
|
265
273
|
# and/or single page apps where only the url fragment changes
|
|
266
|
-
visit_uri_parts[:path] =
|
|
274
|
+
visit_uri_parts[:path] = base_uri.path + visit_uri.path
|
|
267
275
|
|
|
268
|
-
visit_uri =
|
|
269
|
-
else
|
|
270
|
-
visit_uri.port ||= @server.port if @server && config.always_include_port
|
|
276
|
+
visit_uri = base_uri.merge(visit_uri_parts)
|
|
271
277
|
end
|
|
278
|
+
adjust_server_port(visit_uri)
|
|
272
279
|
end
|
|
273
280
|
|
|
274
281
|
driver.visit(visit_uri.to_s)
|
|
@@ -276,7 +283,7 @@ module Capybara
|
|
|
276
283
|
|
|
277
284
|
##
|
|
278
285
|
#
|
|
279
|
-
# Refresh the page
|
|
286
|
+
# Refresh the page.
|
|
280
287
|
#
|
|
281
288
|
def refresh
|
|
282
289
|
raise_server_error!
|
|
@@ -300,9 +307,27 @@ module Capybara
|
|
|
300
307
|
end
|
|
301
308
|
|
|
302
309
|
##
|
|
310
|
+
# @!method send_keys
|
|
311
|
+
# @see Capybara::Node::Element#send_keys
|
|
303
312
|
#
|
|
304
|
-
|
|
305
|
-
|
|
313
|
+
def send_keys(...)
|
|
314
|
+
driver.send_keys(...)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
##
|
|
318
|
+
#
|
|
319
|
+
# Returns the element with focus.
|
|
320
|
+
#
|
|
321
|
+
# Not supported by Rack Test
|
|
322
|
+
#
|
|
323
|
+
def active_element
|
|
324
|
+
Capybara::Queries::ActiveElementQuery.new.resolve_for(self)[0].tap(&:allow_reload!)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
#
|
|
329
|
+
# Executes the given block within the context of a node. {#within} takes the
|
|
330
|
+
# same options as {Capybara::Node::Finders#find #find}, as well as a block. For the duration of the
|
|
306
331
|
# block, any command to Capybara will be handled as though it were scoped
|
|
307
332
|
# to the given element.
|
|
308
333
|
#
|
|
@@ -310,18 +335,18 @@ module Capybara
|
|
|
310
335
|
# fill_in('Street', with: '12 Main Street')
|
|
311
336
|
# end
|
|
312
337
|
#
|
|
313
|
-
# Just as with
|
|
314
|
-
#
|
|
338
|
+
# Just as with `#find`, if multiple elements match the selector given to
|
|
339
|
+
# {#within}, an error will be raised, and just as with `#find`, this
|
|
315
340
|
# behaviour can be controlled through the `:match` and `:exact` options.
|
|
316
341
|
#
|
|
317
342
|
# It is possible to omit the first parameter, in that case, the selector is
|
|
318
|
-
# assumed to be of the type set in Capybara.default_selector.
|
|
343
|
+
# assumed to be of the type set in {Capybara.configure default_selector}.
|
|
319
344
|
#
|
|
320
345
|
# within('div#delivery-address') do
|
|
321
346
|
# fill_in('Street', with: '12 Main Street')
|
|
322
347
|
# end
|
|
323
348
|
#
|
|
324
|
-
# Note that a lot of uses of
|
|
349
|
+
# Note that a lot of uses of {#within} can be replaced more succinctly with
|
|
325
350
|
# chaining:
|
|
326
351
|
#
|
|
327
352
|
# find('div#delivery-address').fill_in('Street', with: '12 Main Street')
|
|
@@ -334,11 +359,11 @@ module Capybara
|
|
|
334
359
|
#
|
|
335
360
|
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
|
336
361
|
#
|
|
337
|
-
def within(*args)
|
|
338
|
-
new_scope =
|
|
362
|
+
def within(*args, **kw_args)
|
|
363
|
+
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
|
|
339
364
|
begin
|
|
340
365
|
scopes.push(new_scope)
|
|
341
|
-
yield
|
|
366
|
+
yield new_scope if block_given?
|
|
342
367
|
ensure
|
|
343
368
|
scopes.pop
|
|
344
369
|
end
|
|
@@ -351,10 +376,8 @@ module Capybara
|
|
|
351
376
|
#
|
|
352
377
|
# @param [String] locator Id or legend of the fieldset
|
|
353
378
|
#
|
|
354
|
-
def within_fieldset(locator)
|
|
355
|
-
within
|
|
356
|
-
yield
|
|
357
|
-
end
|
|
379
|
+
def within_fieldset(locator, &block)
|
|
380
|
+
within(:fieldset, locator, &block)
|
|
358
381
|
end
|
|
359
382
|
|
|
360
383
|
##
|
|
@@ -363,26 +386,24 @@ module Capybara
|
|
|
363
386
|
#
|
|
364
387
|
# @param [String] locator Id or caption of the table
|
|
365
388
|
#
|
|
366
|
-
def within_table(locator)
|
|
367
|
-
within
|
|
368
|
-
yield
|
|
369
|
-
end
|
|
389
|
+
def within_table(locator, &block)
|
|
390
|
+
within(:table, locator, &block)
|
|
370
391
|
end
|
|
371
392
|
|
|
372
393
|
##
|
|
373
394
|
#
|
|
374
|
-
# Switch to the given frame
|
|
395
|
+
# Switch to the given frame.
|
|
375
396
|
#
|
|
376
397
|
# If you use this method you are responsible for making sure you switch back to the parent frame when done in the frame changed to.
|
|
377
|
-
#
|
|
398
|
+
# {#within_frame} is preferred over this method and should be used when possible.
|
|
378
399
|
# May not be supported by all drivers.
|
|
379
400
|
#
|
|
380
401
|
# @overload switch_to_frame(element)
|
|
381
|
-
# @param [Capybara::Node::Element]
|
|
382
|
-
# @overload switch_to_frame(
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
#
|
|
402
|
+
# @param [Capybara::Node::Element] element iframe/frame element to switch to
|
|
403
|
+
# @overload switch_to_frame(location)
|
|
404
|
+
# @param [Symbol] location relative location of the frame to switch to
|
|
405
|
+
# * :parent - the parent frame
|
|
406
|
+
# * :top - the top level document
|
|
386
407
|
#
|
|
387
408
|
def switch_to_frame(frame)
|
|
388
409
|
case frame
|
|
@@ -390,18 +411,25 @@ module Capybara
|
|
|
390
411
|
driver.switch_to_frame(frame)
|
|
391
412
|
scopes.push(:frame)
|
|
392
413
|
when :parent
|
|
393
|
-
|
|
394
|
-
|
|
414
|
+
if scopes.last != :frame
|
|
415
|
+
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's " \
|
|
416
|
+
'`within` block.'
|
|
417
|
+
end
|
|
395
418
|
scopes.pop
|
|
396
419
|
driver.switch_to_frame(:parent)
|
|
397
420
|
when :top
|
|
398
421
|
idx = scopes.index(:frame)
|
|
422
|
+
top_level_scopes = [:frame, nil]
|
|
399
423
|
if idx
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
424
|
+
if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
|
|
425
|
+
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's " \
|
|
426
|
+
'`within` block.'
|
|
427
|
+
end
|
|
428
|
+
scopes.slice!(idx..)
|
|
403
429
|
driver.switch_to_frame(:top)
|
|
404
430
|
end
|
|
431
|
+
else
|
|
432
|
+
raise ArgumentError, 'You must provide a frame element, :parent, or :top when calling switch_to_frame'
|
|
405
433
|
end
|
|
406
434
|
end
|
|
407
435
|
|
|
@@ -412,35 +440,17 @@ module Capybara
|
|
|
412
440
|
#
|
|
413
441
|
# @overload within_frame(element)
|
|
414
442
|
# @param [Capybara::Node::Element] frame element
|
|
415
|
-
# @overload within_frame([kind = :frame], locator, options
|
|
416
|
-
# @param [
|
|
443
|
+
# @overload within_frame([kind = :frame], locator, **options)
|
|
444
|
+
# @param [Symbol] kind Optional selector type (:frame, :css, :xpath, etc.) - Defaults to :frame
|
|
417
445
|
# @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
|
|
418
446
|
# @overload within_frame(index)
|
|
419
447
|
# @param [Integer] index index of a frame (0 based)
|
|
420
|
-
def within_frame(*args)
|
|
421
|
-
|
|
422
|
-
|
|
448
|
+
def within_frame(*args, **kw_args)
|
|
449
|
+
switch_to_frame(_find_frame(*args, **kw_args))
|
|
423
450
|
begin
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
ensure
|
|
428
|
-
switch_to_frame(:parent)
|
|
429
|
-
end
|
|
430
|
-
rescue Capybara::NotSupportedByDriverError
|
|
431
|
-
# Support older driver frame API for now
|
|
432
|
-
if driver.respond_to?(:within_frame)
|
|
433
|
-
begin
|
|
434
|
-
scopes.push(:frame)
|
|
435
|
-
driver.within_frame(frame) do
|
|
436
|
-
yield
|
|
437
|
-
end
|
|
438
|
-
ensure
|
|
439
|
-
scopes.pop
|
|
440
|
-
end
|
|
441
|
-
else
|
|
442
|
-
raise
|
|
443
|
-
end
|
|
451
|
+
yield if block_given?
|
|
452
|
+
ensure
|
|
453
|
+
switch_to_frame(:parent)
|
|
444
454
|
end
|
|
445
455
|
end
|
|
446
456
|
|
|
@@ -465,22 +475,28 @@ module Capybara
|
|
|
465
475
|
end
|
|
466
476
|
|
|
467
477
|
##
|
|
468
|
-
# Open new window.
|
|
469
|
-
#
|
|
478
|
+
# Open a new window.
|
|
479
|
+
# The current window doesn't change as the result of this call.
|
|
470
480
|
# It should be switched to explicitly.
|
|
471
481
|
#
|
|
472
482
|
# @return [Capybara::Window] window that has been opened
|
|
473
483
|
#
|
|
474
|
-
def open_new_window
|
|
484
|
+
def open_new_window(kind = :tab)
|
|
475
485
|
window_opened_by do
|
|
476
|
-
driver.open_new_window
|
|
486
|
+
if driver.method(:open_new_window).arity.zero?
|
|
487
|
+
driver.open_new_window
|
|
488
|
+
else
|
|
489
|
+
driver.open_new_window(kind)
|
|
490
|
+
end
|
|
477
491
|
end
|
|
478
492
|
end
|
|
479
493
|
|
|
480
494
|
##
|
|
495
|
+
# Switch to the given window.
|
|
496
|
+
#
|
|
481
497
|
# @overload switch_to_window(&block)
|
|
482
498
|
# Switches to the first window for which given block returns a value other than false or nil.
|
|
483
|
-
# If window that matches block can't be found, the window will be switched back and
|
|
499
|
+
# If window that matches block can't be found, the window will be switched back and {Capybara::WindowError} will be raised.
|
|
484
500
|
# @example
|
|
485
501
|
# window = switch_to_window { title == 'Page title' }
|
|
486
502
|
# @raise [Capybara::WindowError] if no window matches given block
|
|
@@ -489,24 +505,20 @@ module Capybara
|
|
|
489
505
|
# @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
|
490
506
|
#
|
|
491
507
|
# @return [Capybara::Window] window that has been switched to
|
|
492
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
|
493
|
-
#
|
|
508
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within} or
|
|
509
|
+
# {#within_frame} methods
|
|
494
510
|
# @raise [ArgumentError] if both or neither arguments were provided
|
|
495
511
|
#
|
|
496
|
-
def switch_to_window(window = nil, options
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
raise
|
|
502
|
-
|
|
503
|
-
raise ArgumentError, "`switch_to_window`: either window or block should be provided"
|
|
504
|
-
elsif !scopes.last.nil?
|
|
505
|
-
raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
|
|
506
|
-
"`within` or `within_frame` blocks."
|
|
512
|
+
def switch_to_window(window = nil, **options, &window_locator)
|
|
513
|
+
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
|
|
514
|
+
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
|
|
515
|
+
|
|
516
|
+
unless scopes.last.nil?
|
|
517
|
+
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from ' \
|
|
518
|
+
'`within` or `within_frame` blocks.'
|
|
507
519
|
end
|
|
508
520
|
|
|
509
|
-
_switch_to_window(window, options, &window_locator)
|
|
521
|
+
_switch_to_window(window, **options, &window_locator)
|
|
510
522
|
end
|
|
511
523
|
|
|
512
524
|
##
|
|
@@ -514,63 +526,42 @@ module Capybara
|
|
|
514
526
|
#
|
|
515
527
|
# 1. Switches to the given window (it can be located by window instance/lambda/string).
|
|
516
528
|
# 2. Executes the given block (within window located at previous step).
|
|
517
|
-
# 3. Switches back (this step will be invoked even if exception
|
|
529
|
+
# 3. Switches back (this step will be invoked even if an exception occurs at the second step).
|
|
518
530
|
#
|
|
519
531
|
# @overload within_window(window) { do_something }
|
|
520
|
-
# @param window [Capybara::Window] instance of
|
|
532
|
+
# @param window [Capybara::Window] instance of {Capybara::Window} class
|
|
521
533
|
# that will be switched to
|
|
522
534
|
# @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
|
523
535
|
# @overload within_window(proc_or_lambda) { do_something }
|
|
524
|
-
# @param lambda [Proc]
|
|
536
|
+
# @param lambda [Proc] First window for which lambda
|
|
525
537
|
# returns a value other than false or nil will be switched to.
|
|
526
538
|
# @example
|
|
527
539
|
# within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
|
|
528
540
|
# @raise [Capybara::WindowError] if no window matching lambda was found
|
|
529
|
-
# @overload within_window(string) { do_something }
|
|
530
|
-
# @deprecated Pass window or lambda instead
|
|
531
|
-
# @param [String] handle, name, url or title of the window
|
|
532
541
|
#
|
|
533
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
|
542
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within_frame} method
|
|
534
543
|
# @return value returned by the block
|
|
535
544
|
#
|
|
536
|
-
def within_window(
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
ensure
|
|
548
|
-
scopes.pop
|
|
549
|
-
end
|
|
550
|
-
elsif window_or_handle.is_a?(Proc)
|
|
551
|
-
original = current_window
|
|
552
|
-
scopes << nil
|
|
553
|
-
begin
|
|
554
|
-
_switch_to_window { window_or_handle.call }
|
|
555
|
-
begin
|
|
556
|
-
yield
|
|
557
|
-
ensure
|
|
558
|
-
_switch_to_window(original)
|
|
559
|
-
end
|
|
560
|
-
ensure
|
|
561
|
-
scopes.pop
|
|
545
|
+
def within_window(window_or_proc)
|
|
546
|
+
original = current_window
|
|
547
|
+
scopes << nil
|
|
548
|
+
begin
|
|
549
|
+
case window_or_proc
|
|
550
|
+
when Capybara::Window
|
|
551
|
+
_switch_to_window(window_or_proc) unless original == window_or_proc
|
|
552
|
+
when Proc
|
|
553
|
+
_switch_to_window { window_or_proc.call }
|
|
554
|
+
else
|
|
555
|
+
raise ArgumentError, '`#within_window` requires a `Capybara::Window` instance or a lambda'
|
|
562
556
|
end
|
|
563
|
-
|
|
564
|
-
offending_line = caller.first
|
|
565
|
-
file_line = offending_line.match(/^(.+?):(\d+)/)[0]
|
|
566
|
-
warn "DEPRECATION WARNING: Passing string argument to #within_window is deprecated. "\
|
|
567
|
-
"Pass window object or lambda. (called from #{file_line})"
|
|
557
|
+
|
|
568
558
|
begin
|
|
569
|
-
|
|
570
|
-
driver.within_window(window_or_handle) { yield }
|
|
559
|
+
yield if block_given?
|
|
571
560
|
ensure
|
|
572
|
-
|
|
561
|
+
_switch_to_window(original) unless original == window_or_proc
|
|
573
562
|
end
|
|
563
|
+
ensure
|
|
564
|
+
scopes.pop
|
|
574
565
|
end
|
|
575
566
|
end
|
|
576
567
|
|
|
@@ -578,23 +569,23 @@ module Capybara
|
|
|
578
569
|
# Get the window that has been opened by the passed block.
|
|
579
570
|
# It will wait for it to be opened (in the same way as other Capybara methods wait).
|
|
580
571
|
# It's better to use this method than `windows.last`
|
|
581
|
-
# {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
|
|
572
|
+
# {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}.
|
|
582
573
|
#
|
|
583
|
-
# @
|
|
584
|
-
#
|
|
585
|
-
#
|
|
586
|
-
#
|
|
587
|
-
#
|
|
574
|
+
# @overload window_opened_by(**options, &block)
|
|
575
|
+
# @param options [Hash]
|
|
576
|
+
# @option options [Numeric] :wait maximum wait time. Defaults to {Capybara.configure default_max_wait_time}
|
|
577
|
+
# @return [Capybara::Window] the window that has been opened within a block
|
|
578
|
+
# @raise [Capybara::WindowError] if block passed to window hasn't opened window
|
|
579
|
+
# or opened more than one window
|
|
588
580
|
#
|
|
589
|
-
def window_opened_by(options
|
|
581
|
+
def window_opened_by(**options)
|
|
590
582
|
old_handles = driver.window_handles
|
|
591
|
-
|
|
583
|
+
yield
|
|
592
584
|
|
|
593
|
-
|
|
594
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
|
585
|
+
synchronize_windows(options) do
|
|
595
586
|
opened_handles = (driver.window_handles - old_handles)
|
|
596
587
|
if opened_handles.size != 1
|
|
597
|
-
raise Capybara::WindowError,
|
|
588
|
+
raise Capybara::WindowError, 'block passed to #window_opened_by ' \
|
|
598
589
|
"opened #{opened_handles.size} windows instead of 1"
|
|
599
590
|
end
|
|
600
591
|
Window.new(self, opened_handles.first)
|
|
@@ -604,39 +595,30 @@ module Capybara
|
|
|
604
595
|
##
|
|
605
596
|
#
|
|
606
597
|
# Execute the given script, not returning a result. This is useful for scripts that return
|
|
607
|
-
# complex objects, such as jQuery statements.
|
|
608
|
-
#
|
|
598
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
|
599
|
+
# {#evaluate_script} whenever possible.
|
|
609
600
|
#
|
|
610
601
|
# @param [String] script A string of JavaScript to execute
|
|
611
|
-
# @param args Optional arguments that will be passed to the script.
|
|
602
|
+
# @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
|
|
612
603
|
#
|
|
613
604
|
def execute_script(script, *args)
|
|
614
605
|
@touched = true
|
|
615
|
-
|
|
616
|
-
driver.execute_script(script)
|
|
617
|
-
else
|
|
618
|
-
raise Capybara::NotSupportedByDriverError, "The current driver does not support execute_script arguments" if driver.method(:execute_script).arity == 1
|
|
619
|
-
driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
|
|
620
|
-
end
|
|
606
|
+
driver.execute_script(script, *driver_args(args))
|
|
621
607
|
end
|
|
622
608
|
|
|
623
609
|
##
|
|
624
610
|
#
|
|
625
611
|
# Evaluate the given JavaScript and return the result. Be careful when using this with
|
|
626
|
-
# scripts that return complex objects, such as jQuery statements.
|
|
612
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
|
627
613
|
# be a better alternative.
|
|
628
614
|
#
|
|
629
615
|
# @param [String] script A string of JavaScript to evaluate
|
|
616
|
+
# @param args Optional arguments that will be passed to the script
|
|
630
617
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
631
618
|
#
|
|
632
619
|
def evaluate_script(script, *args)
|
|
633
620
|
@touched = true
|
|
634
|
-
result =
|
|
635
|
-
driver.evaluate_script(script)
|
|
636
|
-
else
|
|
637
|
-
raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_script arguments" if driver.method(:evaluate_script).arity == 1
|
|
638
|
-
driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
|
|
639
|
-
end
|
|
621
|
+
result = driver.evaluate_script(script.strip, *driver_args(args))
|
|
640
622
|
element_script_result(result)
|
|
641
623
|
end
|
|
642
624
|
|
|
@@ -645,16 +627,12 @@ module Capybara
|
|
|
645
627
|
# Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
|
|
646
628
|
#
|
|
647
629
|
# @param [String] script A string of JavaScript to evaluate
|
|
630
|
+
# @param args Optional arguments that will be passed to the script
|
|
648
631
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
649
632
|
#
|
|
650
633
|
def evaluate_async_script(script, *args)
|
|
651
634
|
@touched = true
|
|
652
|
-
result =
|
|
653
|
-
driver.evaluate_async_script(script)
|
|
654
|
-
else
|
|
655
|
-
raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_async_script arguments" if driver.method(:evaluate_async_script).arity == 1
|
|
656
|
-
driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
|
|
657
|
-
end
|
|
635
|
+
result = driver.evaluate_async_script(script, *driver_args(args))
|
|
658
636
|
element_script_result(result)
|
|
659
637
|
end
|
|
660
638
|
|
|
@@ -663,24 +641,23 @@ module Capybara
|
|
|
663
641
|
# Execute the block, accepting a alert.
|
|
664
642
|
#
|
|
665
643
|
# @!macro modal_params
|
|
666
|
-
# Expects a block whose actions will trigger the display modal to appear
|
|
644
|
+
# Expects a block whose actions will trigger the display modal to appear.
|
|
667
645
|
# @example
|
|
668
646
|
# $0 do
|
|
669
647
|
# click_link('link that triggers appearance of system modal')
|
|
670
648
|
# end
|
|
671
|
-
# @overload $0(text, options
|
|
672
|
-
# @param text [String, Regexp] Text or regex to match against the text in the modal.
|
|
673
|
-
# @option options [Numeric] :wait
|
|
649
|
+
# @overload $0(text, **options, &blk)
|
|
650
|
+
# @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched.
|
|
651
|
+
# @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
|
|
674
652
|
# @yield Block whose actions will trigger the system modal
|
|
675
|
-
# @overload $0(options
|
|
676
|
-
# @option options [Numeric] :wait
|
|
653
|
+
# @overload $0(**options, &blk)
|
|
654
|
+
# @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
|
|
677
655
|
# @yield Block whose actions will trigger the system modal
|
|
678
656
|
# @return [String] the message shown in the modal
|
|
679
657
|
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
|
|
680
658
|
#
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
accept_modal(:alert, text_or_options, options, &blk)
|
|
659
|
+
def accept_alert(text = nil, **options, &blk)
|
|
660
|
+
accept_modal(:alert, text, options, &blk)
|
|
684
661
|
end
|
|
685
662
|
|
|
686
663
|
##
|
|
@@ -689,8 +666,8 @@ module Capybara
|
|
|
689
666
|
#
|
|
690
667
|
# @macro modal_params
|
|
691
668
|
#
|
|
692
|
-
def accept_confirm(
|
|
693
|
-
accept_modal(:confirm,
|
|
669
|
+
def accept_confirm(text = nil, **options, &blk)
|
|
670
|
+
accept_modal(:confirm, text, options, &blk)
|
|
694
671
|
end
|
|
695
672
|
|
|
696
673
|
##
|
|
@@ -699,8 +676,8 @@ module Capybara
|
|
|
699
676
|
#
|
|
700
677
|
# @macro modal_params
|
|
701
678
|
#
|
|
702
|
-
def dismiss_confirm(
|
|
703
|
-
dismiss_modal(:confirm,
|
|
679
|
+
def dismiss_confirm(text = nil, **options, &blk)
|
|
680
|
+
dismiss_modal(:confirm, text, options, &blk)
|
|
704
681
|
end
|
|
705
682
|
|
|
706
683
|
##
|
|
@@ -710,8 +687,8 @@ module Capybara
|
|
|
710
687
|
# @macro modal_params
|
|
711
688
|
# @option options [String] :with Response to provide to the prompt
|
|
712
689
|
#
|
|
713
|
-
def accept_prompt(
|
|
714
|
-
accept_modal(:prompt,
|
|
690
|
+
def accept_prompt(text = nil, **options, &blk)
|
|
691
|
+
accept_modal(:prompt, text, options, &blk)
|
|
715
692
|
end
|
|
716
693
|
|
|
717
694
|
##
|
|
@@ -720,82 +697,70 @@ module Capybara
|
|
|
720
697
|
#
|
|
721
698
|
# @macro modal_params
|
|
722
699
|
#
|
|
723
|
-
def dismiss_prompt(
|
|
724
|
-
dismiss_modal(:prompt,
|
|
700
|
+
def dismiss_prompt(text = nil, **options, &blk)
|
|
701
|
+
dismiss_modal(:prompt, text, options, &blk)
|
|
725
702
|
end
|
|
726
703
|
|
|
727
704
|
##
|
|
728
705
|
#
|
|
729
|
-
# Save a snapshot of the page. If
|
|
730
|
-
#
|
|
706
|
+
# Save a snapshot of the page. If {Capybara.configure asset_host} is set it will inject `base` tag
|
|
707
|
+
# pointing to {Capybara.configure asset_host}.
|
|
731
708
|
#
|
|
732
|
-
# If invoked without arguments it will save file to
|
|
733
|
-
#
|
|
734
|
-
#
|
|
735
|
-
# the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
|
|
736
|
-
# relative to Dir.pwd
|
|
709
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
710
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
711
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
737
712
|
#
|
|
738
713
|
# @param [String] path the path to where it should be saved
|
|
739
714
|
# @return [String] the path to which the file was saved
|
|
740
715
|
#
|
|
741
716
|
def save_page(path = nil)
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
717
|
+
prepare_path(path, 'html').tap do |p_path|
|
|
718
|
+
File.write(p_path, Capybara::Helpers.inject_asset_host(body, host: config.asset_host), mode: 'wb')
|
|
719
|
+
end
|
|
745
720
|
end
|
|
746
721
|
|
|
747
722
|
##
|
|
748
723
|
#
|
|
749
724
|
# Save a snapshot of the page and open it in a browser for inspection.
|
|
750
725
|
#
|
|
751
|
-
# If invoked without arguments it will save file to
|
|
752
|
-
#
|
|
753
|
-
#
|
|
754
|
-
# the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
|
|
755
|
-
# relative to Dir.pwd
|
|
726
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
727
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
728
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
756
729
|
#
|
|
757
730
|
# @param [String] path the path to where it should be saved
|
|
758
731
|
#
|
|
759
732
|
def save_and_open_page(path = nil)
|
|
760
|
-
path
|
|
761
|
-
open_file(path)
|
|
733
|
+
save_page(path).tap { |s_path| open_file(s_path) }
|
|
762
734
|
end
|
|
763
735
|
|
|
764
736
|
##
|
|
765
737
|
#
|
|
766
738
|
# Save a screenshot of page.
|
|
767
739
|
#
|
|
768
|
-
# If invoked without arguments it will save file to
|
|
769
|
-
#
|
|
770
|
-
#
|
|
771
|
-
# the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
|
|
772
|
-
# relative to Dir.pwd
|
|
740
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
741
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
742
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
773
743
|
#
|
|
774
744
|
# @param [String] path the path to where it should be saved
|
|
775
745
|
# @param [Hash] options a customizable set of options
|
|
776
746
|
# @return [String] the path to which the file was saved
|
|
777
|
-
def save_screenshot(path = nil, options
|
|
778
|
-
|
|
779
|
-
driver.save_screenshot(path, options)
|
|
780
|
-
path
|
|
747
|
+
def save_screenshot(path = nil, **options)
|
|
748
|
+
prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, **options) }
|
|
781
749
|
end
|
|
782
750
|
|
|
783
751
|
##
|
|
784
752
|
#
|
|
785
753
|
# Save a screenshot of the page and open it for inspection.
|
|
786
754
|
#
|
|
787
|
-
# If invoked without arguments it will save file to
|
|
788
|
-
#
|
|
789
|
-
#
|
|
790
|
-
# the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
|
|
791
|
-
# relative to Dir.pwd
|
|
755
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
756
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
757
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
792
758
|
#
|
|
793
759
|
# @param [String] path the path to where it should be saved
|
|
794
760
|
# @param [Hash] options a customizable set of options
|
|
795
761
|
#
|
|
796
|
-
def save_and_open_screenshot(path = nil, options
|
|
797
|
-
|
|
798
|
-
open_file(path)
|
|
762
|
+
def save_and_open_screenshot(path = nil, **options)
|
|
763
|
+
save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
|
|
799
764
|
end
|
|
800
765
|
|
|
801
766
|
def document
|
|
@@ -803,16 +768,20 @@ module Capybara
|
|
|
803
768
|
end
|
|
804
769
|
|
|
805
770
|
NODE_METHODS.each do |method|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
771
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
772
|
+
def #{method}(...)
|
|
773
|
+
@touched = true
|
|
774
|
+
current_scope.#{method}(...)
|
|
775
|
+
end
|
|
776
|
+
METHOD
|
|
810
777
|
end
|
|
811
778
|
|
|
812
779
|
DOCUMENT_METHODS.each do |method|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
780
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
781
|
+
def #{method}(...)
|
|
782
|
+
document.#{method}(...)
|
|
783
|
+
end
|
|
784
|
+
METHOD
|
|
816
785
|
end
|
|
817
786
|
|
|
818
787
|
def inspect
|
|
@@ -821,15 +790,14 @@ module Capybara
|
|
|
821
790
|
|
|
822
791
|
def current_scope
|
|
823
792
|
scope = scopes.last
|
|
824
|
-
|
|
825
|
-
scope
|
|
793
|
+
[nil, :frame].include?(scope) ? document : scope
|
|
826
794
|
end
|
|
827
795
|
|
|
828
796
|
##
|
|
829
797
|
#
|
|
830
|
-
# Yield a block using a specific wait time
|
|
798
|
+
# Yield a block using a specific maximum wait time.
|
|
831
799
|
#
|
|
832
|
-
def using_wait_time(seconds)
|
|
800
|
+
def using_wait_time(seconds, &block)
|
|
833
801
|
if Capybara.threadsafe
|
|
834
802
|
begin
|
|
835
803
|
previous_wait_time = config.default_max_wait_time
|
|
@@ -839,17 +807,18 @@ module Capybara
|
|
|
839
807
|
config.default_max_wait_time = previous_wait_time
|
|
840
808
|
end
|
|
841
809
|
else
|
|
842
|
-
Capybara.using_wait_time(seconds)
|
|
810
|
+
Capybara.using_wait_time(seconds, &block)
|
|
843
811
|
end
|
|
844
812
|
end
|
|
845
813
|
|
|
846
814
|
##
|
|
847
815
|
#
|
|
848
|
-
#
|
|
849
|
-
#
|
|
816
|
+
# Accepts a block to set the configuration options if {Capybara.configure threadsafe} is `true`. Note that some options only have an effect
|
|
817
|
+
# if set at initialization time, so look at the configuration block that can be passed to the initializer too.
|
|
850
818
|
#
|
|
851
819
|
def configure
|
|
852
|
-
raise
|
|
820
|
+
raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
|
|
821
|
+
|
|
853
822
|
yield config
|
|
854
823
|
end
|
|
855
824
|
|
|
@@ -864,48 +833,48 @@ module Capybara
|
|
|
864
833
|
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
|
865
834
|
end
|
|
866
835
|
end
|
|
836
|
+
|
|
837
|
+
def server_url
|
|
838
|
+
@server&.base_url
|
|
839
|
+
end
|
|
840
|
+
|
|
867
841
|
private
|
|
868
842
|
|
|
869
|
-
@@instance_created = false
|
|
843
|
+
@@instance_created = false # rubocop:disable Style/ClassVars
|
|
844
|
+
|
|
845
|
+
def driver_args(args)
|
|
846
|
+
args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
|
|
847
|
+
end
|
|
870
848
|
|
|
871
849
|
def accept_modal(type, text_or_options, options, &blk)
|
|
872
|
-
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
|
850
|
+
driver.accept_modal(type, **modal_options(text_or_options, **options), &blk)
|
|
873
851
|
end
|
|
874
852
|
|
|
875
853
|
def dismiss_modal(type, text_or_options, options, &blk)
|
|
876
|
-
driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
|
|
854
|
+
driver.dismiss_modal(type, **modal_options(text_or_options, **options), &blk)
|
|
877
855
|
end
|
|
878
856
|
|
|
879
|
-
def modal_options(
|
|
880
|
-
|
|
881
|
-
options[:text] ||= text_or_options unless text_or_options.nil?
|
|
857
|
+
def modal_options(text = nil, **options)
|
|
858
|
+
options[:text] ||= text unless text.nil?
|
|
882
859
|
options[:wait] ||= config.default_max_wait_time
|
|
883
860
|
options
|
|
884
861
|
end
|
|
885
862
|
|
|
886
|
-
|
|
887
863
|
def open_file(path)
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
warn "File saved to #{path}."
|
|
893
|
-
warn "Please install the launchy gem to open the file automatically."
|
|
894
|
-
end
|
|
864
|
+
require 'launchy'
|
|
865
|
+
Launchy.open(path)
|
|
866
|
+
rescue LoadError
|
|
867
|
+
warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
|
|
895
868
|
end
|
|
896
869
|
|
|
897
870
|
def prepare_path(path, extension)
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
else
|
|
901
|
-
path = File.expand_path(default_fn(extension), config.save_and_open_page_path) if path.nil?
|
|
871
|
+
File.expand_path(path || default_fn(extension), config.save_path).tap do |p_path|
|
|
872
|
+
FileUtils.mkdir_p(File.dirname(p_path))
|
|
902
873
|
end
|
|
903
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
904
|
-
path
|
|
905
874
|
end
|
|
906
875
|
|
|
907
876
|
def default_fn(extension)
|
|
908
|
-
timestamp = Time.new.strftime(
|
|
877
|
+
timestamp = Time.new.strftime('%Y%m%d%H%M%S')
|
|
909
878
|
"capybara-#{timestamp}#{rand(10**10)}.#{extension}"
|
|
910
879
|
end
|
|
911
880
|
|
|
@@ -916,9 +885,9 @@ module Capybara
|
|
|
916
885
|
def element_script_result(arg)
|
|
917
886
|
case arg
|
|
918
887
|
when Array
|
|
919
|
-
arg.map { |
|
|
888
|
+
arg.map { |subarg| element_script_result(subarg) }
|
|
920
889
|
when Hash
|
|
921
|
-
arg.
|
|
890
|
+
arg.transform_values! { |value| element_script_result(value) }
|
|
922
891
|
when Capybara::Driver::Node
|
|
923
892
|
Capybara::Node::Element.new(self, arg, nil, nil)
|
|
924
893
|
else
|
|
@@ -926,54 +895,57 @@ module Capybara
|
|
|
926
895
|
end
|
|
927
896
|
end
|
|
928
897
|
|
|
929
|
-
def
|
|
930
|
-
|
|
931
|
-
case args[0]
|
|
932
|
-
when Capybara::Node::Element
|
|
933
|
-
args[0]
|
|
934
|
-
when String, Hash
|
|
935
|
-
find(:frame, *args)
|
|
936
|
-
when Symbol
|
|
937
|
-
find(*args)
|
|
938
|
-
when Integer
|
|
939
|
-
idx = args[0]
|
|
940
|
-
all(:frame, minimum: idx+1)[idx]
|
|
941
|
-
else
|
|
942
|
-
raise TypeError
|
|
943
|
-
end
|
|
944
|
-
end
|
|
898
|
+
def adjust_server_port(uri)
|
|
899
|
+
uri.port ||= @server.port if @server && config.always_include_port
|
|
945
900
|
end
|
|
946
901
|
|
|
947
|
-
def
|
|
948
|
-
|
|
902
|
+
def _find_frame(*args, **kw_args)
|
|
903
|
+
case args[0]
|
|
904
|
+
when Capybara::Node::Element
|
|
905
|
+
args[0]
|
|
906
|
+
when String, nil
|
|
907
|
+
find(:frame, *args, **kw_args)
|
|
908
|
+
when Symbol
|
|
909
|
+
find(*args, **kw_args)
|
|
910
|
+
when Integer
|
|
911
|
+
idx = args[0]
|
|
912
|
+
all(:frame, minimum: idx + 1)[idx]
|
|
913
|
+
else
|
|
914
|
+
raise TypeError
|
|
915
|
+
end
|
|
916
|
+
end
|
|
949
917
|
|
|
950
|
-
|
|
951
|
-
raise Capybara::ScopeError,
|
|
918
|
+
def _switch_to_window(window = nil, **options, &window_locator)
|
|
919
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within_frame` block' if scopes.include?(:frame)
|
|
920
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within` block' unless scopes.last.nil?
|
|
952
921
|
|
|
953
922
|
if window
|
|
954
923
|
driver.switch_to_window(window.handle)
|
|
955
924
|
window
|
|
956
925
|
else
|
|
957
|
-
|
|
958
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
|
926
|
+
synchronize_windows(options) do
|
|
959
927
|
original_window_handle = driver.current_window_handle
|
|
960
928
|
begin
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if yield
|
|
964
|
-
return Window.new(self, handle)
|
|
965
|
-
end
|
|
966
|
-
end
|
|
967
|
-
rescue => e
|
|
929
|
+
_switch_to_window_by_locator(&window_locator)
|
|
930
|
+
rescue StandardError
|
|
968
931
|
driver.switch_to_window(original_window_handle)
|
|
969
|
-
raise
|
|
970
|
-
else
|
|
971
|
-
driver.switch_to_window(original_window_handle)
|
|
972
|
-
raise Capybara::WindowError, "Could not find a window matching block/lambda"
|
|
932
|
+
raise
|
|
973
933
|
end
|
|
974
934
|
end
|
|
975
935
|
end
|
|
976
936
|
end
|
|
977
937
|
|
|
938
|
+
def _switch_to_window_by_locator
|
|
939
|
+
driver.window_handles.each do |handle|
|
|
940
|
+
driver.switch_to_window handle
|
|
941
|
+
return Window.new(self, handle) if yield
|
|
942
|
+
end
|
|
943
|
+
raise Capybara::WindowError, 'Could not find a window matching block/lambda'
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
def synchronize_windows(options, &block)
|
|
947
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
|
948
|
+
document.synchronize(wait_time, errors: [Capybara::WindowError], &block)
|
|
949
|
+
end
|
|
978
950
|
end
|
|
979
951
|
end
|