capybara 2.5.0 → 2.18.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 +5 -5
- data/.yard/templates_custom/default/class/html/selectors.erb +38 -0
- data/.yard/templates_custom/default/class/html/setup.rb +17 -0
- data/.yard/yard_extensions.rb +78 -0
- data/.yardopts +1 -0
- data/History.md +413 -10
- data/License.txt +1 -1
- data/README.md +237 -130
- data/lib/capybara/config.rb +132 -0
- data/lib/capybara/cucumber.rb +3 -1
- data/lib/capybara/driver/base.rb +27 -6
- data/lib/capybara/driver/node.rb +14 -5
- data/lib/capybara/dsl.rb +2 -3
- data/lib/capybara/helpers.rb +13 -65
- data/lib/capybara/minitest/spec.rb +177 -0
- data/lib/capybara/minitest.rb +278 -0
- data/lib/capybara/node/actions.rb +180 -24
- data/lib/capybara/node/base.rb +17 -5
- data/lib/capybara/node/document.rb +5 -0
- data/lib/capybara/node/document_matchers.rb +15 -14
- data/lib/capybara/node/element.rb +55 -7
- data/lib/capybara/node/finders.rb +179 -67
- data/lib/capybara/node/matchers.rb +301 -105
- data/lib/capybara/node/simple.rb +15 -4
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/base_query.rb +69 -3
- data/lib/capybara/queries/current_path_query.rb +17 -8
- data/lib/capybara/queries/match_query.rb +19 -0
- data/lib/capybara/queries/selector_query.rb +251 -0
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/queries/text_query.rb +67 -16
- data/lib/capybara/queries/title_query.rb +4 -2
- data/lib/capybara/query.rb +3 -131
- data/lib/capybara/rack_test/browser.rb +14 -5
- data/lib/capybara/rack_test/css_handlers.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +15 -8
- data/lib/capybara/rack_test/form.rb +34 -12
- data/lib/capybara/rack_test/node.rb +29 -12
- data/lib/capybara/rails.rb +3 -3
- data/lib/capybara/result.rb +104 -9
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/features.rb +17 -6
- data/lib/capybara/rspec/matcher_proxies.rb +45 -0
- data/lib/capybara/rspec/matchers.rb +199 -80
- data/lib/capybara/rspec.rb +4 -2
- data/lib/capybara/selector/css.rb +30 -0
- data/lib/capybara/selector/filter.rb +20 -0
- data/lib/capybara/selector/filter_set.rb +74 -0
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +276 -0
- data/lib/capybara/selector.rb +452 -157
- data/lib/capybara/selenium/driver.rb +282 -81
- data/lib/capybara/selenium/node.rb +144 -46
- data/lib/capybara/server.rb +59 -16
- data/lib/capybara/session/config.rb +114 -0
- data/lib/capybara/session/matchers.rb +29 -19
- data/lib/capybara/session.rb +378 -143
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +13 -791
- data/lib/capybara/spec/public/jquery.js +4 -9045
- data/lib/capybara/spec/public/test.js +45 -11
- data/lib/capybara/spec/session/accept_alert_spec.rb +30 -7
- data/lib/capybara/spec/session/accept_confirm_spec.rb +14 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +35 -6
- data/lib/capybara/spec/session/all_spec.rb +45 -32
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +110 -0
- data/lib/capybara/spec/session/assert_current_path.rb +15 -2
- data/lib/capybara/spec/session/assert_selector.rb +29 -28
- data/lib/capybara/spec/session/assert_text.rb +59 -20
- data/lib/capybara/spec/session/assert_title.rb +25 -11
- data/lib/capybara/spec/session/attach_file_spec.rb +42 -4
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +90 -14
- data/lib/capybara/spec/session/choose_spec.rb +31 -5
- data/lib/capybara/spec/session/click_button_spec.rb +20 -9
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +15 -9
- data/lib/capybara/spec/session/click_link_spec.rb +39 -15
- data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
- data/lib/capybara/spec/session/current_url_spec.rb +12 -3
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +6 -5
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +4 -3
- data/lib/capybara/spec/session/element/assert_match_selector.rb +36 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +23 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +106 -0
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +23 -1
- data/lib/capybara/spec/session/execute_script_spec.rb +22 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +50 -32
- data/lib/capybara/spec/session/find_button_spec.rb +43 -2
- data/lib/capybara/spec/session/find_by_id_spec.rb +3 -2
- data/lib/capybara/spec/session/find_field_spec.rb +42 -6
- data/lib/capybara/spec/session/find_link_spec.rb +22 -3
- data/lib/capybara/spec/session/find_spec.rb +103 -57
- data/lib/capybara/spec/session/first_spec.rb +34 -18
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +103 -0
- data/lib/capybara/spec/session/{within_frame_spec.rb → frame/within_frame_spec.rb} +44 -2
- data/lib/capybara/spec/session/go_back_spec.rb +2 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_button_spec.rb +17 -8
- data/lib/capybara/spec/session/has_css_spec.rb +85 -73
- data/lib/capybara/spec/session/has_current_path_spec.rb +91 -7
- data/lib/capybara/spec/session/has_field_spec.rb +93 -58
- data/lib/capybara/spec/session/has_link_spec.rb +9 -8
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +159 -59
- data/lib/capybara/spec/session/has_selector_spec.rb +64 -28
- data/lib/capybara/spec/session/has_table_spec.rb +1 -0
- data/lib/capybara/spec/session/has_text_spec.rb +27 -12
- data/lib/capybara/spec/session/has_title_spec.rb +22 -4
- data/lib/capybara/spec/session/has_xpath_spec.rb +32 -29
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +4 -3
- data/lib/capybara/spec/session/node_spec.rb +198 -38
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +46 -5
- data/lib/capybara/spec/session/response_code.rb +2 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -5
- data/lib/capybara/spec/session/save_page_spec.rb +34 -2
- data/lib/capybara/spec/session/save_screenshot_spec.rb +31 -1
- data/lib/capybara/spec/session/screenshot_spec.rb +4 -2
- data/lib/capybara/spec/session/select_spec.rb +34 -32
- data/lib/capybara/spec/session/selectors_spec.rb +65 -0
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/text_spec.rb +4 -4
- data/lib/capybara/spec/session/title_spec.rb +2 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +42 -2
- data/lib/capybara/spec/session/unselect_spec.rb +17 -16
- data/lib/capybara/spec/session/visit_spec.rb +77 -2
- data/lib/capybara/spec/session/window/become_closed_spec.rb +12 -11
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +16 -11
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +7 -4
- data/lib/capybara/spec/session/window/window_spec.rb +36 -29
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +31 -7
- data/lib/capybara/spec/session/within_spec.rb +14 -6
- data/lib/capybara/spec/spec_helper.rb +37 -4
- data/lib/capybara/spec/test_app.rb +15 -3
- data/lib/capybara/spec/views/buttons.erb +1 -0
- data/lib/capybara/spec/views/fieldsets.erb +2 -1
- data/lib/capybara/spec/views/form.erb +169 -9
- data/lib/capybara/spec/views/frame_child.erb +10 -2
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +3 -2
- data/lib/capybara/spec/views/frame_two.erb +2 -1
- data/lib/capybara/spec/views/header_links.erb +1 -0
- data/lib/capybara/spec/views/host_links.erb +1 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/path.erb +1 -0
- data/lib/capybara/spec/views/popup_one.erb +1 -0
- data/lib/capybara/spec/views/popup_two.erb +1 -0
- data/lib/capybara/spec/views/postback.erb +2 -1
- data/lib/capybara/spec/views/tables.erb +1 -0
- data/lib/capybara/spec/views/with_base_tag.erb +1 -0
- data/lib/capybara/spec/views/with_count.erb +2 -1
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +7 -1
- data/lib/capybara/spec/views/with_html.erb +40 -2
- data/lib/capybara/spec/views/with_html_entities.erb +1 -0
- data/lib/capybara/spec/views/with_js.erb +32 -1
- data/lib/capybara/spec/views/with_scope.erb +1 -0
- data/lib/capybara/spec/views/with_simple_html.erb +2 -1
- data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
- data/lib/capybara/spec/views/with_title.erb +2 -1
- data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
- data/lib/capybara/spec/views/with_windows.erb +7 -0
- data/lib/capybara/spec/views/within_frames.erb +3 -2
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +20 -3
- data/lib/capybara.rb +189 -93
- data/spec/basic_node_spec.rb +7 -6
- data/spec/capybara_spec.rb +90 -4
- data/spec/dsl_spec.rb +3 -1
- data/spec/filter_set_spec.rb +28 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -1
- data/spec/minitest_spec.rb +130 -0
- data/spec/minitest_spec_spec.rb +135 -0
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/rack_test_spec.rb +50 -7
- data/spec/result_spec.rb +76 -0
- data/spec/rspec/features_spec.rb +21 -8
- data/spec/rspec/scenarios_spec.rb +21 -0
- data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +160 -54
- data/spec/rspec/views_spec.rb +5 -0
- data/spec/rspec_matchers_spec.rb +46 -0
- data/spec/rspec_spec.rb +79 -1
- data/spec/selector_spec.rb +199 -0
- data/spec/selenium_spec_chrome.rb +54 -9
- data/spec/selenium_spec_firefox.rb +68 -0
- data/spec/selenium_spec_marionette.rb +127 -0
- data/spec/server_spec.rb +102 -14
- data/spec/session_spec.rb +54 -0
- data/spec/shared_selenium_session.rb +215 -0
- data/spec/spec_helper.rb +7 -0
- metadata +140 -15
- data/spec/selenium_spec.rb +0 -128
|
@@ -1,17 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require "uri"
|
|
2
3
|
|
|
3
4
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
5
|
+
|
|
4
6
|
DEFAULT_OPTIONS = {
|
|
5
|
-
:browser => :firefox
|
|
7
|
+
:browser => :firefox,
|
|
8
|
+
clear_local_storage: false,
|
|
9
|
+
clear_session_storage: false
|
|
6
10
|
}
|
|
7
|
-
SPECIAL_OPTIONS = [:browser]
|
|
11
|
+
SPECIAL_OPTIONS = [:browser, :clear_local_storage, :clear_session_storage]
|
|
8
12
|
|
|
9
13
|
attr_reader :app, :options
|
|
10
14
|
|
|
11
15
|
def browser
|
|
12
16
|
unless @browser
|
|
13
|
-
|
|
17
|
+
if firefox?
|
|
18
|
+
options[:desired_capabilities] ||= {}
|
|
19
|
+
options[:desired_capabilities].merge!({ unexpectedAlertBehaviour: "ignore" })
|
|
20
|
+
end
|
|
14
21
|
|
|
22
|
+
@processed_options = options.reject { |key,_val| SPECIAL_OPTIONS.include?(key) }
|
|
23
|
+
@browser = Selenium::WebDriver.for(options[:browser], @processed_options)
|
|
24
|
+
|
|
25
|
+
@w3c = ((defined?(Selenium::WebDriver::Remote::W3CCapabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3CCapabilities)) ||
|
|
26
|
+
(defined?(Selenium::WebDriver::Remote::W3C::Capabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)))
|
|
15
27
|
main = Process.pid
|
|
16
28
|
at_exit do
|
|
17
29
|
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
|
@@ -24,16 +36,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
24
36
|
end
|
|
25
37
|
|
|
26
38
|
def initialize(app, options={})
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
rescue LoadError => e
|
|
30
|
-
if e.message =~ /selenium-webdriver/
|
|
31
|
-
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."
|
|
32
|
-
else
|
|
33
|
-
raise e
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
39
|
+
load_selenium
|
|
40
|
+
@session = nil
|
|
37
41
|
@app = app
|
|
38
42
|
@browser = nil
|
|
39
43
|
@exit_status = nil
|
|
@@ -45,6 +49,13 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
45
49
|
browser.navigate.to(path)
|
|
46
50
|
end
|
|
47
51
|
|
|
52
|
+
def refresh
|
|
53
|
+
accept_modal(nil, wait: 0.1) do
|
|
54
|
+
browser.navigate.refresh
|
|
55
|
+
end
|
|
56
|
+
rescue Capybara::ModalNotFound
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
def go_back
|
|
49
60
|
browser.navigate.back
|
|
50
61
|
end
|
|
@@ -76,36 +87,72 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
76
87
|
def wait?; true; end
|
|
77
88
|
def needs_server?; true; end
|
|
78
89
|
|
|
79
|
-
def execute_script(script)
|
|
80
|
-
browser.execute_script
|
|
90
|
+
def execute_script(script, *args)
|
|
91
|
+
browser.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
|
|
81
92
|
end
|
|
82
93
|
|
|
83
|
-
def evaluate_script(script)
|
|
84
|
-
|
|
94
|
+
def evaluate_script(script, *args)
|
|
95
|
+
result = execute_script("return #{script}", *args)
|
|
96
|
+
unwrap_script_result(result)
|
|
85
97
|
end
|
|
86
98
|
|
|
87
|
-
def
|
|
99
|
+
def evaluate_async_script(script, *args)
|
|
100
|
+
browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
|
|
101
|
+
result = browser.execute_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
|
|
102
|
+
unwrap_script_result(result)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def save_screenshot(path, _options={})
|
|
88
106
|
browser.save_screenshot(path)
|
|
89
107
|
end
|
|
90
108
|
|
|
91
109
|
def reset!
|
|
92
110
|
# Use instance variable directly so we avoid starting the browser just to reset the session
|
|
93
111
|
if @browser
|
|
112
|
+
navigated = false
|
|
113
|
+
start_time = Capybara::Helpers.monotonic_time
|
|
94
114
|
begin
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
if !navigated
|
|
116
|
+
# Only trigger a navigation if we haven't done it already, otherwise it
|
|
117
|
+
# can trigger an endless series of unload modals
|
|
118
|
+
begin
|
|
119
|
+
@browser.manage.delete_all_cookies
|
|
120
|
+
if options[:clear_session_storage]
|
|
121
|
+
if @browser.respond_to? :session_storage
|
|
122
|
+
@browser.session_storage.clear
|
|
123
|
+
else
|
|
124
|
+
warn "sessionStorage clear requested but is not available for this driver"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
if options[:clear_local_storage]
|
|
128
|
+
if @browser.respond_to? :local_storage
|
|
129
|
+
@browser.local_storage.clear
|
|
130
|
+
else
|
|
131
|
+
warn "localStorage clear requested but is not available for this driver"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
rescue Selenium::WebDriver::Error::UnhandledError
|
|
135
|
+
# delete_all_cookies fails when we've previously gone
|
|
136
|
+
# to about:blank, so we rescue this error and do nothing
|
|
137
|
+
# instead.
|
|
138
|
+
end
|
|
139
|
+
@browser.navigate.to("about:blank")
|
|
140
|
+
end
|
|
141
|
+
navigated = true
|
|
142
|
+
|
|
143
|
+
#Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
|
144
|
+
until find_xpath("/html/body/*").empty? do
|
|
145
|
+
raise Capybara::ExpectationNotMet.new('Timed out waiting for Selenium session reset') if (Capybara::Helpers.monotonic_time - start_time) >= 10
|
|
146
|
+
sleep 0.05
|
|
100
147
|
end
|
|
101
|
-
|
|
102
|
-
rescue Selenium::WebDriver::Error::UnhandledAlertError
|
|
148
|
+
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
|
103
149
|
# This error is thrown if an unhandled alert is on the page
|
|
104
150
|
# Firefox appears to automatically dismiss this alert, chrome does not
|
|
105
151
|
# We'll try to accept it
|
|
106
152
|
begin
|
|
107
153
|
@browser.switch_to.alert.accept
|
|
108
|
-
|
|
154
|
+
sleep 0.25 # allow time for the modal to be handled
|
|
155
|
+
rescue modal_error
|
|
109
156
|
# The alert is now gone - nothing to do
|
|
110
157
|
end
|
|
111
158
|
# try cleaning up the browser again
|
|
@@ -114,35 +161,21 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
114
161
|
end
|
|
115
162
|
end
|
|
116
163
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# @overload within_frame(element)
|
|
126
|
-
# @param [Capybara::Node::Base] a_node frame element
|
|
127
|
-
#
|
|
128
|
-
def within_frame(frame_handle)
|
|
129
|
-
frame_handle = frame_handle.native if frame_handle.is_a?(Capybara::Node::Base)
|
|
130
|
-
if !browser.switch_to.respond_to?(:parent_frame)
|
|
131
|
-
# Selenium Webdriver < 2.43 doesnt support moving back to the parent
|
|
132
|
-
@frame_handles[browser.window_handle] ||= []
|
|
133
|
-
@frame_handles[browser.window_handle] << frame_handle
|
|
134
|
-
end
|
|
135
|
-
browser.switch_to.frame(frame_handle)
|
|
136
|
-
yield
|
|
137
|
-
ensure
|
|
138
|
-
if browser.switch_to.respond_to?(:parent_frame)
|
|
139
|
-
browser.switch_to.parent_frame
|
|
140
|
-
else
|
|
141
|
-
# There doesnt appear to be any way in Selenium Webdriver < 2.43 to move back to a parent frame
|
|
142
|
-
# other than going back to the root and then reiterating down
|
|
164
|
+
def switch_to_frame(frame)
|
|
165
|
+
case frame
|
|
166
|
+
when :top
|
|
167
|
+
@frame_handles[browser.window_handle] = []
|
|
168
|
+
browser.switch_to.default_content
|
|
169
|
+
when :parent
|
|
170
|
+
# would love to use browser.switch_to.parent_frame here
|
|
171
|
+
# but it has an issue if the current frame is removed from within it
|
|
143
172
|
@frame_handles[browser.window_handle].pop
|
|
144
173
|
browser.switch_to.default_content
|
|
145
174
|
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
|
|
175
|
+
else
|
|
176
|
+
@frame_handles[browser.window_handle] ||= []
|
|
177
|
+
@frame_handles[browser.window_handle] << frame.native
|
|
178
|
+
browser.switch_to.frame(frame.native)
|
|
146
179
|
end
|
|
147
180
|
end
|
|
148
181
|
|
|
@@ -159,7 +192,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
159
192
|
|
|
160
193
|
def resize_window_to(handle, width, height)
|
|
161
194
|
within_given_window(handle) do
|
|
162
|
-
|
|
195
|
+
# Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643
|
|
196
|
+
if marionette? && (window_size(handle) == [width, height])
|
|
197
|
+
{}
|
|
198
|
+
else
|
|
199
|
+
browser.manage.window.resize_to(width, height)
|
|
200
|
+
end
|
|
163
201
|
end
|
|
164
202
|
end
|
|
165
203
|
|
|
@@ -188,39 +226,23 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
188
226
|
browser.switch_to.window handle
|
|
189
227
|
end
|
|
190
228
|
|
|
191
|
-
# @api private
|
|
192
|
-
def find_window(locator)
|
|
193
|
-
handles = browser.window_handles
|
|
194
|
-
return locator if handles.include? locator
|
|
195
|
-
|
|
196
|
-
original_handle = browser.window_handle
|
|
197
|
-
handles.each do |handle|
|
|
198
|
-
switch_to_window(handle)
|
|
199
|
-
if (locator == browser.execute_script("return window.name") ||
|
|
200
|
-
browser.title.include?(locator) ||
|
|
201
|
-
browser.current_url.include?(locator))
|
|
202
|
-
switch_to_window(original_handle)
|
|
203
|
-
return handle
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
|
|
207
|
-
end
|
|
208
|
-
|
|
209
229
|
def within_window(locator)
|
|
210
230
|
handle = find_window(locator)
|
|
211
231
|
browser.switch_to.window(handle) { yield }
|
|
212
232
|
end
|
|
213
233
|
|
|
214
|
-
def accept_modal(
|
|
234
|
+
def accept_modal(_type, options={})
|
|
215
235
|
yield if block_given?
|
|
216
236
|
modal = find_modal(options)
|
|
237
|
+
|
|
217
238
|
modal.send_keys options[:with] if options[:with]
|
|
239
|
+
|
|
218
240
|
message = modal.text
|
|
219
241
|
modal.accept
|
|
220
242
|
message
|
|
221
243
|
end
|
|
222
244
|
|
|
223
|
-
def dismiss_modal(
|
|
245
|
+
def dismiss_modal(_type, options={})
|
|
224
246
|
yield if block_given?
|
|
225
247
|
modal = find_modal(options)
|
|
226
248
|
message = modal.text
|
|
@@ -230,29 +252,139 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
230
252
|
|
|
231
253
|
def quit
|
|
232
254
|
@browser.quit if @browser
|
|
233
|
-
rescue Errno::ECONNREFUSED
|
|
255
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
234
256
|
# Browser must have already gone
|
|
257
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
258
|
+
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
259
|
+
# probably already gone but not sure - so warn
|
|
260
|
+
warn "Ignoring Selenium UnknownError during driver quit: #{e.message}"
|
|
261
|
+
end
|
|
235
262
|
ensure
|
|
236
263
|
@browser = nil
|
|
237
264
|
end
|
|
238
265
|
|
|
239
266
|
def invalid_element_errors
|
|
240
|
-
[Selenium::WebDriver::Error::StaleElementReferenceError,
|
|
241
|
-
Selenium::WebDriver::Error::UnhandledError,
|
|
242
|
-
Selenium::WebDriver::Error::ElementNotVisibleError,
|
|
243
|
-
Selenium::WebDriver::Error::InvalidSelectorError
|
|
267
|
+
[::Selenium::WebDriver::Error::StaleElementReferenceError,
|
|
268
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
|
269
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
|
270
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a race condition that can occur with chromedriver and #go_back/#go_forward
|
|
271
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
|
272
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
|
273
|
+
::Selenium::WebDriver::Error::InvalidElementStateError,
|
|
274
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
|
275
|
+
]
|
|
244
276
|
end
|
|
245
277
|
|
|
246
278
|
def no_such_window_error
|
|
247
279
|
Selenium::WebDriver::Error::NoSuchWindowError
|
|
248
280
|
end
|
|
249
281
|
|
|
282
|
+
# @api private
|
|
283
|
+
def marionette?
|
|
284
|
+
firefox? && browser && @w3c
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# @api private
|
|
288
|
+
def firefox?
|
|
289
|
+
browser_name == "firefox"
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# @api private
|
|
293
|
+
def chrome?
|
|
294
|
+
browser_name == "chrome"
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# @deprecated This method is being removed
|
|
250
298
|
def browser_initialized?
|
|
251
|
-
!@browser.nil?
|
|
299
|
+
super && !@browser.nil?
|
|
252
300
|
end
|
|
253
301
|
|
|
254
302
|
private
|
|
255
303
|
|
|
304
|
+
# @api private
|
|
305
|
+
def browser_name
|
|
306
|
+
options[:browser].to_s
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def modal_error
|
|
310
|
+
if defined?(Selenium::WebDriver::Error::NoSuchAlertError)
|
|
311
|
+
Selenium::WebDriver::Error::NoSuchAlertError
|
|
312
|
+
else
|
|
313
|
+
Selenium::WebDriver::Error::NoAlertPresentError
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def find_window(locator)
|
|
318
|
+
handles = browser.window_handles
|
|
319
|
+
return locator if handles.include? locator
|
|
320
|
+
|
|
321
|
+
original_handle = browser.window_handle
|
|
322
|
+
handles.each do |handle|
|
|
323
|
+
switch_to_window(handle)
|
|
324
|
+
if (locator == browser.execute_script("return window.name") ||
|
|
325
|
+
browser.title.include?(locator) ||
|
|
326
|
+
browser.current_url.include?(locator))
|
|
327
|
+
switch_to_window(original_handle)
|
|
328
|
+
return handle
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def insert_modal_handlers(accept, response_text)
|
|
335
|
+
prompt_response = if accept
|
|
336
|
+
if response_text.nil?
|
|
337
|
+
"default_text"
|
|
338
|
+
else
|
|
339
|
+
"'#{response_text.gsub("\\", "\\\\\\").gsub("'", "\\\\'")}'"
|
|
340
|
+
end
|
|
341
|
+
else
|
|
342
|
+
'null'
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
script = <<-JS
|
|
346
|
+
if (typeof window.capybara === 'undefined') {
|
|
347
|
+
window.capybara = {
|
|
348
|
+
modal_handlers: [],
|
|
349
|
+
current_modal_status: function() {
|
|
350
|
+
return [this.modal_handlers[0].called, this.modal_handlers[0].modal_text];
|
|
351
|
+
},
|
|
352
|
+
add_handler: function(handler) {
|
|
353
|
+
this.modal_handlers.unshift(handler);
|
|
354
|
+
},
|
|
355
|
+
remove_handler: function(handler) {
|
|
356
|
+
window.alert = handler.alert;
|
|
357
|
+
window.confirm = handler.confirm;
|
|
358
|
+
window.prompt = handler.prompt;
|
|
359
|
+
},
|
|
360
|
+
handler_called: function(handler, str) {
|
|
361
|
+
handler.called = true;
|
|
362
|
+
handler.modal_text = str;
|
|
363
|
+
this.remove_handler(handler);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
var modal_handler = {
|
|
369
|
+
prompt: window.prompt,
|
|
370
|
+
confirm: window.confirm,
|
|
371
|
+
alert: window.alert,
|
|
372
|
+
called: false
|
|
373
|
+
}
|
|
374
|
+
window.capybara.add_handler(modal_handler);
|
|
375
|
+
|
|
376
|
+
window.alert = window.confirm = function(str = "") {
|
|
377
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
|
378
|
+
return #{accept ? 'true' : 'false'};
|
|
379
|
+
}
|
|
380
|
+
window.prompt = function(str = "", default_text = "") {
|
|
381
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
|
382
|
+
return #{prompt_response};
|
|
383
|
+
}
|
|
384
|
+
JS
|
|
385
|
+
execute_script script
|
|
386
|
+
end
|
|
387
|
+
|
|
256
388
|
def within_given_window(handle)
|
|
257
389
|
original_handle = self.current_window_handle
|
|
258
390
|
if handle == original_handle
|
|
@@ -269,8 +401,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
269
401
|
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
|
270
402
|
# Actual wait time may be longer than specified
|
|
271
403
|
wait = Selenium::WebDriver::Wait.new(
|
|
272
|
-
timeout: (
|
|
273
|
-
ignore:
|
|
404
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
|
405
|
+
ignore: modal_error)
|
|
274
406
|
begin
|
|
275
407
|
wait.until do
|
|
276
408
|
alert = @browser.switch_to.alert
|
|
@@ -282,4 +414,73 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
282
414
|
end
|
|
283
415
|
end
|
|
284
416
|
|
|
417
|
+
def find_headless_modal(options={})
|
|
418
|
+
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
|
419
|
+
# Actual wait time may be longer than specified
|
|
420
|
+
wait = Selenium::WebDriver::Wait.new(
|
|
421
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
|
422
|
+
ignore: modal_error)
|
|
423
|
+
begin
|
|
424
|
+
wait.until do
|
|
425
|
+
called, alert_text = evaluate_script('window.capybara && window.capybara.current_modal_status()')
|
|
426
|
+
if called
|
|
427
|
+
execute_script('window.capybara && window.capybara.modal_handlers.shift()')
|
|
428
|
+
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
|
|
429
|
+
if alert_text.match(regexp)
|
|
430
|
+
alert_text
|
|
431
|
+
else
|
|
432
|
+
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
|
|
433
|
+
end
|
|
434
|
+
elsif called.nil?
|
|
435
|
+
# page changed so modal_handler data has gone away
|
|
436
|
+
warn "Can't verify modal text when page change occurs - ignoring" if options[:text]
|
|
437
|
+
""
|
|
438
|
+
else
|
|
439
|
+
nil
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
rescue Selenium::WebDriver::Error::TimeOutError
|
|
443
|
+
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def silenced_unknown_error_message?(msg)
|
|
448
|
+
silenced_unknown_error_messages.any? { |r| msg =~ r }
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def silenced_unknown_error_messages
|
|
452
|
+
[ /Error communicating with the remote browser/ ]
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def unwrap_script_result(arg)
|
|
456
|
+
case arg
|
|
457
|
+
when Array
|
|
458
|
+
arg.map { |e| unwrap_script_result(e) }
|
|
459
|
+
when Hash
|
|
460
|
+
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
|
461
|
+
when Selenium::WebDriver::Element
|
|
462
|
+
Capybara::Selenium::Node.new(self, arg)
|
|
463
|
+
else
|
|
464
|
+
arg
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def load_selenium
|
|
469
|
+
begin
|
|
470
|
+
require 'selenium-webdriver'
|
|
471
|
+
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
|
472
|
+
if !defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
|
473
|
+
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
|
474
|
+
end
|
|
475
|
+
if !defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
|
476
|
+
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
|
477
|
+
end
|
|
478
|
+
rescue LoadError => e
|
|
479
|
+
if e.message =~ /selenium-webdriver/
|
|
480
|
+
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."
|
|
481
|
+
else
|
|
482
|
+
raise e
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
285
486
|
end
|