capybara 3.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/History.md +1813 -0
- data/License.txt +22 -0
- data/README.md +1099 -0
- data/lib/capybara.rb +511 -0
- data/lib/capybara/config.rb +94 -0
- data/lib/capybara/cucumber.rb +27 -0
- data/lib/capybara/driver/base.rb +170 -0
- data/lib/capybara/driver/node.rb +139 -0
- data/lib/capybara/dsl.rb +65 -0
- data/lib/capybara/helpers.rb +108 -0
- data/lib/capybara/minitest.rb +386 -0
- data/lib/capybara/minitest/spec.rb +264 -0
- data/lib/capybara/node/actions.rb +420 -0
- data/lib/capybara/node/base.rb +143 -0
- data/lib/capybara/node/document.rb +48 -0
- data/lib/capybara/node/document_matchers.rb +67 -0
- data/lib/capybara/node/element.rb +606 -0
- data/lib/capybara/node/finders.rb +325 -0
- data/lib/capybara/node/matchers.rb +883 -0
- data/lib/capybara/node/simple.rb +208 -0
- data/lib/capybara/queries/ancestor_query.rb +27 -0
- data/lib/capybara/queries/base_query.rb +106 -0
- data/lib/capybara/queries/current_path_query.rb +51 -0
- data/lib/capybara/queries/match_query.rb +26 -0
- data/lib/capybara/queries/selector_query.rb +710 -0
- data/lib/capybara/queries/sibling_query.rb +26 -0
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +110 -0
- data/lib/capybara/queries/title_query.rb +39 -0
- data/lib/capybara/rack_test/browser.rb +140 -0
- data/lib/capybara/rack_test/css_handlers.rb +13 -0
- data/lib/capybara/rack_test/driver.rb +109 -0
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +127 -0
- data/lib/capybara/rack_test/node.rb +325 -0
- data/lib/capybara/rails.rb +16 -0
- data/lib/capybara/registrations/drivers.rb +36 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
- data/lib/capybara/registrations/servers.rb +44 -0
- data/lib/capybara/result.rb +190 -0
- data/lib/capybara/rspec.rb +29 -0
- data/lib/capybara/rspec/features.rb +23 -0
- data/lib/capybara/rspec/matcher_proxies.rb +82 -0
- data/lib/capybara/rspec/matchers.rb +201 -0
- data/lib/capybara/rspec/matchers/base.rb +111 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +38 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/selector.rb +233 -0
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
- data/lib/capybara/selector/css.rb +102 -0
- data/lib/capybara/selector/definition.rb +276 -0
- data/lib/capybara/selector/definition/button.rb +51 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +27 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +62 -0
- data/lib/capybara/selector/definition/link.rb +46 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/filter.rb +5 -0
- data/lib/capybara/selector/filter_set.rb +124 -0
- data/lib/capybara/selector/filters/base.rb +77 -0
- data/lib/capybara/selector/filters/expression_filter.rb +22 -0
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +31 -0
- data/lib/capybara/selector/regexp_disassembler.rb +214 -0
- data/lib/capybara/selector/selector.rb +147 -0
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
- data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
- data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
- data/lib/capybara/selenium/driver.rb +496 -0
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +119 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +126 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +110 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +78 -0
- data/lib/capybara/selenium/logger_suppressor.rb +34 -0
- data/lib/capybara/selenium/node.rb +610 -0
- data/lib/capybara/selenium/nodes/chrome_node.rb +119 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +131 -0
- data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
- data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
- data/lib/capybara/selenium/patches/atoms.rb +18 -0
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +47 -0
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server.rb +126 -0
- data/lib/capybara/server/animation_disabler.rb +58 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +69 -0
- data/lib/capybara/session.rb +942 -0
- data/lib/capybara/session/config.rb +124 -0
- data/lib/capybara/session/matchers.rb +87 -0
- data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
- data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/fixtures/test_file.txt +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +13 -0
- data/lib/capybara/spec/public/jquery.js +5 -0
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +268 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +81 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +32 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +78 -0
- data/lib/capybara/spec/session/all_spec.rb +278 -0
- data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
- data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
- data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
- data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +216 -0
- data/lib/capybara/spec/session/body_spec.rb +23 -0
- data/lib/capybara/spec/session/check_spec.rb +235 -0
- data/lib/capybara/spec/session/choose_spec.rb +121 -0
- data/lib/capybara/spec/session/click_button_spec.rb +506 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +129 -0
- data/lib/capybara/spec/session/click_link_spec.rb +229 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +31 -0
- data/lib/capybara/spec/session/current_url_spec.rb +115 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +36 -0
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +21 -0
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +38 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +31 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +25 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +120 -0
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +49 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +28 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +286 -0
- data/lib/capybara/spec/session/find_button_spec.rb +74 -0
- data/lib/capybara/spec/session/find_by_id_spec.rb +33 -0
- data/lib/capybara/spec/session/find_field_spec.rb +113 -0
- data/lib/capybara/spec/session/find_link_spec.rb +70 -0
- data/lib/capybara/spec/session/find_spec.rb +531 -0
- data/lib/capybara/spec/session/first_spec.rb +156 -0
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
- data/lib/capybara/spec/session/go_back_spec.rb +12 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +14 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
- data/lib/capybara/spec/session/has_button_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +374 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +138 -0
- data/lib/capybara/spec/session/has_field_spec.rb +349 -0
- data/lib/capybara/spec/session/has_link_spec.rb +39 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
- data/lib/capybara/spec/session/has_select_spec.rb +310 -0
- data/lib/capybara/spec/session/has_selector_spec.rb +202 -0
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +198 -0
- data/lib/capybara/spec/session/has_text_spec.rb +394 -0
- data/lib/capybara/spec/session/has_title_spec.rb +71 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +149 -0
- data/lib/capybara/spec/session/headers_spec.rb +8 -0
- data/lib/capybara/spec/session/html_spec.rb +47 -0
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/node_spec.rb +1292 -0
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +33 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +148 -0
- data/lib/capybara/spec/session/response_code_spec.rb +8 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +21 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +43 -0
- data/lib/capybara/spec/session/save_page_spec.rb +110 -0
- data/lib/capybara/spec/session/save_screenshot_spec.rb +55 -0
- data/lib/capybara/spec/session/screenshot_spec.rb +18 -0
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +229 -0
- data/lib/capybara/spec/session/selectors_spec.rb +98 -0
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/session/text_spec.rb +74 -0
- data/lib/capybara/spec/session/title_spec.rb +29 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +100 -0
- data/lib/capybara/spec/session/unselect_spec.rb +116 -0
- data/lib/capybara/spec/session/visit_spec.rb +204 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +89 -0
- data/lib/capybara/spec/session/window/current_window_spec.rb +28 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +31 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +132 -0
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +99 -0
- data/lib/capybara/spec/session/window/window_spec.rb +203 -0
- data/lib/capybara/spec/session/window/windows_spec.rb +34 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +157 -0
- data/lib/capybara/spec/session/within_spec.rb +199 -0
- data/lib/capybara/spec/spec_helper.rb +134 -0
- data/lib/capybara/spec/test_app.rb +226 -0
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/buttons.erb +5 -0
- data/lib/capybara/spec/views/fieldsets.erb +30 -0
- data/lib/capybara/spec/views/form.erb +685 -0
- data/lib/capybara/spec/views/frame_child.erb +18 -0
- data/lib/capybara/spec/views/frame_one.erb +10 -0
- data/lib/capybara/spec/views/frame_parent.erb +9 -0
- data/lib/capybara/spec/views/frame_two.erb +9 -0
- data/lib/capybara/spec/views/header_links.erb +8 -0
- data/lib/capybara/spec/views/host_links.erb +13 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +32 -0
- data/lib/capybara/spec/views/path.erb +13 -0
- data/lib/capybara/spec/views/popup_one.erb +9 -0
- data/lib/capybara/spec/views/popup_two.erb +9 -0
- data/lib/capybara/spec/views/postback.erb +14 -0
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +130 -0
- data/lib/capybara/spec/views/with_animation.erb +74 -0
- data/lib/capybara/spec/views/with_base_tag.erb +11 -0
- data/lib/capybara/spec/views/with_count.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +22 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +24 -0
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +208 -0
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_html_entities.erb +2 -0
- data/lib/capybara/spec/views/with_js.erb +160 -0
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +20 -0
- data/lib/capybara/spec/views/with_scope.erb +42 -0
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_simple_html.erb +2 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_title.erb +5 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
- data/lib/capybara/spec/views/with_windows.erb +54 -0
- data/lib/capybara/spec/views/within_frames.erb +15 -0
- data/lib/capybara/version.rb +5 -0
- data/lib/capybara/window.rb +146 -0
- data/spec/basic_node_spec.rb +154 -0
- data/spec/capybara_spec.rb +112 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +276 -0
- data/spec/filter_set_spec.rb +46 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +13 -0
- data/spec/fixtures/selenium_driver_rspec_success.rb +13 -0
- data/spec/minitest_spec.rb +163 -0
- data/spec/minitest_spec_spec.rb +162 -0
- data/spec/per_session_config_spec.rb +68 -0
- data/spec/rack_test_spec.rb +268 -0
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +196 -0
- data/spec/rspec/features_spec.rb +99 -0
- data/spec/rspec/scenarios_spec.rb +19 -0
- data/spec/rspec/shared_spec_matchers.rb +947 -0
- data/spec/rspec/views_spec.rb +14 -0
- data/spec/rspec_matchers_spec.rb +62 -0
- data/spec/rspec_spec.rb +145 -0
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +513 -0
- data/spec/selenium_spec_chrome.rb +188 -0
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +47 -0
- data/spec/selenium_spec_firefox.rb +208 -0
- data/spec/selenium_spec_firefox_remote.rb +80 -0
- data/spec/selenium_spec_ie.rb +150 -0
- data/spec/selenium_spec_safari.rb +148 -0
- data/spec/server_spec.rb +292 -0
- data/spec/session_spec.rb +91 -0
- data/spec/shared_selenium_node.rb +83 -0
- data/spec/shared_selenium_session.rb +476 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +753 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'English'
|
|
5
|
+
|
|
6
|
+
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
7
|
+
include Capybara::Selenium::Find
|
|
8
|
+
|
|
9
|
+
DEFAULT_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
|
+
attr_reader :app, :options
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def load_selenium
|
|
19
|
+
require 'selenium-webdriver'
|
|
20
|
+
require 'capybara/selenium/logger_suppressor'
|
|
21
|
+
require 'capybara/selenium/patches/atoms'
|
|
22
|
+
require 'capybara/selenium/patches/is_displayed'
|
|
23
|
+
require 'capybara/selenium/patches/action_pauser'
|
|
24
|
+
if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0')
|
|
25
|
+
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade."
|
|
26
|
+
end
|
|
27
|
+
rescue LoadError => e
|
|
28
|
+
raise e unless e.message.match?(/selenium-webdriver/)
|
|
29
|
+
|
|
30
|
+
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."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :specializations
|
|
34
|
+
|
|
35
|
+
def register_specialization(browser_name, specialization)
|
|
36
|
+
@specializations ||= {}
|
|
37
|
+
@specializations[browser_name] = specialization
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def browser
|
|
42
|
+
unless @browser
|
|
43
|
+
options[:http_client] ||= begin
|
|
44
|
+
require 'capybara/selenium/patches/persistent_client'
|
|
45
|
+
if options[:timeout]
|
|
46
|
+
::Capybara::Selenium::PersistentClient.new(read_timeout: options[:timeout])
|
|
47
|
+
else
|
|
48
|
+
::Capybara::Selenium::PersistentClient.new
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
|
52
|
+
@browser = Selenium::WebDriver.for(options[:browser], processed_options)
|
|
53
|
+
|
|
54
|
+
specialize_driver
|
|
55
|
+
setup_exit_handler
|
|
56
|
+
end
|
|
57
|
+
@browser
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialize(app, **options)
|
|
61
|
+
self.class.load_selenium
|
|
62
|
+
@app = app
|
|
63
|
+
@browser = nil
|
|
64
|
+
@exit_status = nil
|
|
65
|
+
@frame_handles = Hash.new { |hash, handle| hash[handle] = [] }
|
|
66
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
67
|
+
@node_class = ::Capybara::Selenium::Node
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def visit(path)
|
|
71
|
+
browser.navigate.to(path)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def refresh
|
|
75
|
+
browser.navigate.refresh
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def go_back
|
|
79
|
+
browser.navigate.back
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def go_forward
|
|
83
|
+
browser.navigate.forward
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def html
|
|
87
|
+
browser.page_source
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def title
|
|
91
|
+
browser.title
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def current_url
|
|
95
|
+
browser.current_url
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def wait?; true; end
|
|
99
|
+
def needs_server?; true; end
|
|
100
|
+
|
|
101
|
+
def execute_script(script, *args)
|
|
102
|
+
browser.execute_script(script, *native_args(args))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def evaluate_script(script, *args)
|
|
106
|
+
result = execute_script("return #{script}", *args)
|
|
107
|
+
unwrap_script_result(result)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def evaluate_async_script(script, *args)
|
|
111
|
+
browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
|
|
112
|
+
result = browser.execute_async_script(script, *native_args(args))
|
|
113
|
+
unwrap_script_result(result)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def save_screenshot(path, **_options)
|
|
117
|
+
browser.save_screenshot(path)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def reset!
|
|
121
|
+
# Use instance variable directly so we avoid starting the browser just to reset the session
|
|
122
|
+
return unless @browser
|
|
123
|
+
|
|
124
|
+
navigated = false
|
|
125
|
+
timer = Capybara::Helpers.timer(expire_in: 10)
|
|
126
|
+
begin
|
|
127
|
+
# Only trigger a navigation if we haven't done it already, otherwise it
|
|
128
|
+
# can trigger an endless series of unload modals
|
|
129
|
+
reset_browser_state unless navigated
|
|
130
|
+
navigated = true
|
|
131
|
+
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
|
132
|
+
wait_for_empty_page(timer)
|
|
133
|
+
rescue *unhandled_alert_errors
|
|
134
|
+
# This error is thrown if an unhandled alert is on the page
|
|
135
|
+
# Firefox appears to automatically dismiss this alert, chrome does not
|
|
136
|
+
# We'll try to accept it
|
|
137
|
+
accept_unhandled_reset_alert
|
|
138
|
+
# try cleaning up the browser again
|
|
139
|
+
retry
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def frame_obscured_at?(x:, y:)
|
|
144
|
+
frame = @frame_handles[current_window_handle].last
|
|
145
|
+
return false unless frame
|
|
146
|
+
|
|
147
|
+
switch_to_frame(:parent)
|
|
148
|
+
begin
|
|
149
|
+
frame.base.obscured?(x: x, y: y)
|
|
150
|
+
ensure
|
|
151
|
+
switch_to_frame(frame)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def switch_to_frame(frame)
|
|
156
|
+
handles = @frame_handles[current_window_handle]
|
|
157
|
+
case frame
|
|
158
|
+
when :top
|
|
159
|
+
handles.clear
|
|
160
|
+
browser.switch_to.default_content
|
|
161
|
+
when :parent
|
|
162
|
+
handles.pop
|
|
163
|
+
browser.switch_to.parent_frame
|
|
164
|
+
else
|
|
165
|
+
handles << frame
|
|
166
|
+
browser.switch_to.frame(frame.native)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def current_window_handle
|
|
171
|
+
browser.window_handle
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def window_size(handle)
|
|
175
|
+
within_given_window(handle) do
|
|
176
|
+
size = browser.manage.window.size
|
|
177
|
+
[size.width, size.height]
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def resize_window_to(handle, width, height)
|
|
182
|
+
within_given_window(handle) do
|
|
183
|
+
browser.manage.window.resize_to(width, height)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def maximize_window(handle)
|
|
188
|
+
within_given_window(handle) do
|
|
189
|
+
browser.manage.window.maximize
|
|
190
|
+
end
|
|
191
|
+
sleep 0.1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def fullscreen_window(handle)
|
|
195
|
+
within_given_window(handle) do
|
|
196
|
+
browser.manage.window.full_screen
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def close_window(handle)
|
|
201
|
+
raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first
|
|
202
|
+
|
|
203
|
+
within_given_window(handle) do
|
|
204
|
+
browser.close
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def window_handles
|
|
209
|
+
browser.window_handles
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def open_new_window(kind = :tab)
|
|
213
|
+
browser.manage.new_window(kind)
|
|
214
|
+
rescue NoMethodError, Selenium::WebDriver::Error::WebDriverError
|
|
215
|
+
# If not supported by the driver or browser default to using JS
|
|
216
|
+
browser.execute_script('window.open();')
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def switch_to_window(handle)
|
|
220
|
+
browser.switch_to.window handle
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def accept_modal(_type, **options)
|
|
224
|
+
yield if block_given?
|
|
225
|
+
modal = find_modal(**options)
|
|
226
|
+
|
|
227
|
+
modal.send_keys options[:with] if options[:with]
|
|
228
|
+
|
|
229
|
+
message = modal.text
|
|
230
|
+
modal.accept
|
|
231
|
+
message
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def dismiss_modal(_type, **options)
|
|
235
|
+
yield if block_given?
|
|
236
|
+
modal = find_modal(**options)
|
|
237
|
+
message = modal.text
|
|
238
|
+
modal.dismiss
|
|
239
|
+
message
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def quit
|
|
243
|
+
@browser&.quit
|
|
244
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
245
|
+
# Browser must have already gone
|
|
246
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
247
|
+
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
248
|
+
# probably already gone but not sure - so warn
|
|
249
|
+
warn "Ignoring Selenium UnknownError during driver quit: #{e.message}"
|
|
250
|
+
end
|
|
251
|
+
ensure
|
|
252
|
+
@browser = nil
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def invalid_element_errors
|
|
256
|
+
@invalid_element_errors ||= begin
|
|
257
|
+
[
|
|
258
|
+
::Selenium::WebDriver::Error::StaleElementReferenceError,
|
|
259
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
|
260
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition
|
|
261
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
|
262
|
+
::Selenium::WebDriver::Error::NoSuchElementError, # IE
|
|
263
|
+
::Selenium::WebDriver::Error::InvalidArgumentError # IE
|
|
264
|
+
].tap do |errors|
|
|
265
|
+
unless selenium_4?
|
|
266
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
|
267
|
+
errors.concat [
|
|
268
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
|
269
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
|
270
|
+
::Selenium::WebDriver::Error::InvalidElementStateError,
|
|
271
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError
|
|
272
|
+
]
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def no_such_window_error
|
|
280
|
+
Selenium::WebDriver::Error::NoSuchWindowError
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
|
|
285
|
+
def selenium_4?
|
|
286
|
+
defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def native_args(args)
|
|
290
|
+
args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def clear_browser_state
|
|
294
|
+
delete_all_cookies
|
|
295
|
+
clear_storage
|
|
296
|
+
rescue *clear_browser_state_errors
|
|
297
|
+
# delete_all_cookies fails when we've previously gone
|
|
298
|
+
# to about:blank, so we rescue this error and do nothing
|
|
299
|
+
# instead.
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def clear_browser_state_errors
|
|
303
|
+
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def unhandled_alert_errors
|
|
307
|
+
@unhandled_alert_errors ||= with_legacy_error(
|
|
308
|
+
[Selenium::WebDriver::Error::UnexpectedAlertOpenError],
|
|
309
|
+
'UnhandledAlertError'
|
|
310
|
+
)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def delete_all_cookies
|
|
314
|
+
@browser.manage.delete_all_cookies
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def clear_storage
|
|
318
|
+
clear_session_storage unless options[:clear_session_storage] == false
|
|
319
|
+
clear_local_storage unless options[:clear_local_storage] == false
|
|
320
|
+
rescue Selenium::WebDriver::Error::JavascriptError
|
|
321
|
+
# session/local storage may not be available if on non-http pages (e.g. about:blank)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def clear_session_storage
|
|
325
|
+
if @browser.respond_to? :session_storage
|
|
326
|
+
@browser.session_storage.clear
|
|
327
|
+
else
|
|
328
|
+
begin
|
|
329
|
+
@browser&.execute_script('window.sessionStorage.clear()')
|
|
330
|
+
rescue # rubocop:disable Style/RescueStandardError
|
|
331
|
+
unless options[:clear_session_storage].nil?
|
|
332
|
+
warn 'sessionStorage clear requested but is not supported by this driver'
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def clear_local_storage
|
|
339
|
+
if @browser.respond_to? :local_storage
|
|
340
|
+
@browser.local_storage.clear
|
|
341
|
+
else
|
|
342
|
+
begin
|
|
343
|
+
@browser&.execute_script('window.localStorage.clear()')
|
|
344
|
+
rescue # rubocop:disable Style/RescueStandardError
|
|
345
|
+
unless options[:clear_local_storage].nil?
|
|
346
|
+
warn 'localStorage clear requested but is not supported by this driver'
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def navigate_with_accept(url)
|
|
353
|
+
@browser.navigate.to(url)
|
|
354
|
+
sleep 0.1 # slight wait for alert
|
|
355
|
+
@browser.switch_to.alert.accept
|
|
356
|
+
rescue modal_error
|
|
357
|
+
# alert now gone, should mean navigation happened
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def modal_error
|
|
361
|
+
Selenium::WebDriver::Error::NoSuchAlertError
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def within_given_window(handle)
|
|
365
|
+
original_handle = current_window_handle
|
|
366
|
+
if handle == original_handle
|
|
367
|
+
yield
|
|
368
|
+
else
|
|
369
|
+
switch_to_window(handle)
|
|
370
|
+
result = yield
|
|
371
|
+
switch_to_window(original_handle)
|
|
372
|
+
result
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def find_modal(text: nil, **options)
|
|
377
|
+
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
|
378
|
+
# Actual wait time may be longer than specified
|
|
379
|
+
wait = Selenium::WebDriver::Wait.new(
|
|
380
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0,
|
|
381
|
+
ignore: modal_error
|
|
382
|
+
)
|
|
383
|
+
begin
|
|
384
|
+
wait.until do
|
|
385
|
+
alert = @browser.switch_to.alert
|
|
386
|
+
regexp = text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(text.to_s))
|
|
387
|
+
matched = alert.text.match?(regexp)
|
|
388
|
+
unless matched
|
|
389
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog with #{text} - found '#{alert.text}' instead."
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
alert
|
|
393
|
+
end
|
|
394
|
+
rescue *find_modal_errors
|
|
395
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def find_modal_errors
|
|
400
|
+
@find_modal_errors ||= with_legacy_error([Selenium::WebDriver::Error::TimeoutError], 'TimeOutError')
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def with_legacy_error(errors, legacy_error)
|
|
404
|
+
errors.tap do |errs|
|
|
405
|
+
unless selenium_4?
|
|
406
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
|
407
|
+
errs << Selenium::WebDriver::Error.const_get(legacy_error)
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def silenced_unknown_error_message?(msg)
|
|
414
|
+
silenced_unknown_error_messages.any? { |regex| msg.match? regex }
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def silenced_unknown_error_messages
|
|
418
|
+
[/Error communicating with the remote browser/]
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def unwrap_script_result(arg)
|
|
422
|
+
case arg
|
|
423
|
+
when Array
|
|
424
|
+
arg.map { |arr| unwrap_script_result(arr) }
|
|
425
|
+
when Hash
|
|
426
|
+
arg.transform_values! { |value| unwrap_script_result(value) }
|
|
427
|
+
when Selenium::WebDriver::Element
|
|
428
|
+
build_node(arg)
|
|
429
|
+
else
|
|
430
|
+
arg
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def find_context
|
|
435
|
+
browser
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def build_node(native_node, initial_cache = {})
|
|
439
|
+
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def bridge
|
|
443
|
+
browser.send(:bridge)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def specialize_driver
|
|
447
|
+
browser_type = browser.browser
|
|
448
|
+
Capybara::Selenium::Driver.specializations.select { |k, _v| k === browser_type }.each_value do |specialization| # rubocop:disable Style/CaseEquality
|
|
449
|
+
extend specialization
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def setup_exit_handler
|
|
454
|
+
main = Process.pid
|
|
455
|
+
at_exit do
|
|
456
|
+
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
|
457
|
+
@exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
|
|
458
|
+
quit if Process.pid == main
|
|
459
|
+
exit @exit_status if @exit_status # Force exit with stored status
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def reset_browser_state
|
|
464
|
+
clear_browser_state
|
|
465
|
+
@browser.navigate.to('about:blank')
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def wait_for_empty_page(timer)
|
|
469
|
+
until find_xpath('/html/body/*').empty?
|
|
470
|
+
raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
|
|
471
|
+
|
|
472
|
+
sleep 0.01
|
|
473
|
+
|
|
474
|
+
# It has been observed that it is possible that asynchronous JS code in
|
|
475
|
+
# the application under test can navigate the browser away from about:blank
|
|
476
|
+
# if the timing is just right. Ensure we are still at about:blank...
|
|
477
|
+
@browser.navigate.to('about:blank') unless current_url == 'about:blank'
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def accept_unhandled_reset_alert
|
|
482
|
+
@browser.switch_to.alert.accept
|
|
483
|
+
sleep 0.25 # allow time for the modal to be handled
|
|
484
|
+
rescue modal_error
|
|
485
|
+
# The alert is now gone.
|
|
486
|
+
# If navigation has not occurred attempt again and accept alert
|
|
487
|
+
# since FF may have dismissed the alert at first attempt.
|
|
488
|
+
navigate_with_accept('about:blank') if current_url != 'about:blank'
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
require 'capybara/selenium/driver_specializations/chrome_driver'
|
|
493
|
+
require 'capybara/selenium/driver_specializations/firefox_driver'
|
|
494
|
+
require 'capybara/selenium/driver_specializations/internet_explorer_driver'
|
|
495
|
+
require 'capybara/selenium/driver_specializations/safari_driver'
|
|
496
|
+
require 'capybara/selenium/driver_specializations/edge_driver'
|