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
|
@@ -1,48 +1,94 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'English'
|
|
3
5
|
|
|
4
6
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
7
|
+
include Capybara::Selenium::Find
|
|
5
8
|
|
|
6
9
|
DEFAULT_OPTIONS = {
|
|
7
|
-
:
|
|
8
|
-
clear_local_storage:
|
|
9
|
-
clear_session_storage:
|
|
10
|
-
}
|
|
11
|
-
SPECIAL_OPTIONS = [
|
|
10
|
+
browser: :firefox,
|
|
11
|
+
clear_local_storage: nil,
|
|
12
|
+
clear_session_storage: nil
|
|
13
|
+
}.freeze
|
|
14
|
+
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout native_displayed].freeze
|
|
15
|
+
CAPS_VERSION = Gem::Requirement.new('< 4.8.0')
|
|
12
16
|
|
|
13
17
|
attr_reader :app, :options
|
|
14
18
|
|
|
19
|
+
class << self
|
|
20
|
+
attr_reader :selenium_webdriver_version
|
|
21
|
+
|
|
22
|
+
def load_selenium
|
|
23
|
+
require 'selenium-webdriver'
|
|
24
|
+
require 'capybara/selenium/patches/atoms'
|
|
25
|
+
require 'capybara/selenium/patches/is_displayed'
|
|
26
|
+
|
|
27
|
+
# Look up the version of `selenium-webdriver` to
|
|
28
|
+
# see if it's a version we support.
|
|
29
|
+
#
|
|
30
|
+
# By default, we use Gem.loaded_specs to determine
|
|
31
|
+
# the version number. However, in some cases, such
|
|
32
|
+
# as when loading `selenium-webdriver` outside of
|
|
33
|
+
# Rubygems, we fall back to referencing
|
|
34
|
+
# Selenium::WebDriver::VERSION. Ideally we'd
|
|
35
|
+
# use the constant in all cases, but earlier versions
|
|
36
|
+
# of `selenium-webdriver` didn't provide the constant.
|
|
37
|
+
@selenium_webdriver_version =
|
|
38
|
+
if Gem.loaded_specs['selenium-webdriver']
|
|
39
|
+
Gem.loaded_specs['selenium-webdriver'].version
|
|
40
|
+
else
|
|
41
|
+
Gem::Version.new(Selenium::WebDriver::VERSION)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
unless Gem::Requirement.new('>= 4.8').satisfied_by? @selenium_webdriver_version
|
|
45
|
+
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade to 4.8+."
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@selenium_webdriver_version
|
|
49
|
+
rescue LoadError => e
|
|
50
|
+
raise e unless e.message.include?('selenium-webdriver')
|
|
51
|
+
|
|
52
|
+
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :specializations
|
|
56
|
+
|
|
57
|
+
def register_specialization(browser_name, specialization)
|
|
58
|
+
@specializations ||= {}
|
|
59
|
+
@specializations[browser_name] = specialization
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
15
63
|
def browser
|
|
16
64
|
unless @browser
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
options[:
|
|
65
|
+
options[:http_client] ||= begin
|
|
66
|
+
require 'capybara/selenium/patches/persistent_client'
|
|
67
|
+
if options[:timeout]
|
|
68
|
+
::Capybara::Selenium::PersistentClient.new(read_timeout: options[:timeout])
|
|
69
|
+
else
|
|
70
|
+
::Capybara::Selenium::PersistentClient.new
|
|
71
|
+
end
|
|
20
72
|
end
|
|
73
|
+
processed_options = options.except(*SPECIAL_OPTIONS)
|
|
21
74
|
|
|
22
|
-
@
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(defined?(Selenium::WebDriver::Remote::W3C::Capabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)))
|
|
27
|
-
main = Process.pid
|
|
28
|
-
at_exit do
|
|
29
|
-
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
|
30
|
-
@exit_status = $!.status if $!.is_a?(SystemExit)
|
|
31
|
-
quit if Process.pid == main
|
|
32
|
-
exit @exit_status if @exit_status # Force exit with stored status
|
|
33
|
-
end
|
|
75
|
+
@browser = Selenium::WebDriver.for(options[:browser], processed_options)
|
|
76
|
+
|
|
77
|
+
specialize_driver
|
|
78
|
+
setup_exit_handler
|
|
34
79
|
end
|
|
35
80
|
@browser
|
|
36
81
|
end
|
|
37
82
|
|
|
38
|
-
def initialize(app, options
|
|
39
|
-
|
|
40
|
-
|
|
83
|
+
def initialize(app, **options)
|
|
84
|
+
super()
|
|
85
|
+
self.class.load_selenium
|
|
41
86
|
@app = app
|
|
42
87
|
@browser = nil
|
|
43
88
|
@exit_status = nil
|
|
44
|
-
@frame_handles = {}
|
|
89
|
+
@frame_handles = Hash.new { |hash, handle| hash[handle] = [] }
|
|
45
90
|
@options = DEFAULT_OPTIONS.merge(options)
|
|
91
|
+
@node_class = ::Capybara::Selenium::Node
|
|
46
92
|
end
|
|
47
93
|
|
|
48
94
|
def visit(path)
|
|
@@ -50,10 +96,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
50
96
|
end
|
|
51
97
|
|
|
52
98
|
def refresh
|
|
53
|
-
|
|
54
|
-
browser.navigate.refresh
|
|
55
|
-
end
|
|
56
|
-
rescue Capybara::ModalNotFound
|
|
99
|
+
browser.navigate.refresh
|
|
57
100
|
end
|
|
58
101
|
|
|
59
102
|
def go_back
|
|
@@ -66,6 +109,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
66
109
|
|
|
67
110
|
def html
|
|
68
111
|
browser.page_source
|
|
112
|
+
rescue Selenium::WebDriver::Error::JavascriptError => e
|
|
113
|
+
raise unless e.message.include?('documentElement is null')
|
|
69
114
|
end
|
|
70
115
|
|
|
71
116
|
def title
|
|
@@ -76,19 +121,11 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
76
121
|
browser.current_url
|
|
77
122
|
end
|
|
78
123
|
|
|
79
|
-
def find_xpath(selector)
|
|
80
|
-
browser.find_elements(:xpath, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def find_css(selector)
|
|
84
|
-
browser.find_elements(:css, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
124
|
def wait?; true; end
|
|
88
125
|
def needs_server?; true; end
|
|
89
126
|
|
|
90
127
|
def execute_script(script, *args)
|
|
91
|
-
browser.execute_script(script, *args
|
|
128
|
+
browser.execute_script(script, *native_args(args))
|
|
92
129
|
end
|
|
93
130
|
|
|
94
131
|
def evaluate_script(script, *args)
|
|
@@ -98,83 +135,69 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
98
135
|
|
|
99
136
|
def evaluate_async_script(script, *args)
|
|
100
137
|
browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
|
|
101
|
-
result = browser.execute_async_script(script, *args
|
|
138
|
+
result = browser.execute_async_script(script, *native_args(args))
|
|
102
139
|
unwrap_script_result(result)
|
|
103
140
|
end
|
|
104
141
|
|
|
105
|
-
def
|
|
106
|
-
|
|
142
|
+
def active_element
|
|
143
|
+
build_node(native_active_element)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def send_keys(*args)
|
|
147
|
+
# Should this call the specialized nodes rather than native???
|
|
148
|
+
native_active_element.send_keys(*args)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def save_screenshot(path, **options)
|
|
152
|
+
browser.save_screenshot(path, **options)
|
|
107
153
|
end
|
|
108
154
|
|
|
109
155
|
def reset!
|
|
110
156
|
# Use instance variable directly so we avoid starting the browser just to reset the session
|
|
111
|
-
|
|
112
|
-
navigated = false
|
|
113
|
-
start_time = Capybara::Helpers.monotonic_time
|
|
114
|
-
begin
|
|
115
|
-
if !navigated
|
|
116
|
-
# Only trigger a navigation if we haven't done it already, otherwise it
|
|
117
|
-
# can trigger an endless series of unload modals
|
|
118
|
-
begin
|
|
119
|
-
@browser.manage.delete_all_cookies
|
|
120
|
-
if options[:clear_session_storage]
|
|
121
|
-
if @browser.respond_to? :session_storage
|
|
122
|
-
@browser.session_storage.clear
|
|
123
|
-
else
|
|
124
|
-
warn "sessionStorage clear requested but is not available for this driver"
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
if options[:clear_local_storage]
|
|
128
|
-
if @browser.respond_to? :local_storage
|
|
129
|
-
@browser.local_storage.clear
|
|
130
|
-
else
|
|
131
|
-
warn "localStorage clear requested but is not available for this driver"
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
rescue Selenium::WebDriver::Error::UnhandledError
|
|
135
|
-
# delete_all_cookies fails when we've previously gone
|
|
136
|
-
# to about:blank, so we rescue this error and do nothing
|
|
137
|
-
# instead.
|
|
138
|
-
end
|
|
139
|
-
@browser.navigate.to("about:blank")
|
|
140
|
-
end
|
|
141
|
-
navigated = true
|
|
157
|
+
return unless @browser
|
|
142
158
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
navigated = false
|
|
160
|
+
timer = Capybara::Helpers.timer(expire_in: 10)
|
|
161
|
+
begin
|
|
162
|
+
# Only trigger a navigation if we haven't done it already, otherwise it
|
|
163
|
+
# can trigger an endless series of unload modals
|
|
164
|
+
reset_browser_state unless navigated
|
|
165
|
+
navigated = true
|
|
166
|
+
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
|
167
|
+
wait_for_empty_page(timer)
|
|
168
|
+
rescue *unhandled_alert_errors
|
|
169
|
+
# This error is thrown if an unhandled alert is on the page
|
|
170
|
+
# Firefox appears to automatically dismiss this alert, chrome does not
|
|
171
|
+
# We'll try to accept it
|
|
172
|
+
accept_unhandled_reset_alert
|
|
173
|
+
# try cleaning up the browser again
|
|
174
|
+
retry
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def frame_obscured_at?(x:, y:)
|
|
179
|
+
frame = @frame_handles[current_window_handle].last
|
|
180
|
+
return false unless frame
|
|
181
|
+
|
|
182
|
+
switch_to_frame(:parent)
|
|
183
|
+
begin
|
|
184
|
+
frame.base.obscured?(x: x, y: y)
|
|
185
|
+
ensure
|
|
186
|
+
switch_to_frame(frame)
|
|
161
187
|
end
|
|
162
188
|
end
|
|
163
189
|
|
|
164
190
|
def switch_to_frame(frame)
|
|
191
|
+
handles = @frame_handles[current_window_handle]
|
|
165
192
|
case frame
|
|
166
193
|
when :top
|
|
167
|
-
|
|
194
|
+
handles.clear
|
|
168
195
|
browser.switch_to.default_content
|
|
169
196
|
when :parent
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@frame_handles[browser.window_handle].pop
|
|
173
|
-
browser.switch_to.default_content
|
|
174
|
-
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
|
|
197
|
+
handles.pop
|
|
198
|
+
browser.switch_to.parent_frame
|
|
175
199
|
else
|
|
176
|
-
|
|
177
|
-
@frame_handles[browser.window_handle] << frame.native
|
|
200
|
+
handles << frame
|
|
178
201
|
browser.switch_to.frame(frame.native)
|
|
179
202
|
end
|
|
180
203
|
end
|
|
@@ -192,12 +215,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
192
215
|
|
|
193
216
|
def resize_window_to(handle, width, height)
|
|
194
217
|
within_given_window(handle) do
|
|
195
|
-
|
|
196
|
-
if marionette? && (window_size(handle) == [width, height])
|
|
197
|
-
{}
|
|
198
|
-
else
|
|
199
|
-
browser.manage.window.resize_to(width, height)
|
|
200
|
-
end
|
|
218
|
+
browser.manage.window.resize_to(width, height)
|
|
201
219
|
end
|
|
202
220
|
end
|
|
203
221
|
|
|
@@ -208,7 +226,15 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
208
226
|
sleep 0.1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405
|
|
209
227
|
end
|
|
210
228
|
|
|
229
|
+
def fullscreen_window(handle)
|
|
230
|
+
within_given_window(handle) do
|
|
231
|
+
browser.manage.window.full_screen
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
211
235
|
def close_window(handle)
|
|
236
|
+
raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first
|
|
237
|
+
|
|
212
238
|
within_given_window(handle) do
|
|
213
239
|
browser.close
|
|
214
240
|
end
|
|
@@ -218,7 +244,16 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
218
244
|
browser.window_handles
|
|
219
245
|
end
|
|
220
246
|
|
|
221
|
-
def open_new_window
|
|
247
|
+
def open_new_window(kind = :tab)
|
|
248
|
+
if browser.switch_to.respond_to?(:new_window)
|
|
249
|
+
handle = current_window_handle
|
|
250
|
+
browser.switch_to.new_window(kind)
|
|
251
|
+
switch_to_window(handle)
|
|
252
|
+
else
|
|
253
|
+
browser.manage.new_window(kind)
|
|
254
|
+
end
|
|
255
|
+
rescue NoMethodError, Selenium::WebDriver::Error::WebDriverError
|
|
256
|
+
# If not supported by the driver or browser default to using JS
|
|
222
257
|
browser.execute_script('window.open();')
|
|
223
258
|
end
|
|
224
259
|
|
|
@@ -226,14 +261,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
226
261
|
browser.switch_to.window handle
|
|
227
262
|
end
|
|
228
263
|
|
|
229
|
-
def
|
|
230
|
-
handle = find_window(locator)
|
|
231
|
-
browser.switch_to.window(handle) { yield }
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def accept_modal(_type, options={})
|
|
264
|
+
def accept_modal(_type, **options)
|
|
235
265
|
yield if block_given?
|
|
236
|
-
modal = find_modal(options)
|
|
266
|
+
modal = find_modal(**options)
|
|
237
267
|
|
|
238
268
|
modal.send_keys options[:with] if options[:with]
|
|
239
269
|
|
|
@@ -242,17 +272,18 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
242
272
|
message
|
|
243
273
|
end
|
|
244
274
|
|
|
245
|
-
def dismiss_modal(_type, options
|
|
275
|
+
def dismiss_modal(_type, **options)
|
|
246
276
|
yield if block_given?
|
|
247
|
-
modal = find_modal(options)
|
|
277
|
+
modal = find_modal(**options)
|
|
248
278
|
message = modal.text
|
|
249
279
|
modal.dismiss
|
|
250
280
|
message
|
|
251
281
|
end
|
|
252
282
|
|
|
253
283
|
def quit
|
|
254
|
-
@browser
|
|
255
|
-
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
284
|
+
@browser&.quit
|
|
285
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED,
|
|
286
|
+
Selenium::WebDriver::Error::InvalidSessionIdError
|
|
256
287
|
# Browser must have already gone
|
|
257
288
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
258
289
|
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
@@ -264,129 +295,105 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
264
295
|
end
|
|
265
296
|
|
|
266
297
|
def invalid_element_errors
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
298
|
+
@invalid_element_errors ||=
|
|
299
|
+
[
|
|
300
|
+
::Selenium::WebDriver::Error::StaleElementReferenceError,
|
|
301
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
|
302
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition
|
|
303
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
|
304
|
+
::Selenium::WebDriver::Error::NoSuchElementError, # IE
|
|
305
|
+
::Selenium::WebDriver::Error::InvalidArgumentError # IE
|
|
306
|
+
].tap do |errors|
|
|
307
|
+
if defined?(::Selenium::WebDriver::Error::DetachedShadowRootError)
|
|
308
|
+
errors.push(::Selenium::WebDriver::Error::DetachedShadowRootError)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
276
311
|
end
|
|
277
312
|
|
|
278
313
|
def no_such_window_error
|
|
279
314
|
Selenium::WebDriver::Error::NoSuchWindowError
|
|
280
315
|
end
|
|
281
316
|
|
|
282
|
-
|
|
283
|
-
def marionette?
|
|
284
|
-
firefox? && browser && @w3c
|
|
285
|
-
end
|
|
317
|
+
private
|
|
286
318
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
browser_name == "firefox"
|
|
319
|
+
def native_args(args)
|
|
320
|
+
args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
|
|
290
321
|
end
|
|
291
322
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
browser_name == "chrome"
|
|
323
|
+
def native_active_element
|
|
324
|
+
browser.switch_to.active_element
|
|
295
325
|
end
|
|
296
326
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
327
|
+
def clear_browser_state
|
|
328
|
+
delete_all_cookies
|
|
329
|
+
clear_storage
|
|
330
|
+
rescue *clear_browser_state_errors
|
|
331
|
+
# delete_all_cookies fails when we've previously gone
|
|
332
|
+
# to about:blank, so we rescue this error and do nothing
|
|
333
|
+
# instead.
|
|
300
334
|
end
|
|
301
335
|
|
|
302
|
-
|
|
336
|
+
def clear_browser_state_errors
|
|
337
|
+
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
|
|
338
|
+
end
|
|
303
339
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
options[:browser].to_s
|
|
340
|
+
def unhandled_alert_errors
|
|
341
|
+
@unhandled_alert_errors ||= [Selenium::WebDriver::Error::UnexpectedAlertOpenError]
|
|
307
342
|
end
|
|
308
343
|
|
|
309
|
-
def
|
|
310
|
-
|
|
311
|
-
Selenium::WebDriver::Error::NoSuchAlertError
|
|
312
|
-
else
|
|
313
|
-
Selenium::WebDriver::Error::NoAlertPresentError
|
|
314
|
-
end
|
|
344
|
+
def delete_all_cookies
|
|
345
|
+
@browser.manage.delete_all_cookies
|
|
315
346
|
end
|
|
316
347
|
|
|
317
|
-
def
|
|
318
|
-
|
|
319
|
-
|
|
348
|
+
def clear_storage
|
|
349
|
+
clear_session_storage unless options[:clear_session_storage] == false
|
|
350
|
+
clear_local_storage unless options[:clear_local_storage] == false
|
|
351
|
+
rescue Selenium::WebDriver::Error::JavascriptError
|
|
352
|
+
# session/local storage may not be available if on non-http pages (e.g. about:blank)
|
|
353
|
+
end
|
|
320
354
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
355
|
+
def clear_session_storage
|
|
356
|
+
if @browser.respond_to? :session_storage
|
|
357
|
+
@browser.session_storage.clear
|
|
358
|
+
else
|
|
359
|
+
begin
|
|
360
|
+
@browser&.execute_script('window.sessionStorage.clear()')
|
|
361
|
+
rescue # rubocop:disable Style/RescueStandardError
|
|
362
|
+
unless options[:clear_session_storage].nil?
|
|
363
|
+
warn 'sessionStorage clear requested but is not supported by this driver'
|
|
364
|
+
end
|
|
329
365
|
end
|
|
330
366
|
end
|
|
331
|
-
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
|
|
332
367
|
end
|
|
333
368
|
|
|
334
|
-
def
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
"default_text"
|
|
338
|
-
else
|
|
339
|
-
"'#{response_text.gsub("\\", "\\\\\\").gsub("'", "\\\\'")}'"
|
|
340
|
-
end
|
|
369
|
+
def clear_local_storage
|
|
370
|
+
if @browser.respond_to? :local_storage
|
|
371
|
+
@browser.local_storage.clear
|
|
341
372
|
else
|
|
342
|
-
|
|
373
|
+
begin
|
|
374
|
+
@browser&.execute_script('window.localStorage.clear()')
|
|
375
|
+
rescue # rubocop:disable Style/RescueStandardError
|
|
376
|
+
unless options[:clear_local_storage].nil?
|
|
377
|
+
warn 'localStorage clear requested but is not supported by this driver'
|
|
378
|
+
end
|
|
379
|
+
end
|
|
343
380
|
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def navigate_with_accept(url)
|
|
384
|
+
@browser.navigate.to(url)
|
|
385
|
+
sleep 0.1 # slight wait for alert
|
|
386
|
+
@browser.switch_to.alert.accept
|
|
387
|
+
rescue modal_error
|
|
388
|
+
# alert now gone, should mean navigation happened
|
|
389
|
+
end
|
|
344
390
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
window.capybara = {
|
|
348
|
-
modal_handlers: [],
|
|
349
|
-
current_modal_status: function() {
|
|
350
|
-
return [this.modal_handlers[0].called, this.modal_handlers[0].modal_text];
|
|
351
|
-
},
|
|
352
|
-
add_handler: function(handler) {
|
|
353
|
-
this.modal_handlers.unshift(handler);
|
|
354
|
-
},
|
|
355
|
-
remove_handler: function(handler) {
|
|
356
|
-
window.alert = handler.alert;
|
|
357
|
-
window.confirm = handler.confirm;
|
|
358
|
-
window.prompt = handler.prompt;
|
|
359
|
-
},
|
|
360
|
-
handler_called: function(handler, str) {
|
|
361
|
-
handler.called = true;
|
|
362
|
-
handler.modal_text = str;
|
|
363
|
-
this.remove_handler(handler);
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
var modal_handler = {
|
|
369
|
-
prompt: window.prompt,
|
|
370
|
-
confirm: window.confirm,
|
|
371
|
-
alert: window.alert,
|
|
372
|
-
called: false
|
|
373
|
-
}
|
|
374
|
-
window.capybara.add_handler(modal_handler);
|
|
375
|
-
|
|
376
|
-
window.alert = window.confirm = function(str = "") {
|
|
377
|
-
window.capybara.handler_called(modal_handler, str.toString());
|
|
378
|
-
return #{accept ? 'true' : 'false'};
|
|
379
|
-
}
|
|
380
|
-
window.prompt = function(str = "", default_text = "") {
|
|
381
|
-
window.capybara.handler_called(modal_handler, str.toString());
|
|
382
|
-
return #{prompt_response};
|
|
383
|
-
}
|
|
384
|
-
JS
|
|
385
|
-
execute_script script
|
|
391
|
+
def modal_error
|
|
392
|
+
Selenium::WebDriver::Error::NoSuchAlertError
|
|
386
393
|
end
|
|
387
394
|
|
|
388
395
|
def within_given_window(handle)
|
|
389
|
-
original_handle =
|
|
396
|
+
original_handle = current_window_handle
|
|
390
397
|
if handle == original_handle
|
|
391
398
|
yield
|
|
392
399
|
else
|
|
@@ -397,90 +404,114 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
397
404
|
end
|
|
398
405
|
end
|
|
399
406
|
|
|
400
|
-
def find_modal(options
|
|
407
|
+
def find_modal(text: nil, **options)
|
|
401
408
|
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
|
402
409
|
# Actual wait time may be longer than specified
|
|
403
410
|
wait = Selenium::WebDriver::Wait.new(
|
|
404
|
-
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0
|
|
405
|
-
ignore: modal_error
|
|
411
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0,
|
|
412
|
+
ignore: modal_error
|
|
413
|
+
)
|
|
406
414
|
begin
|
|
407
415
|
wait.until do
|
|
408
416
|
alert = @browser.switch_to.alert
|
|
409
|
-
regexp =
|
|
410
|
-
alert.text.match(regexp)
|
|
417
|
+
regexp = text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(text.to_s))
|
|
418
|
+
matched = alert.text.match?(regexp)
|
|
419
|
+
unless matched
|
|
420
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog with #{text} - found '#{alert.text}' instead."
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
alert
|
|
411
424
|
end
|
|
412
|
-
rescue
|
|
413
|
-
raise Capybara::ModalNotFound
|
|
425
|
+
rescue *find_modal_errors
|
|
426
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
|
414
427
|
end
|
|
415
428
|
end
|
|
416
429
|
|
|
417
|
-
def
|
|
418
|
-
|
|
419
|
-
# Actual wait time may be longer than specified
|
|
420
|
-
wait = Selenium::WebDriver::Wait.new(
|
|
421
|
-
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
|
422
|
-
ignore: modal_error)
|
|
423
|
-
begin
|
|
424
|
-
wait.until do
|
|
425
|
-
called, alert_text = evaluate_script('window.capybara && window.capybara.current_modal_status()')
|
|
426
|
-
if called
|
|
427
|
-
execute_script('window.capybara && window.capybara.modal_handlers.shift()')
|
|
428
|
-
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
|
|
429
|
-
if alert_text.match(regexp)
|
|
430
|
-
alert_text
|
|
431
|
-
else
|
|
432
|
-
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
|
|
433
|
-
end
|
|
434
|
-
elsif called.nil?
|
|
435
|
-
# page changed so modal_handler data has gone away
|
|
436
|
-
warn "Can't verify modal text when page change occurs - ignoring" if options[:text]
|
|
437
|
-
""
|
|
438
|
-
else
|
|
439
|
-
nil
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
rescue Selenium::WebDriver::Error::TimeOutError
|
|
443
|
-
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
|
|
444
|
-
end
|
|
430
|
+
def find_modal_errors
|
|
431
|
+
@find_modal_errors ||= [Selenium::WebDriver::Error::TimeoutError]
|
|
445
432
|
end
|
|
446
433
|
|
|
447
434
|
def silenced_unknown_error_message?(msg)
|
|
448
|
-
silenced_unknown_error_messages.any? { |
|
|
435
|
+
silenced_unknown_error_messages.any? { |regex| msg.match? regex }
|
|
449
436
|
end
|
|
450
437
|
|
|
451
438
|
def silenced_unknown_error_messages
|
|
452
|
-
[
|
|
439
|
+
[/Error communicating with the remote browser/]
|
|
453
440
|
end
|
|
454
441
|
|
|
455
442
|
def unwrap_script_result(arg)
|
|
456
443
|
case arg
|
|
457
444
|
when Array
|
|
458
|
-
arg.map { |
|
|
445
|
+
arg.map { |arr| unwrap_script_result(arr) }
|
|
459
446
|
when Hash
|
|
460
|
-
arg.
|
|
461
|
-
when Selenium::WebDriver::Element
|
|
462
|
-
|
|
447
|
+
arg.transform_values! { |value| unwrap_script_result(value) }
|
|
448
|
+
when Selenium::WebDriver::Element, Selenium::WebDriver::ShadowRoot
|
|
449
|
+
build_node(arg)
|
|
463
450
|
else
|
|
464
451
|
arg
|
|
465
452
|
end
|
|
466
453
|
end
|
|
467
454
|
|
|
468
|
-
def
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
455
|
+
def find_context
|
|
456
|
+
browser
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def build_node(native_node, initial_cache = {})
|
|
460
|
+
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def bridge
|
|
464
|
+
browser.send(:bridge)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def specialize_driver
|
|
468
|
+
browser_type = browser.browser
|
|
469
|
+
Capybara::Selenium::Driver.specializations.select { |k, _v| k === browser_type }.each_value do |specialization| # rubocop:disable Style/CaseEquality
|
|
470
|
+
extend specialization
|
|
484
471
|
end
|
|
485
472
|
end
|
|
473
|
+
|
|
474
|
+
def setup_exit_handler
|
|
475
|
+
main = Process.pid
|
|
476
|
+
at_exit do
|
|
477
|
+
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
|
478
|
+
@exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
|
|
479
|
+
quit if Process.pid == main
|
|
480
|
+
exit @exit_status if @exit_status # Force exit with stored status
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def reset_browser_state
|
|
485
|
+
clear_browser_state
|
|
486
|
+
@browser.navigate.to('about:blank')
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def wait_for_empty_page(timer)
|
|
490
|
+
until find_xpath('/html/body/*').empty?
|
|
491
|
+
raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
|
|
492
|
+
|
|
493
|
+
sleep 0.01
|
|
494
|
+
|
|
495
|
+
# It has been observed that it is possible that asynchronous JS code in
|
|
496
|
+
# the application under test can navigate the browser away from about:blank
|
|
497
|
+
# if the timing is just right. Ensure we are still at about:blank...
|
|
498
|
+
@browser.navigate.to('about:blank') unless current_url == 'about:blank'
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def accept_unhandled_reset_alert
|
|
503
|
+
@browser.switch_to.alert.accept
|
|
504
|
+
sleep 0.25 # allow time for the modal to be handled
|
|
505
|
+
rescue modal_error
|
|
506
|
+
# The alert is now gone.
|
|
507
|
+
# If navigation has not occurred attempt again and accept alert
|
|
508
|
+
# since FF may have dismissed the alert at first attempt.
|
|
509
|
+
navigate_with_accept('about:blank') if current_url != 'about:blank'
|
|
510
|
+
end
|
|
486
511
|
end
|
|
512
|
+
|
|
513
|
+
require 'capybara/selenium/driver_specializations/chrome_driver'
|
|
514
|
+
require 'capybara/selenium/driver_specializations/firefox_driver'
|
|
515
|
+
require 'capybara/selenium/driver_specializations/internet_explorer_driver'
|
|
516
|
+
require 'capybara/selenium/driver_specializations/safari_driver'
|
|
517
|
+
require 'capybara/selenium/driver_specializations/edge_driver'
|