capybara 3.3.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 -0
- data/History.md +803 -13
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +25 -9
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +17 -3
- data/lib/capybara/driver/node.rb +31 -6
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +31 -7
- data/lib/capybara/minitest/spec.rb +180 -88
- data/lib/capybara/minitest.rb +262 -149
- data/lib/capybara/node/actions.rb +202 -116
- data/lib/capybara/node/base.rb +34 -19
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +10 -12
- data/lib/capybara/node/element.rb +269 -115
- data/lib/capybara/node/finders.rb +99 -77
- data/lib/capybara/node/matchers.rb +327 -151
- data/lib/capybara/node/simple.rb +48 -13
- 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 +8 -9
- data/lib/capybara/queries/base_query.rb +23 -16
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +587 -130
- data/lib/capybara/queries/sibling_query.rb +8 -6
- data/lib/capybara/queries/style_query.rb +6 -2
- data/lib/capybara/queries/text_query.rb +28 -14
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +92 -25
- data/lib/capybara/rack_test/driver.rb +16 -7
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +68 -41
- data/lib/capybara/rack_test/node.rb +106 -39
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/registration_container.rb +41 -0
- data/lib/capybara/registrations/drivers.rb +42 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +66 -0
- data/lib/capybara/result.rb +75 -52
- data/lib/capybara/rspec/features.rb +7 -7
- data/lib/capybara/rspec/matcher_proxies.rb +39 -18
- data/lib/capybara/rspec/matchers/base.rb +113 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +141 -339
- data/lib/capybara/rspec.rb +2 -0
- 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 +27 -25
- data/lib/capybara/selector/definition/button.rb +68 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +28 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +62 -0
- data/lib/capybara/selector/definition/link.rb +55 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/definition.rb +280 -0
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +73 -25
- data/lib/capybara/selector/filters/base.rb +24 -5
- data/lib/capybara/selector/filters/expression_filter.rb +3 -3
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +16 -2
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +85 -348
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -447
- 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 +255 -143
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
- 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 +436 -134
- 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 +56 -19
- data/lib/capybara/server/checker.rb +9 -3
- data/lib/capybara/server/middleware.rb +28 -12
- data/lib/capybara/server.rb +33 -10
- data/lib/capybara/session/config.rb +34 -10
- data/lib/capybara/session/matchers.rb +23 -16
- data/lib/capybara/session.rb +230 -170
- 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 +121 -8
- data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +127 -40
- data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
- data/lib/capybara/spec/session/assert_current_path_spec.rb +21 -18
- data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
- data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
- data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
- data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
- data/lib/capybara/spec/session/body_spec.rb +6 -6
- data/lib/capybara/spec/session/check_spec.rb +102 -47
- data/lib/capybara/spec/session/choose_spec.rb +58 -32
- data/lib/capybara/spec/session/click_button_spec.rb +219 -163
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
- data/lib/capybara/spec/session/click_link_spec.rb +77 -54
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +38 -29
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
- data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -56
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +101 -46
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
- data/lib/capybara/spec/session/find_field_spec.rb +32 -30
- data/lib/capybara/spec/session/find_link_spec.rb +31 -21
- data/lib/capybara/spec/session/find_spec.rb +244 -141
- data/lib/capybara/spec/session/first_spec.rb +43 -43
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
- data/lib/capybara/spec/session/go_back_spec.rb +1 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
- data/lib/capybara/spec/session/has_button_spec.rb +94 -13
- data/lib/capybara/spec/session/has_css_spec.rb +272 -132
- data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +137 -58
- data/lib/capybara/spec/session/has_link_spec.rb +44 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +181 -4
- data/lib/capybara/spec/session/has_text_spec.rb +101 -53
- data/lib/capybara/spec/session/has_title_spec.rb +19 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
- data/lib/capybara/spec/session/headers_spec.rb +1 -1
- data/lib/capybara/spec/session/html_spec.rb +13 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
- data/lib/capybara/spec/session/node_spec.rb +894 -142
- data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
- data/lib/capybara/spec/session/response_code_spec.rb +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +37 -37
- data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
- 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 +85 -85
- data/lib/capybara/spec/session/selectors_spec.rb +49 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +25 -24
- data/lib/capybara/spec/session/title_spec.rb +7 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +68 -49
- data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
- data/lib/capybara/spec/session/window/window_spec.rb +62 -63
- data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
- data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
- data/lib/capybara/spec/session/within_spec.rb +79 -42
- data/lib/capybara/spec/spec_helper.rb +41 -53
- data/lib/capybara/spec/test_app.rb +132 -43
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +139 -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 +67 -0
- data/lib/capybara/spec/views/with_animation.erb +39 -4
- 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 +37 -9
- 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 +26 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +19 -25
- data/lib/capybara.rb +126 -111
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +56 -44
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +8 -8
- data/spec/dsl_spec.rb +79 -52
- data/spec/filter_set_spec.rb +9 -9
- data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
- data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
- data/spec/minitest_spec.rb +45 -7
- data/spec/minitest_spec_spec.rb +87 -64
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +172 -116
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +80 -72
- data/spec/rspec/features_spec.rb +21 -16
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +407 -365
- data/spec/rspec/views_spec.rb +3 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +63 -41
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +334 -89
- data/spec/selenium_spec_chrome.rb +176 -62
- data/spec/selenium_spec_chrome_remote.rb +54 -14
- data/spec/selenium_spec_edge.rb +41 -8
- data/spec/selenium_spec_firefox.rb +228 -0
- data/spec/selenium_spec_firefox_remote.rb +94 -0
- data/spec/selenium_spec_ie.rb +129 -11
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +171 -97
- data/spec/session_spec.rb +34 -18
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +344 -80
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +326 -28
- data/lib/capybara/rspec/compound.rb +0 -94
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
- data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
- data/lib/capybara/spec/session/has_style_spec.rb +0 -25
- 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 -167
|
@@ -4,53 +4,89 @@ require 'uri'
|
|
|
4
4
|
require 'English'
|
|
5
5
|
|
|
6
6
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
7
|
+
include Capybara::Selenium::Find
|
|
8
|
+
|
|
7
9
|
DEFAULT_OPTIONS = {
|
|
8
10
|
browser: :firefox,
|
|
9
|
-
clear_local_storage:
|
|
10
|
-
clear_session_storage:
|
|
11
|
+
clear_local_storage: nil,
|
|
12
|
+
clear_session_storage: nil
|
|
11
13
|
}.freeze
|
|
12
|
-
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage].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')
|
|
16
|
+
|
|
13
17
|
attr_reader :app, :options
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
21
61
|
end
|
|
22
62
|
|
|
23
63
|
def browser
|
|
24
64
|
unless @browser
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
extend ChromeDriver if chrome?
|
|
34
|
-
extend MarionetteDriver if marionette?
|
|
35
|
-
|
|
36
|
-
main = Process.pid
|
|
37
|
-
at_exit do
|
|
38
|
-
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
|
39
|
-
@exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
|
|
40
|
-
quit if Process.pid == main
|
|
41
|
-
exit @exit_status if @exit_status # Force exit with stored status
|
|
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
|
|
42
72
|
end
|
|
73
|
+
processed_options = options.except(*SPECIAL_OPTIONS)
|
|
74
|
+
|
|
75
|
+
@browser = Selenium::WebDriver.for(options[:browser], processed_options)
|
|
76
|
+
|
|
77
|
+
specialize_driver
|
|
78
|
+
setup_exit_handler
|
|
43
79
|
end
|
|
44
80
|
@browser
|
|
45
81
|
end
|
|
46
82
|
|
|
47
83
|
def initialize(app, **options)
|
|
84
|
+
super()
|
|
48
85
|
self.class.load_selenium
|
|
49
|
-
@session = nil
|
|
50
86
|
@app = app
|
|
51
87
|
@browser = nil
|
|
52
88
|
@exit_status = nil
|
|
53
|
-
@frame_handles = {}
|
|
89
|
+
@frame_handles = Hash.new { |hash, handle| hash[handle] = [] }
|
|
54
90
|
@options = DEFAULT_OPTIONS.merge(options)
|
|
55
91
|
@node_class = ::Capybara::Selenium::Node
|
|
56
92
|
end
|
|
@@ -73,6 +109,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
73
109
|
|
|
74
110
|
def html
|
|
75
111
|
browser.page_source
|
|
112
|
+
rescue Selenium::WebDriver::Error::JavascriptError => e
|
|
113
|
+
raise unless e.message.include?('documentElement is null')
|
|
76
114
|
end
|
|
77
115
|
|
|
78
116
|
def title
|
|
@@ -83,14 +121,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
83
121
|
browser.current_url
|
|
84
122
|
end
|
|
85
123
|
|
|
86
|
-
def find_xpath(selector)
|
|
87
|
-
browser.find_elements(:xpath, selector).map(&method(:build_node))
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def find_css(selector)
|
|
91
|
-
browser.find_elements(:css, selector).map(&method(:build_node))
|
|
92
|
-
end
|
|
93
|
-
|
|
94
124
|
def wait?; true; end
|
|
95
125
|
def needs_server?; true; end
|
|
96
126
|
|
|
@@ -109,8 +139,17 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
109
139
|
unwrap_script_result(result)
|
|
110
140
|
end
|
|
111
141
|
|
|
112
|
-
def
|
|
113
|
-
|
|
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)
|
|
114
153
|
end
|
|
115
154
|
|
|
116
155
|
def reset!
|
|
@@ -120,70 +159,45 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
120
159
|
navigated = false
|
|
121
160
|
timer = Capybara::Helpers.timer(expire_in: 10)
|
|
122
161
|
begin
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
begin
|
|
127
|
-
@browser.manage.delete_all_cookies
|
|
128
|
-
clear_storage
|
|
129
|
-
# rescue Selenium::WebDriver::Error::NoSuchAlertError
|
|
130
|
-
# # Handle a bug in Firefox/Geckodriver where it thinks it needs an alert modal to exist
|
|
131
|
-
# # for no good reason
|
|
132
|
-
# retry
|
|
133
|
-
rescue Selenium::WebDriver::Error::UnhandledError # rubocop:disable Lint/HandleExceptions
|
|
134
|
-
# delete_all_cookies fails when we've previously gone
|
|
135
|
-
# to about:blank, so we rescue this error and do nothing
|
|
136
|
-
# instead.
|
|
137
|
-
end
|
|
138
|
-
@browser.navigate.to("about:blank")
|
|
139
|
-
end
|
|
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
|
|
140
165
|
navigated = true
|
|
141
|
-
|
|
142
166
|
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
sleep 0.05
|
|
146
|
-
end
|
|
147
|
-
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
|
167
|
+
wait_for_empty_page(timer)
|
|
168
|
+
rescue *unhandled_alert_errors
|
|
148
169
|
# This error is thrown if an unhandled alert is on the page
|
|
149
170
|
# Firefox appears to automatically dismiss this alert, chrome does not
|
|
150
171
|
# We'll try to accept it
|
|
151
|
-
|
|
152
|
-
@browser.switch_to.alert.accept
|
|
153
|
-
sleep 0.25 # allow time for the modal to be handled
|
|
154
|
-
rescue modal_error
|
|
155
|
-
# The alert is now gone
|
|
156
|
-
if current_url != "about:blank"
|
|
157
|
-
begin
|
|
158
|
-
# If navigation has not occurred attempt again and accept alert
|
|
159
|
-
# since FF may have dismissed the alert at first attempt
|
|
160
|
-
@browser.navigate.to("about:blank")
|
|
161
|
-
sleep 0.1 # slight wait for alert
|
|
162
|
-
@browser.switch_to.alert.accept
|
|
163
|
-
rescue modal_error # rubocop:disable Metrics/BlockNesting, Lint/HandleExceptions
|
|
164
|
-
# alert now gone, should mean navigation happened
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
172
|
+
accept_unhandled_reset_alert
|
|
168
173
|
# try cleaning up the browser again
|
|
169
174
|
retry
|
|
170
175
|
end
|
|
171
176
|
end
|
|
172
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)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
173
190
|
def switch_to_frame(frame)
|
|
191
|
+
handles = @frame_handles[current_window_handle]
|
|
174
192
|
case frame
|
|
175
193
|
when :top
|
|
176
|
-
|
|
194
|
+
handles.clear
|
|
177
195
|
browser.switch_to.default_content
|
|
178
196
|
when :parent
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@frame_handles[browser.window_handle].pop
|
|
182
|
-
browser.switch_to.default_content
|
|
183
|
-
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
|
|
197
|
+
handles.pop
|
|
198
|
+
browser.switch_to.parent_frame
|
|
184
199
|
else
|
|
185
|
-
|
|
186
|
-
@frame_handles[browser.window_handle] << frame.native
|
|
200
|
+
handles << frame
|
|
187
201
|
browser.switch_to.frame(frame.native)
|
|
188
202
|
end
|
|
189
203
|
end
|
|
@@ -219,7 +233,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
219
233
|
end
|
|
220
234
|
|
|
221
235
|
def close_window(handle)
|
|
222
|
-
raise ArgumentError,
|
|
236
|
+
raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first
|
|
237
|
+
|
|
223
238
|
within_given_window(handle) do
|
|
224
239
|
browser.close
|
|
225
240
|
end
|
|
@@ -229,7 +244,16 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
229
244
|
browser.window_handles
|
|
230
245
|
end
|
|
231
246
|
|
|
232
|
-
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
|
|
233
257
|
browser.execute_script('window.open();')
|
|
234
258
|
end
|
|
235
259
|
|
|
@@ -239,7 +263,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
239
263
|
|
|
240
264
|
def accept_modal(_type, **options)
|
|
241
265
|
yield if block_given?
|
|
242
|
-
modal = find_modal(options)
|
|
266
|
+
modal = find_modal(**options)
|
|
243
267
|
|
|
244
268
|
modal.send_keys options[:with] if options[:with]
|
|
245
269
|
|
|
@@ -250,7 +274,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
250
274
|
|
|
251
275
|
def dismiss_modal(_type, **options)
|
|
252
276
|
yield if block_given?
|
|
253
|
-
modal = find_modal(options)
|
|
277
|
+
modal = find_modal(**options)
|
|
254
278
|
message = modal.text
|
|
255
279
|
modal.dismiss
|
|
256
280
|
message
|
|
@@ -258,7 +282,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
258
282
|
|
|
259
283
|
def quit
|
|
260
284
|
@browser&.quit
|
|
261
|
-
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
285
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED,
|
|
286
|
+
Selenium::WebDriver::Error::InvalidSessionIdError
|
|
262
287
|
# Browser must have already gone
|
|
263
288
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
264
289
|
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
@@ -270,19 +295,19 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
270
295
|
end
|
|
271
296
|
|
|
272
297
|
def invalid_element_errors
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
|
286
311
|
end
|
|
287
312
|
|
|
288
313
|
def no_such_window_error
|
|
@@ -291,56 +316,78 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
291
316
|
|
|
292
317
|
private
|
|
293
318
|
|
|
294
|
-
def
|
|
295
|
-
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
def marionette?
|
|
299
|
-
firefox? && w3c?
|
|
319
|
+
def native_args(args)
|
|
320
|
+
args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
|
|
300
321
|
end
|
|
301
322
|
|
|
302
|
-
def
|
|
303
|
-
|
|
323
|
+
def native_active_element
|
|
324
|
+
browser.switch_to.active_element
|
|
304
325
|
end
|
|
305
326
|
|
|
306
|
-
def
|
|
307
|
-
|
|
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.
|
|
308
334
|
end
|
|
309
335
|
|
|
310
|
-
def
|
|
311
|
-
|
|
336
|
+
def clear_browser_state_errors
|
|
337
|
+
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
|
|
312
338
|
end
|
|
313
339
|
|
|
314
|
-
def
|
|
315
|
-
|
|
340
|
+
def unhandled_alert_errors
|
|
341
|
+
@unhandled_alert_errors ||= [Selenium::WebDriver::Error::UnexpectedAlertOpenError]
|
|
316
342
|
end
|
|
317
343
|
|
|
318
|
-
def
|
|
319
|
-
browser.
|
|
344
|
+
def delete_all_cookies
|
|
345
|
+
@browser.manage.delete_all_cookies
|
|
320
346
|
end
|
|
321
347
|
|
|
322
|
-
def
|
|
323
|
-
|
|
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)
|
|
324
353
|
end
|
|
325
354
|
|
|
326
|
-
def
|
|
327
|
-
if
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
|
332
365
|
end
|
|
333
366
|
end
|
|
367
|
+
end
|
|
334
368
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
369
|
+
def clear_local_storage
|
|
370
|
+
if @browser.respond_to? :local_storage
|
|
371
|
+
@browser.local_storage.clear
|
|
372
|
+
else
|
|
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
|
|
340
379
|
end
|
|
341
380
|
end
|
|
342
381
|
end
|
|
343
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
|
|
390
|
+
|
|
344
391
|
def modal_error
|
|
345
392
|
Selenium::WebDriver::Error::NoSuchAlertError
|
|
346
393
|
end
|
|
@@ -367,16 +414,25 @@ private
|
|
|
367
414
|
begin
|
|
368
415
|
wait.until do
|
|
369
416
|
alert = @browser.switch_to.alert
|
|
370
|
-
regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
|
|
371
|
-
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
|
|
372
424
|
end
|
|
373
|
-
rescue
|
|
425
|
+
rescue *find_modal_errors
|
|
374
426
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
|
375
427
|
end
|
|
376
428
|
end
|
|
377
429
|
|
|
430
|
+
def find_modal_errors
|
|
431
|
+
@find_modal_errors ||= [Selenium::WebDriver::Error::TimeoutError]
|
|
432
|
+
end
|
|
433
|
+
|
|
378
434
|
def silenced_unknown_error_message?(msg)
|
|
379
|
-
silenced_unknown_error_messages.any? { |
|
|
435
|
+
silenced_unknown_error_messages.any? { |regex| msg.match? regex }
|
|
380
436
|
end
|
|
381
437
|
|
|
382
438
|
def silenced_unknown_error_messages
|
|
@@ -386,20 +442,76 @@ private
|
|
|
386
442
|
def unwrap_script_result(arg)
|
|
387
443
|
case arg
|
|
388
444
|
when Array
|
|
389
|
-
arg.map { |
|
|
445
|
+
arg.map { |arr| unwrap_script_result(arr) }
|
|
390
446
|
when Hash
|
|
391
|
-
arg.
|
|
392
|
-
when Selenium::WebDriver::Element
|
|
447
|
+
arg.transform_values! { |value| unwrap_script_result(value) }
|
|
448
|
+
when Selenium::WebDriver::Element, Selenium::WebDriver::ShadowRoot
|
|
393
449
|
build_node(arg)
|
|
394
450
|
else
|
|
395
451
|
arg
|
|
396
452
|
end
|
|
397
453
|
end
|
|
398
454
|
|
|
399
|
-
def
|
|
400
|
-
|
|
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
|
|
471
|
+
end
|
|
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'
|
|
401
510
|
end
|
|
402
511
|
end
|
|
403
512
|
|
|
404
513
|
require 'capybara/selenium/driver_specializations/chrome_driver'
|
|
405
|
-
require 'capybara/selenium/driver_specializations/
|
|
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'
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'capybara/selenium/nodes/chrome_node'
|
|
4
|
+
require 'capybara/selenium/patches/logs'
|
|
5
|
+
|
|
3
6
|
module Capybara::Selenium::Driver::ChromeDriver
|
|
7
|
+
def self.extended(base)
|
|
8
|
+
bridge = base.send(:bridge)
|
|
9
|
+
bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
|
|
10
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
|
11
|
+
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
|
12
|
+
end
|
|
13
|
+
|
|
4
14
|
def fullscreen_window(handle)
|
|
5
15
|
within_given_window(handle) do
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
result['value']
|
|
13
|
-
end
|
|
16
|
+
super
|
|
17
|
+
rescue NoMethodError => e
|
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
|
19
|
+
|
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
|
21
|
+
result['value']
|
|
14
22
|
end
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def resize_window_to(handle, width, height)
|
|
18
26
|
super
|
|
19
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
20
|
-
raise unless e.message
|
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
|
29
|
+
|
|
21
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
|
22
31
|
# and raises unnecessary error. Wait a bit and try again.
|
|
23
|
-
sleep 0.
|
|
32
|
+
sleep 0.25
|
|
24
33
|
super
|
|
25
34
|
end
|
|
26
35
|
|
|
@@ -29,7 +38,80 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
|
29
38
|
return unless @browser
|
|
30
39
|
|
|
31
40
|
switch_to_window(window_handles.first)
|
|
32
|
-
window_handles.slice(1
|
|
41
|
+
window_handles.slice(1..).each { |win| close_window(win) }
|
|
42
|
+
return super if chromedriver_version < 73
|
|
43
|
+
|
|
44
|
+
timer = Capybara::Helpers.timer(expire_in: 10)
|
|
45
|
+
begin
|
|
46
|
+
clear_storage unless uniform_storage_clear?
|
|
47
|
+
@browser.navigate.to('about:blank')
|
|
48
|
+
wait_for_empty_page(timer)
|
|
49
|
+
rescue *unhandled_alert_errors
|
|
50
|
+
accept_unhandled_reset_alert
|
|
51
|
+
retry
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def storage_types_to_clear
|
|
60
|
+
types = ['cookies']
|
|
61
|
+
types << 'local_storage' if clear_all_storage?
|
|
62
|
+
types.join(',')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def clear_all_storage?
|
|
66
|
+
storage_clears.none? false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def uniform_storage_clear?
|
|
70
|
+
storage_clears.uniq { |s| s == false }.length <= 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def storage_clears
|
|
74
|
+
options.values_at(:clear_session_storage, :clear_local_storage)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def clear_storage
|
|
78
|
+
# Chrome errors if attempt to clear storage on about:blank
|
|
79
|
+
# In W3C mode it crashes chromedriver
|
|
80
|
+
url = current_url
|
|
81
|
+
super unless url.nil? || url.start_with?('about:')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def delete_all_cookies
|
|
85
|
+
execute_cdp('Network.clearBrowserCookies')
|
|
86
|
+
rescue *cdp_unsupported_errors
|
|
87
|
+
# If the CDP clear isn't supported do original limited clear
|
|
33
88
|
super
|
|
34
89
|
end
|
|
90
|
+
|
|
91
|
+
def cdp_unsupported_errors
|
|
92
|
+
@cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def execute_cdp(cmd, params = {})
|
|
96
|
+
if browser.respond_to? :execute_cdp
|
|
97
|
+
browser.execute_cdp(cmd, **params)
|
|
98
|
+
else
|
|
99
|
+
args = { cmd: cmd, params: params }
|
|
100
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
|
|
101
|
+
result['value']
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def build_node(native_node, initial_cache = {})
|
|
106
|
+
::Capybara::Selenium::ChromeNode.new(self, native_node, initial_cache)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def chromedriver_version
|
|
110
|
+
@chromedriver_version ||= begin
|
|
111
|
+
caps = browser.capabilities
|
|
112
|
+
caps['chrome']&.fetch('chromedriverVersion', nil).to_f
|
|
113
|
+
end
|
|
114
|
+
end
|
|
35
115
|
end
|
|
116
|
+
|
|
117
|
+
Capybara::Selenium::Driver.register_specialization :chrome, Capybara::Selenium::Driver::ChromeDriver
|