capybara 2.15.0 → 3.0.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/History.md +137 -2
- data/README.md +36 -25
- data/lib/capybara/config.rb +11 -57
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +19 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +19 -29
- data/lib/capybara/minitest/spec.rb +16 -13
- data/lib/capybara/minitest.rb +140 -137
- data/lib/capybara/node/actions.rb +68 -89
- data/lib/capybara/node/base.rb +11 -18
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +8 -8
- data/lib/capybara/node/element.rb +32 -42
- data/lib/capybara/node/finders.rb +64 -71
- data/lib/capybara/node/matchers.rb +50 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +12 -8
- data/lib/capybara/queries/base_query.rb +22 -18
- data/lib/capybara/queries/current_path_query.rb +12 -25
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +100 -96
- data/lib/capybara/queries/sibling_query.rb +5 -5
- data/lib/capybara/queries/text_query.rb +35 -35
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +15 -18
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +6 -10
- data/lib/capybara/rack_test/form.rb +52 -39
- data/lib/capybara/rack_test/node.rb +93 -63
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- data/lib/capybara/rspec/compound.rb +5 -10
- data/lib/capybara/rspec/features.rb +17 -48
- data/lib/capybara/rspec/matcher_proxies.rb +31 -15
- data/lib/capybara/rspec/matchers.rb +116 -58
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +18 -15
- data/lib/capybara/selector/filters/base.rb +7 -6
- data/lib/capybara/selector/filters/expression_filter.rb +6 -23
- data/lib/capybara/selector/filters/node_filter.rb +2 -12
- data/lib/capybara/selector/selector.rb +28 -34
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selenium/driver.rb +172 -163
- data/lib/capybara/selenium/node.rb +218 -104
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session/config.rb +47 -59
- data/lib/capybara/session/matchers.rb +23 -14
- data/lib/capybara/session.rb +175 -229
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/test.js +38 -6
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +30 -1
- data/lib/capybara/spec/session/all_spec.rb +31 -18
- data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
- data/lib/capybara/spec/session/assert_current_path.rb +12 -11
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +31 -23
- data/lib/capybara/spec/session/assert_title.rb +13 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +57 -29
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +7 -6
- data/lib/capybara/spec/session/choose_spec.rb +5 -4
- data/lib/capybara/spec/session/click_button_spec.rb +24 -32
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
- data/lib/capybara/spec/session/click_link_spec.rb +8 -7
- data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
- data/lib/capybara/spec/session/current_url_spec.rb +19 -8
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +5 -4
- data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
- data/lib/capybara/spec/session/find_button_spec.rb +4 -3
- data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
- data/lib/capybara/spec/session/find_field_spec.rb +9 -15
- data/lib/capybara/spec/session/find_link_spec.rb +6 -5
- data/lib/capybara/spec/session/find_spec.rb +37 -31
- data/lib/capybara/spec/session/first_spec.rb +60 -33
- 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 +2 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_button_spec.rb +2 -1
- data/lib/capybara/spec/session/has_css_spec.rb +3 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +49 -22
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +5 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +32 -31
- data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
- data/lib/capybara/spec/session/has_table_spec.rb +2 -1
- data/lib/capybara/spec/session/has_text_spec.rb +9 -13
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +107 -58
- data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
- data/lib/capybara/spec/session/refresh_spec.rb +6 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
- data/lib/capybara/spec/session/response_code.rb +1 -0
- 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 -11
- data/lib/capybara/spec/session/save_page_spec.rb +1 -17
- data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +21 -20
- data/lib/capybara/spec/session/selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +1 -1
- data/lib/capybara/spec/session/text_spec.rb +17 -3
- data/lib/capybara/spec/session/title_spec.rb +11 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
- data/lib/capybara/spec/session/unselect_spec.rb +7 -6
- data/lib/capybara/spec/session/visit_spec.rb +64 -3
- data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
- 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 +2 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +12 -12
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
- data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +36 -18
- data/lib/capybara/spec/test_app.rb +17 -9
- data/lib/capybara/spec/views/form.erb +7 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +5 -0
- data/lib/capybara/spec/views/with_html.erb +27 -1
- data/lib/capybara/spec/views/with_js.erb +11 -0
- data/lib/capybara/spec/views/within_frames.erb +4 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +6 -10
- data/lib/capybara.rb +29 -26
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +16 -69
- data/spec/dsl_spec.rb +5 -13
- data/spec/filter_set_spec.rb +5 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
- data/spec/minitest_spec.rb +13 -4
- data/spec/minitest_spec_spec.rb +12 -3
- data/spec/per_session_config_spec.rb +9 -8
- data/spec/rack_test_spec.rb +21 -20
- data/spec/result_spec.rb +17 -16
- data/spec/rspec/features_spec.rb +17 -14
- data/spec/rspec/scenarios_spec.rb +5 -7
- data/spec/rspec/shared_spec_matchers.rb +96 -99
- data/spec/rspec/views_spec.rb +2 -1
- data/spec/rspec_matchers_spec.rb +18 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +20 -11
- data/spec/selenium_spec_edge.rb +27 -0
- data/spec/selenium_spec_ie.rb +31 -0
- data/spec/selenium_spec_marionette.rb +38 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +82 -22
- data/spec/spec_helper.rb +3 -6
- metadata +76 -81
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "uri"
|
4
|
+
require "English"
|
3
5
|
|
4
6
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
5
|
-
|
6
7
|
DEFAULT_OPTIONS = {
|
7
|
-
:
|
8
|
+
browser: :firefox,
|
8
9
|
clear_local_storage: false,
|
9
10
|
clear_session_storage: false
|
10
|
-
}
|
11
|
-
SPECIAL_OPTIONS = [
|
11
|
+
}.freeze
|
12
|
+
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage].freeze
|
12
13
|
|
13
14
|
attr_reader :app, :options
|
14
15
|
|
@@ -16,19 +17,19 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
16
17
|
unless @browser
|
17
18
|
if firefox?
|
18
19
|
options[:desired_capabilities] ||= {}
|
19
|
-
options[:desired_capabilities]
|
20
|
+
options[:desired_capabilities][:unexpectedAlertBehaviour] = "ignore"
|
20
21
|
end
|
21
22
|
|
22
|
-
@processed_options = options.reject { |key,_val| SPECIAL_OPTIONS.include?(key) }
|
23
|
+
@processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
23
24
|
@browser = Selenium::WebDriver.for(options[:browser], @processed_options)
|
24
25
|
|
25
26
|
@w3c = ((defined?(Selenium::WebDriver::Remote::W3CCapabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3CCapabilities)) ||
|
26
27
|
(defined?(Selenium::WebDriver::Remote::W3C::Capabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)))
|
27
|
-
|
28
28
|
main = Process.pid
|
29
|
+
|
29
30
|
at_exit do
|
30
31
|
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
31
|
-
@exit_status =
|
32
|
+
@exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
|
32
33
|
quit if Process.pid == main
|
33
34
|
exit @exit_status if @exit_status # Force exit with stored status
|
34
35
|
end
|
@@ -36,26 +37,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
36
37
|
@browser
|
37
38
|
end
|
38
39
|
|
39
|
-
def initialize(app, options
|
40
|
+
def initialize(app, **options)
|
41
|
+
load_selenium
|
40
42
|
@session = nil
|
41
|
-
begin
|
42
|
-
require 'selenium-webdriver'
|
43
|
-
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
44
|
-
if !defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
45
|
-
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
46
|
-
end
|
47
|
-
if !defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
48
|
-
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
49
|
-
end
|
50
|
-
rescue LoadError => e
|
51
|
-
if e.message =~ /selenium-webdriver/
|
52
|
-
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
53
|
-
else
|
54
|
-
raise e
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
43
|
@app = app
|
60
44
|
@browser = nil
|
61
45
|
@exit_status = nil
|
@@ -68,10 +52,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
68
52
|
end
|
69
53
|
|
70
54
|
def refresh
|
71
|
-
|
72
|
-
browser.navigate.refresh
|
73
|
-
end
|
74
|
-
rescue Capybara::ModalNotFound
|
55
|
+
browser.navigate.refresh
|
75
56
|
end
|
76
57
|
|
77
58
|
def go_back
|
@@ -106,7 +87,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
106
87
|
def needs_server?; true; end
|
107
88
|
|
108
89
|
def execute_script(script, *args)
|
109
|
-
browser.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ?
|
90
|
+
browser.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg })
|
110
91
|
end
|
111
92
|
|
112
93
|
def evaluate_script(script, *args)
|
@@ -114,62 +95,71 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
114
95
|
unwrap_script_result(result)
|
115
96
|
end
|
116
97
|
|
117
|
-
def
|
98
|
+
def evaluate_async_script(script, *args)
|
99
|
+
browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
|
100
|
+
result = browser.execute_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg })
|
101
|
+
unwrap_script_result(result)
|
102
|
+
end
|
103
|
+
|
104
|
+
def save_screenshot(path, **_options)
|
118
105
|
browser.save_screenshot(path)
|
119
106
|
end
|
120
107
|
|
121
108
|
def reset!
|
122
109
|
# Use instance variable directly so we avoid starting the browser just to reset the session
|
123
|
-
|
124
|
-
|
125
|
-
|
110
|
+
return unless @browser
|
111
|
+
|
112
|
+
if firefox? || chrome?
|
113
|
+
switch_to_window(window_handles.first)
|
114
|
+
window_handles.slice(1..-1).each { |win| close_window(win) }
|
115
|
+
end
|
116
|
+
|
117
|
+
navigated = false
|
118
|
+
start_time = Capybara::Helpers.monotonic_time
|
119
|
+
begin
|
120
|
+
unless navigated
|
121
|
+
# Only trigger a navigation if we haven't done it already, otherwise it
|
122
|
+
# can trigger an endless series of unload modals
|
123
|
+
begin
|
124
|
+
@browser.manage.delete_all_cookies
|
125
|
+
clear_storage
|
126
|
+
rescue Selenium::WebDriver::Error::UnhandledError
|
127
|
+
# delete_all_cookies fails when we've previously gone
|
128
|
+
# to about:blank, so we rescue this error and do nothing
|
129
|
+
# instead.
|
130
|
+
end
|
131
|
+
@browser.navigate.to("about:blank")
|
132
|
+
end
|
133
|
+
navigated = true
|
134
|
+
|
135
|
+
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
136
|
+
until find_xpath("/html/body/*").empty?
|
137
|
+
raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if (Capybara::Helpers.monotonic_time - start_time) >= 10
|
138
|
+
sleep 0.05
|
139
|
+
end
|
140
|
+
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
141
|
+
# This error is thrown if an unhandled alert is on the page
|
142
|
+
# Firefox appears to automatically dismiss this alert, chrome does not
|
143
|
+
# We'll try to accept it
|
126
144
|
begin
|
127
|
-
|
128
|
-
|
129
|
-
|
145
|
+
@browser.switch_to.alert.accept
|
146
|
+
sleep 0.25 # allow time for the modal to be handled
|
147
|
+
rescue modal_error
|
148
|
+
# The alert is now gone
|
149
|
+
if current_url != "about:blank"
|
130
150
|
begin
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
if options[:clear_local_storage]
|
140
|
-
if @browser.respond_to? :local_storage
|
141
|
-
@browser.local_storage.clear
|
142
|
-
else
|
143
|
-
warn "localStorage clear requested but is not available for this driver"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
rescue Selenium::WebDriver::Error::UnhandledError
|
147
|
-
# delete_all_cookies fails when we've previously gone
|
148
|
-
# to about:blank, so we rescue this error and do nothing
|
149
|
-
# instead.
|
151
|
+
# If navigation has not occurred attempt again and accept alert
|
152
|
+
# since FF may have dismissed the alert at first attempt
|
153
|
+
@browser.navigate.to("about:blank")
|
154
|
+
sleep 0.1 # slight wait for alert
|
155
|
+
@browser.switch_to.alert.accept
|
156
|
+
rescue modal_error # rubocop:disable Metrics/BlockNesting
|
157
|
+
# alert now gone, should mean navigation happened
|
150
158
|
end
|
151
|
-
@browser.navigate.to("about:blank")
|
152
|
-
end
|
153
|
-
navigated = true
|
154
|
-
|
155
|
-
#Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
156
|
-
until find_xpath("/html/body/*").empty? do
|
157
|
-
raise Capybara::ExpectationNotMet.new('Timed out waiting for Selenium session reset') if (Capybara::Helpers.monotonic_time - start_time) >= 10
|
158
|
-
sleep 0.05
|
159
159
|
end
|
160
|
-
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
161
|
-
# This error is thrown if an unhandled alert is on the page
|
162
|
-
# Firefox appears to automatically dismiss this alert, chrome does not
|
163
|
-
# We'll try to accept it
|
164
|
-
begin
|
165
|
-
@browser.switch_to.alert.accept
|
166
|
-
sleep 0.25 # allow time for the modal to be handled
|
167
|
-
rescue Selenium::WebDriver::Error::NoAlertPresentError
|
168
|
-
# The alert is now gone - nothing to do
|
169
|
-
end
|
170
|
-
# try cleaning up the browser again
|
171
|
-
retry
|
172
160
|
end
|
161
|
+
# try cleaning up the browser again
|
162
|
+
retry
|
173
163
|
end
|
174
164
|
end
|
175
165
|
|
@@ -221,6 +211,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
221
211
|
end
|
222
212
|
|
223
213
|
def close_window(handle)
|
214
|
+
raise ArgumentError, "Not allowed to close the primary window" if handle == window_handles.first
|
224
215
|
within_given_window(handle) do
|
225
216
|
browser.close
|
226
217
|
end
|
@@ -238,43 +229,28 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
238
229
|
browser.switch_to.window handle
|
239
230
|
end
|
240
231
|
|
241
|
-
def
|
242
|
-
|
243
|
-
|
244
|
-
end
|
232
|
+
def accept_modal(_type, **options)
|
233
|
+
yield if block_given?
|
234
|
+
modal = find_modal(options)
|
245
235
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
else
|
252
|
-
yield if block_given?
|
253
|
-
modal = find_modal(options)
|
254
|
-
modal.send_keys options[:with] if options[:with]
|
255
|
-
message = modal.text
|
256
|
-
modal.accept
|
257
|
-
message
|
258
|
-
end
|
236
|
+
modal.send_keys options[:with] if options[:with]
|
237
|
+
|
238
|
+
message = modal.text
|
239
|
+
modal.accept
|
240
|
+
message
|
259
241
|
end
|
260
242
|
|
261
|
-
def dismiss_modal(_type, options
|
262
|
-
if
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
yield if block_given?
|
268
|
-
modal = find_modal(options)
|
269
|
-
message = modal.text
|
270
|
-
modal.dismiss
|
271
|
-
message
|
272
|
-
end
|
243
|
+
def dismiss_modal(_type, **options)
|
244
|
+
yield if block_given?
|
245
|
+
modal = find_modal(options)
|
246
|
+
message = modal.text
|
247
|
+
modal.dismiss
|
248
|
+
message
|
273
249
|
end
|
274
250
|
|
275
251
|
def quit
|
276
252
|
@browser.quit if @browser
|
277
|
-
rescue Errno::ECONNREFUSED
|
253
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
278
254
|
# Browser must have already gone
|
279
255
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
280
256
|
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
@@ -286,14 +262,18 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
286
262
|
end
|
287
263
|
|
288
264
|
def invalid_element_errors
|
289
|
-
[
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
265
|
+
[
|
266
|
+
::Selenium::WebDriver::Error::StaleElementReferenceError,
|
267
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
268
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
269
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a race condition that can occur with chromedriver and #go_back/#go_forward
|
270
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
271
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
272
|
+
::Selenium::WebDriver::Error::InvalidElementStateError,
|
273
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
274
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
275
|
+
::Selenium::WebDriver::Error::NoSuchElementError, # IE
|
276
|
+
::Selenium::WebDriver::Error::InvalidArgumentError # IE
|
297
277
|
]
|
298
278
|
end
|
299
279
|
|
@@ -317,47 +297,57 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
317
297
|
end
|
318
298
|
|
319
299
|
# @api private
|
320
|
-
def
|
321
|
-
|
322
|
-
caps = @processed_options[:desired_capabilities]
|
323
|
-
chrome_options = caps[:chrome_options] || caps[:chromeOptions] || {}
|
324
|
-
args = chrome_options['args'] || chrome_options[:args] || []
|
325
|
-
return args.include?("--headless") || args.include?("headless")
|
326
|
-
end
|
327
|
-
return false
|
300
|
+
def edge?
|
301
|
+
browser_name == "edge"
|
328
302
|
end
|
329
303
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
super && !@browser.nil?
|
304
|
+
# @api private
|
305
|
+
def ie?
|
306
|
+
browser_name == "ie"
|
334
307
|
end
|
335
308
|
|
336
|
-
|
309
|
+
private
|
337
310
|
|
338
|
-
# @api private
|
339
311
|
def browser_name
|
340
312
|
options[:browser].to_s
|
341
313
|
end
|
342
314
|
|
343
|
-
def
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
315
|
+
def clear_storage
|
316
|
+
if options[:clear_session_storage]
|
317
|
+
if @browser.respond_to? :session_storage
|
318
|
+
@browser.session_storage.clear
|
319
|
+
else
|
320
|
+
warn "sessionStorage clear requested but is not available for this driver"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
if options[:clear_local_storage]
|
324
|
+
if @browser.respond_to? :local_storage
|
325
|
+
@browser.local_storage.clear
|
326
|
+
else
|
327
|
+
warn "localStorage clear requested but is not available for this driver"
|
355
328
|
end
|
356
329
|
end
|
357
|
-
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
|
358
330
|
end
|
359
331
|
|
360
|
-
def
|
332
|
+
def modal_error
|
333
|
+
if defined?(Selenium::WebDriver::Error::NoSuchAlertError)
|
334
|
+
Selenium::WebDriver::Error::NoSuchAlertError
|
335
|
+
else
|
336
|
+
Selenium::WebDriver::Error::NoAlertPresentError
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def insert_modal_handlers(accept, response_text)
|
341
|
+
prompt_response = if accept
|
342
|
+
if response_text.nil?
|
343
|
+
"default_text"
|
344
|
+
else
|
345
|
+
"'#{response_text.gsub("\\", "\\\\\\").gsub("'", "\\\\'")}'"
|
346
|
+
end
|
347
|
+
else
|
348
|
+
'null'
|
349
|
+
end
|
350
|
+
|
361
351
|
script = <<-JS
|
362
352
|
if (typeof window.capybara === 'undefined') {
|
363
353
|
window.capybara = {
|
@@ -389,20 +379,20 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
389
379
|
}
|
390
380
|
window.capybara.add_handler(modal_handler);
|
391
381
|
|
392
|
-
window.alert = window.confirm = function(str) {
|
393
|
-
window.capybara.handler_called(modal_handler, str);
|
382
|
+
window.alert = window.confirm = function(str = "") {
|
383
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
394
384
|
return #{accept ? 'true' : 'false'};
|
395
|
-
}
|
396
|
-
window.prompt = function(str) {
|
397
|
-
window.capybara.handler_called(modal_handler, str);
|
398
|
-
return #{
|
385
|
+
}
|
386
|
+
window.prompt = function(str = "", default_text = "") {
|
387
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
388
|
+
return #{prompt_response};
|
399
389
|
}
|
400
390
|
JS
|
401
391
|
execute_script script
|
402
392
|
end
|
403
393
|
|
404
394
|
def within_given_window(handle)
|
405
|
-
original_handle =
|
395
|
+
original_handle = current_window_handle
|
406
396
|
if handle == original_handle
|
407
397
|
yield
|
408
398
|
else
|
@@ -413,39 +403,41 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
413
403
|
end
|
414
404
|
end
|
415
405
|
|
416
|
-
def find_modal(options
|
406
|
+
def find_modal(text: nil, **options)
|
417
407
|
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
418
408
|
# Actual wait time may be longer than specified
|
419
409
|
wait = Selenium::WebDriver::Wait.new(
|
420
|
-
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0
|
421
|
-
ignore:
|
410
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0,
|
411
|
+
ignore: modal_error
|
412
|
+
)
|
422
413
|
begin
|
423
414
|
wait.until do
|
424
415
|
alert = @browser.switch_to.alert
|
425
|
-
regexp =
|
416
|
+
regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
|
426
417
|
alert.text.match(regexp) ? alert : nil
|
427
418
|
end
|
428
419
|
rescue Selenium::WebDriver::Error::TimeOutError
|
429
|
-
raise Capybara::ModalNotFound
|
420
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
430
421
|
end
|
431
422
|
end
|
432
423
|
|
433
|
-
def find_headless_modal(options
|
424
|
+
def find_headless_modal(text: nil, **options)
|
434
425
|
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
435
426
|
# Actual wait time may be longer than specified
|
436
427
|
wait = Selenium::WebDriver::Wait.new(
|
437
|
-
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0
|
438
|
-
ignore:
|
428
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0,
|
429
|
+
ignore: modal_error
|
430
|
+
)
|
439
431
|
begin
|
440
432
|
wait.until do
|
441
433
|
called, alert_text = evaluate_script('window.capybara && window.capybara.current_modal_status()')
|
442
434
|
if called
|
443
435
|
execute_script('window.capybara && window.capybara.modal_handlers.shift()')
|
444
|
-
regexp =
|
436
|
+
regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
|
445
437
|
if alert_text.match(regexp)
|
446
438
|
alert_text
|
447
439
|
else
|
448
|
-
raise Capybara::ModalNotFound
|
440
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
449
441
|
end
|
450
442
|
elsif called.nil?
|
451
443
|
# page changed so modal_handler data has gone away
|
@@ -456,7 +448,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
456
448
|
end
|
457
449
|
end
|
458
450
|
rescue Selenium::WebDriver::Error::TimeOutError
|
459
|
-
raise Capybara::ModalNotFound
|
451
|
+
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}"
|
460
452
|
end
|
461
453
|
end
|
462
454
|
|
@@ -465,7 +457,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
465
457
|
end
|
466
458
|
|
467
459
|
def silenced_unknown_error_messages
|
468
|
-
[
|
460
|
+
[/Error communicating with the remote browser/]
|
469
461
|
end
|
470
462
|
|
471
463
|
def unwrap_script_result(arg)
|
@@ -480,4 +472,21 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
480
472
|
arg
|
481
473
|
end
|
482
474
|
end
|
475
|
+
|
476
|
+
def load_selenium
|
477
|
+
require 'selenium-webdriver'
|
478
|
+
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
479
|
+
unless defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
480
|
+
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
481
|
+
end
|
482
|
+
unless defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
483
|
+
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
484
|
+
end
|
485
|
+
rescue LoadError => e
|
486
|
+
if e.message =~ /selenium-webdriver/
|
487
|
+
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."
|
488
|
+
else
|
489
|
+
raise e
|
490
|
+
end
|
491
|
+
end
|
483
492
|
end
|