capybara 3.0.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 +891 -12
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +29 -10
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +22 -4
- data/lib/capybara/driver/node.rb +38 -9
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +57 -8
- data/lib/capybara/minitest/spec.rb +185 -84
- data/lib/capybara/minitest.rb +264 -145
- data/lib/capybara/node/actions.rb +248 -124
- data/lib/capybara/node/base.rb +35 -20
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +13 -15
- data/lib/capybara/node/element.rb +350 -113
- data/lib/capybara/node/finders.rb +104 -82
- data/lib/capybara/node/matchers.rb +363 -157
- data/lib/capybara/node/simple.rb +54 -15
- 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 +9 -10
- data/lib/capybara/queries/base_query.rb +25 -18
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +11 -0
- data/lib/capybara/queries/selector_query.rb +617 -104
- data/lib/capybara/queries/sibling_query.rb +9 -7
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +40 -22
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +106 -31
- 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 +74 -49
- data/lib/capybara/rack_test/node.rb +120 -47
- 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 +87 -53
- data/lib/capybara/rspec/features.rb +8 -10
- 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 +142 -315
- data/lib/capybara/rspec.rb +3 -2
- 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 -8
- 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 +72 -28
- data/lib/capybara/selector/filters/base.rb +45 -2
- data/lib/capybara/selector/filters/expression_filter.rb +5 -6
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +18 -4
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +89 -200
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -534
- 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 +270 -245
- 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 +460 -170
- 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 +58 -67
- data/lib/capybara/session/config.rb +40 -6
- data/lib/capybara/session/matchers.rb +26 -19
- data/lib/capybara/session.rb +252 -194
- 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 +126 -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 +135 -44
- 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.rb → assert_current_path_spec.rb} +20 -18
- 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.rb → assert_text_spec.rb} +76 -52
- data/lib/capybara/spec/session/{assert_title.rb → 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 +11 -13
- data/lib/capybara/spec/session/check_spec.rb +112 -51
- data/lib/capybara/spec/session/choose_spec.rb +62 -30
- data/lib/capybara/spec/session/click_button_spec.rb +227 -161
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
- data/lib/capybara/spec/session/click_link_spec.rb +89 -55
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +44 -37
- 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.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 +8 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -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 +110 -44
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
- data/lib/capybara/spec/session/find_field_spec.rb +33 -31
- data/lib/capybara/spec/session/find_link_spec.rb +42 -14
- data/lib/capybara/spec/session/find_spec.rb +251 -142
- data/lib/capybara/spec/session/first_spec.rb +45 -44
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
- 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 +98 -12
- data/lib/capybara/spec/session/has_css_spec.rb +271 -137
- 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 +46 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
- 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.rb → 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 +958 -122
- data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
- data/lib/capybara/spec/session/{response_code.rb → 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 +5 -4
- data/lib/capybara/spec/session/save_page_spec.rb +41 -38
- data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
- 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 +102 -76
- data/lib/capybara/spec/session/selectors_spec.rb +51 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +26 -24
- data/lib/capybara/spec/session/title_spec.rb +8 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +79 -53
- data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
- data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
- data/lib/capybara/spec/session/window/window_spec.rb +88 -54
- data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
- data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
- data/lib/capybara/spec/session/within_spec.rb +82 -44
- data/lib/capybara/spec/spec_helper.rb +46 -52
- data/lib/capybara/spec/test_app.rb +148 -41
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +156 -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 +46 -11
- 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 +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +32 -26
- data/lib/capybara.rb +128 -104
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +65 -51
- 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 +84 -55
- data/spec/filter_set_spec.rb +24 -7
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
- data/spec/minitest_spec.rb +49 -5
- data/spec/minitest_spec_spec.rb +92 -62
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +183 -115
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +99 -39
- data/spec/rspec/features_spec.rb +28 -25
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +418 -364
- data/spec/rspec/views_spec.rb +4 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +109 -85
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +392 -62
- data/spec/selenium_spec_chrome.rb +183 -41
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- 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 +192 -81
- data/spec/session_spec.rb +52 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +460 -123
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +344 -45
- 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/rspec/compound.rb +0 -90
- data/lib/capybara/spec/session/assert_selector.rb +0 -149
- 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 -143
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/nodes/chrome_node'
|
4
|
+
require 'capybara/selenium/patches/logs'
|
5
|
+
|
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
|
+
|
14
|
+
def fullscreen_window(handle)
|
15
|
+
within_given_window(handle) do
|
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']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def resize_window_to(handle, width, height)
|
26
|
+
super
|
27
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
29
|
+
|
30
|
+
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
31
|
+
# and raises unnecessary error. Wait a bit and try again.
|
32
|
+
sleep 0.25
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset!
|
37
|
+
# Use instance variable directly so we avoid starting the browser just to reset the session
|
38
|
+
return unless @browser
|
39
|
+
|
40
|
+
switch_to_window(window_handles.first)
|
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
|
88
|
+
super
|
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
|
115
|
+
end
|
116
|
+
|
117
|
+
Capybara::Selenium::Driver.register_specialization :chrome, Capybara::Selenium::Driver::ChromeDriver
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/nodes/edge_node'
|
4
|
+
|
5
|
+
module Capybara::Selenium::Driver::EdgeDriver
|
6
|
+
def self.extended(base)
|
7
|
+
bridge = base.send(:bridge)
|
8
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
9
|
+
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def fullscreen_window(handle)
|
13
|
+
return super if edgedriver_version < 75
|
14
|
+
|
15
|
+
within_given_window(handle) do
|
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']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def resize_window_to(handle, width, height)
|
26
|
+
super
|
27
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
29
|
+
|
30
|
+
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
31
|
+
# and raises unnecessary error. Wait a bit and try again.
|
32
|
+
sleep 0.25
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset!
|
37
|
+
return super if edgedriver_version < 75
|
38
|
+
# Use instance variable directly so we avoid starting the browser just to reset the session
|
39
|
+
return unless @browser
|
40
|
+
|
41
|
+
switch_to_window(window_handles.first)
|
42
|
+
window_handles.slice(1..).each { |win| close_window(win) }
|
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
|
+
def download_path=(path)
|
58
|
+
if @browser.respond_to?(:download_path=)
|
59
|
+
@browser.download_path = path
|
60
|
+
else
|
61
|
+
# Not yet implemented in seleniun-webdriver for edge so do it ourselves
|
62
|
+
execute_cdp('Page.setDownloadBehavior', behavior: 'allow', downloadPath: path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def storage_types_to_clear
|
69
|
+
types = ['cookies']
|
70
|
+
types << 'local_storage' if clear_all_storage?
|
71
|
+
types.join(',')
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear_all_storage?
|
75
|
+
storage_clears.none? false
|
76
|
+
end
|
77
|
+
|
78
|
+
def uniform_storage_clear?
|
79
|
+
storage_clears.uniq { |s| s == false }.length <= 1
|
80
|
+
end
|
81
|
+
|
82
|
+
def storage_clears
|
83
|
+
options.values_at(:clear_session_storage, :clear_local_storage)
|
84
|
+
end
|
85
|
+
|
86
|
+
def clear_storage
|
87
|
+
# Edgedriver crashes if attempt to clear storage on about:blank
|
88
|
+
url = current_url
|
89
|
+
super unless url.nil? || url.start_with?('about:')
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete_all_cookies
|
93
|
+
return super if edgedriver_version < 75
|
94
|
+
|
95
|
+
execute_cdp('Network.clearBrowserCookies')
|
96
|
+
rescue *cdp_unsupported_errors
|
97
|
+
# If the CDP clear isn't supported do original limited clear
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
def cdp_unsupported_errors
|
102
|
+
@cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
|
103
|
+
end
|
104
|
+
|
105
|
+
def execute_cdp(cmd, params = {})
|
106
|
+
if browser.respond_to? :execute_cdp
|
107
|
+
browser.execute_cdp(cmd, **params)
|
108
|
+
else
|
109
|
+
args = { cmd: cmd, params: params }
|
110
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/ms/cdp/execute", args)
|
111
|
+
result['value']
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def build_node(native_node, initial_cache = {})
|
116
|
+
::Capybara::Selenium::EdgeNode.new(self, native_node, initial_cache)
|
117
|
+
end
|
118
|
+
|
119
|
+
def edgedriver_version
|
120
|
+
@edgedriver_version ||= begin
|
121
|
+
caps = browser.capabilities
|
122
|
+
caps['msedge']&.fetch('msedgedriverVersion', nil).to_f
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Capybara::Selenium::Driver.register_specialization :edge, Capybara::Selenium::Driver::EdgeDriver
|
128
|
+
Capybara::Selenium::Driver.register_specialization :edge_chrome, Capybara::Selenium::Driver::EdgeDriver
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/nodes/firefox_node'
|
4
|
+
|
5
|
+
module Capybara::Selenium::Driver::FirefoxDriver
|
6
|
+
def self.extended(driver)
|
7
|
+
driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver
|
8
|
+
bridge = driver.send(:bridge)
|
9
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Capybara::Selenium::Driver::W3CFirefoxDriver
|
14
|
+
class << self
|
15
|
+
def extended(driver)
|
16
|
+
require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(driver.browser)
|
17
|
+
driver.options[:native_displayed] = false if driver.options[:native_displayed].nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def pause_broken?(sel_driver)
|
21
|
+
sel_driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def resize_window_to(handle, width, height)
|
26
|
+
within_given_window(handle) do
|
27
|
+
# Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643
|
28
|
+
if window_size(handle) == [width, height]
|
29
|
+
{}
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset!
|
37
|
+
# Use instance variable directly so we avoid starting the browser just to reset the session
|
38
|
+
return unless @browser
|
39
|
+
|
40
|
+
if browser_version >= 68
|
41
|
+
begin
|
42
|
+
# Firefox 68 hangs if we try to switch windows while a modal is visible
|
43
|
+
browser.switch_to.alert&.dismiss
|
44
|
+
rescue Selenium::WebDriver::Error::NoSuchAlertError
|
45
|
+
# Swallow
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
switch_to_window(window_handles.first)
|
50
|
+
window_handles.slice(1..).each { |win| close_window(win) }
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def refresh
|
55
|
+
# Accept any "will repost content" confirmation that occurs
|
56
|
+
accept_modal :confirm, wait: 0.1 do
|
57
|
+
super
|
58
|
+
end
|
59
|
+
rescue Capybara::ModalNotFound
|
60
|
+
# No modal was opened - page has refreshed - ignore
|
61
|
+
end
|
62
|
+
|
63
|
+
def switch_to_frame(frame)
|
64
|
+
return super unless frame == :parent
|
65
|
+
|
66
|
+
# geckodriver/firefox has an issue if the current frame is removed from within it
|
67
|
+
# so we have to move to the default_content and iterate back through the frames
|
68
|
+
handles = @frame_handles[current_window_handle]
|
69
|
+
browser.switch_to.default_content
|
70
|
+
handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def build_node(native_node, initial_cache = {})
|
76
|
+
::Capybara::Selenium::FirefoxNode.new(self, native_node, initial_cache)
|
77
|
+
end
|
78
|
+
|
79
|
+
def browser_version
|
80
|
+
browser.capabilities[:browser_version].to_f
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Capybara::Selenium::Driver.register_specialization :firefox, Capybara::Selenium::Driver::FirefoxDriver
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/nodes/ie_node'
|
4
|
+
|
5
|
+
module Capybara::Selenium::Driver::InternetExplorerDriver
|
6
|
+
def switch_to_frame(frame)
|
7
|
+
return super unless frame == :parent
|
8
|
+
|
9
|
+
# iedriverserver has an issue if the current frame is removed from within it
|
10
|
+
# so we have to move to the default_content and iterate back through the frames
|
11
|
+
handles = @frame_handles[current_window_handle]
|
12
|
+
browser.switch_to.default_content
|
13
|
+
handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_node(native_node, initial_cache = {})
|
19
|
+
::Capybara::Selenium::IENode.new(self, native_node, initial_cache)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Capybara::Selenium
|
24
|
+
Driver.register_specialization :ie, Driver::InternetExplorerDriver
|
25
|
+
Driver.register_specialization :internet_explorer, Driver::InternetExplorerDriver
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/nodes/safari_node'
|
4
|
+
|
5
|
+
module Capybara::Selenium::Driver::SafariDriver
|
6
|
+
def switch_to_frame(frame)
|
7
|
+
return super unless frame == :parent
|
8
|
+
|
9
|
+
# safaridriver/safari has an issue where switch_to_frame(:parent)
|
10
|
+
# behaves like switch_to_frame(:top)
|
11
|
+
handles = @frame_handles[current_window_handle]
|
12
|
+
browser.switch_to.default_content
|
13
|
+
handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh.native) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_node(native_node, initial_cache = {})
|
19
|
+
::Capybara::Selenium::SafariNode.new(self, native_node, initial_cache)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Capybara::Selenium::Driver.register_specialization(/^(safari|Safari_Technology_Preview)$/,
|
24
|
+
Capybara::Selenium::Driver::SafariDriver)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Capybara::Selenium::Node
|
4
|
+
module FileInputClickEmulation
|
5
|
+
def click(keys = [], **options)
|
6
|
+
super
|
7
|
+
rescue Selenium::WebDriver::Error::InvalidArgumentError
|
8
|
+
return emulate_click if attaching_file? && visible_file_field?
|
9
|
+
|
10
|
+
raise
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def visible_file_field?
|
16
|
+
(attrs(:tagName, :type).map { |val| val&.downcase } == %w[input file]) && visible?
|
17
|
+
end
|
18
|
+
|
19
|
+
def attaching_file?
|
20
|
+
caller_locations.any? { |cl| cl.base_label == 'attach_file' }
|
21
|
+
end
|
22
|
+
|
23
|
+
def emulate_click
|
24
|
+
driver.execute_script(<<~JS, self)
|
25
|
+
arguments[0].dispatchEvent(
|
26
|
+
new MouseEvent('click', {
|
27
|
+
view: window,
|
28
|
+
bubbles: true,
|
29
|
+
cancelable: true
|
30
|
+
}));
|
31
|
+
JS
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Selenium
|
5
|
+
module Find
|
6
|
+
def find_xpath(selector, uses_visibility: false, styles: nil, position: false, **_options)
|
7
|
+
find_by(:xpath, selector, uses_visibility: uses_visibility, texts: [], styles: styles, position: position)
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_css(selector, uses_visibility: false, texts: [], styles: nil, position: false, **_options)
|
11
|
+
find_by(:css, selector, uses_visibility: uses_visibility, texts: texts, styles: styles, position: position)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def find_by(format, selector, uses_visibility:, texts:, styles:, position:)
|
17
|
+
els = find_context.find_elements(format, selector)
|
18
|
+
hints = []
|
19
|
+
|
20
|
+
if (els.size > 2) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
|
21
|
+
els = filter_by_text(els, texts) unless texts.empty?
|
22
|
+
hints = gather_hints(els, uses_visibility: uses_visibility, styles: styles, position: position)
|
23
|
+
end
|
24
|
+
els.map.with_index { |el, idx| build_node(el, hints[idx] || {}) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def gather_hints(elements, uses_visibility:, styles:, position:)
|
28
|
+
hints_js, functions = build_hints_js(uses_visibility, styles, position)
|
29
|
+
return [] unless functions.any?
|
30
|
+
|
31
|
+
(es_context.execute_script(hints_js, elements) || []).map! do |results|
|
32
|
+
hint = {}
|
33
|
+
hint[:style] = results.pop if functions.include?(:style_func)
|
34
|
+
hint[:position] = results.pop if functions.include?(:position_func)
|
35
|
+
hint[:visible] = results.pop if functions.include?(:vis_func)
|
36
|
+
hint
|
37
|
+
end
|
38
|
+
rescue ::Selenium::WebDriver::Error::StaleElementReferenceError,
|
39
|
+
::Capybara::NotSupportedByDriverError
|
40
|
+
# warn 'Unexpected Stale Element Error - skipping optimization'
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
|
44
|
+
def filter_by_text(elements, texts)
|
45
|
+
es_context.execute_script <<~JS, elements, texts
|
46
|
+
var texts = arguments[1];
|
47
|
+
return arguments[0].filter(function(el){
|
48
|
+
var content = el.textContent.toLowerCase();
|
49
|
+
return texts.every(function(txt){ return content.indexOf(txt.toLowerCase()) != -1 });
|
50
|
+
})
|
51
|
+
JS
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_hints_js(uses_visibility, styles, position)
|
55
|
+
functions = []
|
56
|
+
hints_js = +''
|
57
|
+
|
58
|
+
if uses_visibility && !is_displayed_atom.empty?
|
59
|
+
hints_js << <<~VISIBILITY_JS
|
60
|
+
var vis_func = #{is_displayed_atom};
|
61
|
+
VISIBILITY_JS
|
62
|
+
functions << :vis_func
|
63
|
+
end
|
64
|
+
|
65
|
+
if position
|
66
|
+
hints_js << <<~POSITION_JS
|
67
|
+
var position_func = function(el){
|
68
|
+
return el.getBoundingClientRect();
|
69
|
+
};
|
70
|
+
POSITION_JS
|
71
|
+
functions << :position_func
|
72
|
+
end
|
73
|
+
|
74
|
+
if styles.is_a? Hash
|
75
|
+
hints_js << <<~STYLE_JS
|
76
|
+
var style_func = function(el){
|
77
|
+
var el_styles = window.getComputedStyle(el);
|
78
|
+
return #{styles.keys.map(&:to_s)}.reduce(function(res, style){
|
79
|
+
res[style] = el_styles[style];
|
80
|
+
return res;
|
81
|
+
}, {});
|
82
|
+
};
|
83
|
+
STYLE_JS
|
84
|
+
functions << :style_func
|
85
|
+
end
|
86
|
+
|
87
|
+
hints_js << <<~EACH_JS
|
88
|
+
return arguments[0].map(function(el){
|
89
|
+
return [#{functions.join(',')}].map(function(fn){ return fn.call(null, el) });
|
90
|
+
});
|
91
|
+
EACH_JS
|
92
|
+
|
93
|
+
[hints_js, functions]
|
94
|
+
end
|
95
|
+
|
96
|
+
def es_context
|
97
|
+
respond_to?(:execute_script) ? self : driver
|
98
|
+
end
|
99
|
+
|
100
|
+
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
101
|
+
@@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
|
102
|
+
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
103
|
+
rescue StandardError
|
104
|
+
# If the atom doesn't exist or other error
|
105
|
+
''
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|