capybara 3.16.2 → 3.17.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/History.md +13 -0
- data/README.md +1 -1
- data/lib/capybara.rb +2 -71
- data/lib/capybara/config.rb +1 -2
- data/lib/capybara/node/actions.rb +5 -5
- data/lib/capybara/node/base.rb +4 -4
- data/lib/capybara/node/element.rb +5 -5
- data/lib/capybara/node/finders.rb +6 -4
- data/lib/capybara/node/simple.rb +3 -2
- data/lib/capybara/queries/selector_query.rb +5 -5
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/registrations/drivers.rb +36 -0
- data/lib/capybara/registrations/servers.rb +38 -0
- data/lib/capybara/result.rb +2 -2
- data/lib/capybara/rspec/matcher_proxies.rb +2 -2
- data/lib/capybara/rspec/matchers/base.rb +2 -2
- data/lib/capybara/selector.rb +55 -32
- data/lib/capybara/selector/css.rb +1 -1
- data/lib/capybara/selector/filters/base.rb +1 -1
- data/lib/capybara/selector/selector.rb +1 -0
- data/lib/capybara/selenium/driver.rb +84 -43
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +16 -4
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +23 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +5 -0
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +14 -1
- data/lib/capybara/selenium/extensions/find.rb +48 -37
- data/lib/capybara/selenium/logger_suppressor.rb +29 -0
- data/lib/capybara/selenium/node.rb +22 -8
- data/lib/capybara/selenium/nodes/chrome_node.rb +8 -2
- data/lib/capybara/server/animation_disabler.rb +1 -1
- data/lib/capybara/server/checker.rb +1 -1
- data/lib/capybara/server/middleware.rb +3 -3
- data/lib/capybara/session/config.rb +2 -8
- data/lib/capybara/spec/session/attach_file_spec.rb +1 -1
- data/lib/capybara/spec/session/check_spec.rb +4 -4
- data/lib/capybara/spec/session/choose_spec.rb +2 -2
- data/lib/capybara/spec/session/click_button_spec.rb +28 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +2 -2
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +12 -1
- data/lib/capybara/spec/session/node_spec.rb +18 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
- data/lib/capybara/spec/session/unselect_spec.rb +1 -1
- data/lib/capybara/spec/views/frame_child.erb +2 -1
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/minitest_spec_spec.rb +1 -1
- data/spec/result_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +8 -4
- data/spec/selector_spec.rb +4 -0
- data/spec/selenium_spec_safari.rb +2 -3
- data/spec/session_spec.rb +7 -0
- data/spec/shared_selenium_session.rb +14 -11
- data/spec/spec_helper.rb +2 -1
- metadata +6 -16
@@ -41,8 +41,8 @@ module Capybara
|
|
41
41
|
class WrappedElementMatcher < Base
|
42
42
|
def matches?(actual)
|
43
43
|
element_matches?(wrap(actual))
|
44
|
-
rescue Capybara::ExpectationNotMet =>
|
45
|
-
@failure_message =
|
44
|
+
rescue Capybara::ExpectationNotMet => e
|
45
|
+
@failure_message = e.message
|
46
46
|
false
|
47
47
|
end
|
48
48
|
|
data/lib/capybara/selector.rb
CHANGED
@@ -7,20 +7,28 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
7
7
|
node_filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
|
8
8
|
node_filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
|
9
9
|
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
10
|
-
node_filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
11
10
|
|
12
11
|
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
|
13
12
|
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
|
13
|
+
expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
|
14
|
+
expression_filter(:multiple) { |xpath, val| xpath[val ? XPath.attr(:multiple) : ~XPath.attr(:multiple)] }
|
14
15
|
|
15
|
-
describe(:
|
16
|
+
describe(:expression_filters) do |name: nil, placeholder: nil, disabled: nil, multiple: nil, **|
|
17
|
+
desc = +''
|
18
|
+
desc << ' that is not disabled' if disabled == false
|
19
|
+
desc << " with name #{name}" if name
|
20
|
+
desc << " with placeholder #{placeholder}" if placeholder
|
21
|
+
desc << ' with the multiple attribute' if multiple == true
|
22
|
+
desc << ' without the multiple attribute' if multiple == false
|
23
|
+
desc
|
24
|
+
end
|
25
|
+
|
26
|
+
describe(:node_filters) do |checked: nil, unchecked: nil, disabled: nil, **|
|
16
27
|
desc, states = +'', []
|
17
28
|
states << 'checked' if checked || (unchecked == false)
|
18
29
|
states << 'not checked' if unchecked || (checked == false)
|
19
30
|
states << 'disabled' if disabled == true
|
20
|
-
states << 'not disabled' if disabled == false
|
21
31
|
desc << " that is #{states.join(' and ')}" unless states.empty?
|
22
|
-
desc << ' with the multiple attribute' if multiple == true
|
23
|
-
desc << ' without the multiple attribute' if multiple == false
|
24
32
|
desc
|
25
33
|
end
|
26
34
|
end
|
@@ -37,7 +45,7 @@ end
|
|
37
45
|
|
38
46
|
Capybara.add_selector(:id, locator_type: [String, Symbol, Regexp]) do
|
39
47
|
xpath { |id| builder(XPath.descendant).add_attribute_conditions(id: id) }
|
40
|
-
locator_filter { |node, id| id.is_a?(Regexp) ? node[:id]
|
48
|
+
locator_filter { |node, id| id.is_a?(Regexp) ? id.match?(node[:id]) : true }
|
41
49
|
end
|
42
50
|
|
43
51
|
Capybara.add_selector(:field, locator_type: [String, Symbol]) do
|
@@ -63,16 +71,13 @@ Capybara.add_selector(:field, locator_type: [String, Symbol]) do
|
|
63
71
|
node_filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
|
64
72
|
node_filter(:with) do |node, with|
|
65
73
|
val = node.value
|
66
|
-
(with.is_a?(Regexp) ? val
|
74
|
+
(with.is_a?(Regexp) ? with.match?(val) : val == with.to_s).tap do |res|
|
67
75
|
add_error("Expected value to be #{with.inspect} but was #{val.inspect}") unless res
|
68
76
|
end
|
69
77
|
end
|
70
78
|
|
71
|
-
describe_expression_filters do |type: nil,
|
72
|
-
|
73
|
-
(expression_filters.keys & options.keys).each { |ef| desc << " with #{ef} #{options[ef]}" }
|
74
|
-
desc << " of type #{type.inspect}" if type
|
75
|
-
desc
|
79
|
+
describe_expression_filters do |type: nil, **|
|
80
|
+
" of type #{type.inspect}" if type
|
76
81
|
end
|
77
82
|
|
78
83
|
describe_node_filters do |**options|
|
@@ -90,6 +95,7 @@ Capybara.add_selector(:fieldset, locator_type: [String, Symbol]) do
|
|
90
95
|
end
|
91
96
|
|
92
97
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
98
|
+
expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
|
93
99
|
end
|
94
100
|
|
95
101
|
Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
@@ -123,26 +129,28 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
123
129
|
builder(expr).add_attribute_conditions(download: download)
|
124
130
|
end
|
125
131
|
|
126
|
-
describe_expression_filters do
|
132
|
+
describe_expression_filters do |download: nil, **options|
|
127
133
|
desc = +''
|
128
134
|
if (href = options[:href])
|
129
135
|
desc << " with href #{'matching ' if href.is_a? Regexp}#{href.inspect}"
|
130
136
|
elsif options.key?(:href) # is nil/false specified?
|
131
137
|
desc << ' with no href attribute'
|
132
138
|
end
|
139
|
+
desc << " with download attribute#{" #{download}" if download.is_a? String}" if download
|
140
|
+
desc << ' without download attribute' if download == false
|
133
141
|
desc
|
134
142
|
end
|
135
143
|
end
|
136
144
|
|
137
145
|
Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
138
|
-
xpath(:value, :title, :type) do |locator, **options|
|
146
|
+
xpath(:value, :title, :type, :name) do |locator, **options|
|
139
147
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
140
148
|
btn_xpath = XPath.descendant(:button)
|
141
149
|
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
|
142
150
|
|
143
151
|
unless locator.nil?
|
144
152
|
locator = locator.to_s
|
145
|
-
locator_matchers = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
153
|
+
locator_matchers = XPath.attr(:id).equals(locator) | XPath.attr(:name).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
146
154
|
locator_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
147
155
|
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
148
156
|
|
@@ -155,14 +163,20 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
155
163
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
156
164
|
end
|
157
165
|
|
158
|
-
%i[value title type].inject(input_btn_xpath.union(btn_xpath).union(image_btn_xpath)) do |memo, ef|
|
166
|
+
%i[value title type name].inject(input_btn_xpath.union(btn_xpath).union(image_btn_xpath)) do |memo, ef|
|
159
167
|
memo[find_by_attr(ef, options[ef])]
|
160
168
|
end
|
161
169
|
end
|
162
170
|
|
163
171
|
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
172
|
+
expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
|
173
|
+
|
174
|
+
describe_expression_filters do |disabled: nil, **options|
|
175
|
+
desc = +''
|
176
|
+
desc << ' that is not disabled' if disabled == false
|
177
|
+
desc << describe_all_expression_filters(options)
|
178
|
+
end
|
164
179
|
|
165
|
-
describe_expression_filters
|
166
180
|
describe_node_filters do |disabled: nil, **|
|
167
181
|
' that is disabled' if disabled == true
|
168
182
|
end
|
@@ -176,7 +190,7 @@ Capybara.add_selector(:link_or_button, locator_type: [String, Symbol]) do
|
|
176
190
|
end.reduce(:union)
|
177
191
|
end
|
178
192
|
|
179
|
-
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value|
|
193
|
+
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
180
194
|
|
181
195
|
describe_node_filters do |disabled: nil, **|
|
182
196
|
' that is disabled' if disabled == true
|
@@ -205,12 +219,11 @@ Capybara.add_selector(:fillable_field, locator_type: [String, Symbol]) do
|
|
205
219
|
|
206
220
|
node_filter(:with) do |node, with|
|
207
221
|
val = node.value
|
208
|
-
(with.is_a?(Regexp) ? val
|
222
|
+
(with.is_a?(Regexp) ? with.match?(val) : val == with.to_s).tap do |res|
|
209
223
|
add_error("Expected value to be #{with.inspect} but was #{val.inspect}") unless res
|
210
224
|
end
|
211
225
|
end
|
212
226
|
|
213
|
-
describe_expression_filters
|
214
227
|
describe_node_filters do |**options|
|
215
228
|
" with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
216
229
|
end
|
@@ -234,7 +247,6 @@ Capybara.add_selector(:radio_button, locator_type: [String, Symbol]) do
|
|
234
247
|
end
|
235
248
|
end
|
236
249
|
|
237
|
-
describe_expression_filters
|
238
250
|
describe_node_filters do |option: nil, **|
|
239
251
|
" with value #{option.inspect}" if option
|
240
252
|
end
|
@@ -257,7 +269,6 @@ Capybara.add_selector(:checkbox, locator_type: [String, Symbol]) do
|
|
257
269
|
end
|
258
270
|
end
|
259
271
|
|
260
|
-
describe_expression_filters
|
261
272
|
describe_node_filters do |option: nil, **|
|
262
273
|
" with value #{option.inspect}" if option
|
263
274
|
end
|
@@ -304,18 +315,18 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
|
304
315
|
end
|
305
316
|
end
|
306
317
|
|
307
|
-
describe_expression_filters do |with_options: nil,
|
318
|
+
describe_expression_filters do |with_options: nil, **|
|
308
319
|
desc = +''
|
309
320
|
desc << " with at least options #{with_options.inspect}" if with_options
|
310
|
-
desc << describe_all_expression_filters(opts)
|
311
321
|
desc
|
312
322
|
end
|
313
323
|
|
314
|
-
describe_node_filters do |options: nil, selected: nil, with_selected: nil, **|
|
324
|
+
describe_node_filters do |options: nil, selected: nil, with_selected: nil, disabled: nil, **|
|
315
325
|
desc = +''
|
316
326
|
desc << " with options #{options.inspect}" if options
|
317
327
|
desc << " with #{selected.inspect} selected" if selected
|
318
328
|
desc << " with at least #{with_selected.inspect} selected" if with_selected
|
329
|
+
desc << ' which is disabled' if disabled
|
319
330
|
desc
|
320
331
|
end
|
321
332
|
end
|
@@ -343,10 +354,9 @@ Capybara.add_selector(:datalist_input, locator_type: [String, Symbol]) do
|
|
343
354
|
end
|
344
355
|
end
|
345
356
|
|
346
|
-
describe_expression_filters do |with_options: nil,
|
357
|
+
describe_expression_filters do |with_options: nil, **|
|
347
358
|
desc = +''
|
348
359
|
desc << " with at least options #{with_options.inspect}" if with_options
|
349
|
-
desc << describe_all_expression_filters(opts)
|
350
360
|
desc
|
351
361
|
end
|
352
362
|
|
@@ -363,11 +373,19 @@ Capybara.add_selector(:option, locator_type: [String, Symbol]) do
|
|
363
373
|
end
|
364
374
|
|
365
375
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
376
|
+
expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
|
377
|
+
|
366
378
|
node_filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
|
367
379
|
|
380
|
+
describe_expression_filters do |disabled: nil, **options|
|
381
|
+
desc = +''
|
382
|
+
desc << ' that is not disabled' if disabled == false
|
383
|
+
(expression_filters.keys & options.keys).inject(desc) { |memo, ef| memo << " with #{ef} #{options[ef]}" }
|
384
|
+
end
|
385
|
+
|
368
386
|
describe_node_filters do |**options|
|
369
387
|
desc = +''
|
370
|
-
desc <<
|
388
|
+
desc << ' that is disabled' if options[:disabled]
|
371
389
|
desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
|
372
390
|
desc
|
373
391
|
end
|
@@ -384,9 +402,16 @@ Capybara.add_selector(:datalist_option, locator_type: [String, Symbol]) do
|
|
384
402
|
end
|
385
403
|
|
386
404
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
405
|
+
expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
|
406
|
+
|
407
|
+
describe_expression_filters do |disabled: nil, **options|
|
408
|
+
desc = +''
|
409
|
+
desc << ' that is not disabled' if disabled == false
|
410
|
+
desc << describe_all_expression_filters(options)
|
411
|
+
end
|
387
412
|
|
388
413
|
describe_node_filters do |**options|
|
389
|
-
|
414
|
+
' that is disabled' if options[:disabled]
|
390
415
|
end
|
391
416
|
end
|
392
417
|
|
@@ -400,8 +425,6 @@ Capybara.add_selector(:file_field, locator_type: [String, Symbol]) do
|
|
400
425
|
end
|
401
426
|
|
402
427
|
filter_set(:_field, %i[disabled multiple name])
|
403
|
-
|
404
|
-
describe_expression_filters
|
405
428
|
end
|
406
429
|
|
407
430
|
Capybara.add_selector(:label, locator_type: [String, Symbol]) do
|
@@ -595,7 +618,7 @@ Capybara.add_selector(:element, locator_type: [String, Symbol]) do
|
|
595
618
|
node_filter(:attributes, matcher: /.+/) do |node, name, val|
|
596
619
|
next true unless val.is_a?(Regexp)
|
597
620
|
|
598
|
-
(node[name]
|
621
|
+
(val.match? node[name]).tap do |res|
|
599
622
|
add_error("Expected #{name} to match #{val.inspect} but it was #{node[name]}") unless res
|
600
623
|
end
|
601
624
|
end
|
@@ -6,7 +6,7 @@ module Capybara
|
|
6
6
|
def self.escape(str)
|
7
7
|
value = str.dup
|
8
8
|
out = +''
|
9
|
-
out << value.slice!(0...1) if value
|
9
|
+
out << value.slice!(0...1) if value.match?(/^[-_]/)
|
10
10
|
out << (value[0].match?(NMSTART) ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
11
11
|
out << value.gsub(/[^a-zA-Z0-9_-]/) { |char| escape_char char }
|
12
12
|
out
|
@@ -59,6 +59,7 @@ module Capybara
|
|
59
59
|
# * Locator: Matches the id, Capybara.test_id attribute, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
|
60
60
|
# * Filters:
|
61
61
|
# * :id (String, Regexp, XPath::Expression) — Matches the id attribute
|
62
|
+
# * :name (String) - Matches the name attribute
|
62
63
|
# * :title (String) — Matches the title attribute
|
63
64
|
# * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
|
64
65
|
# * :value (String) — Matches the value of an input button
|
@@ -14,17 +14,27 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
14
14
|
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout].freeze
|
15
15
|
attr_reader :app, :options
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
class << self
|
18
|
+
def load_selenium
|
19
|
+
require 'selenium-webdriver'
|
20
|
+
require 'capybara/selenium/logger_suppressor'
|
21
|
+
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0')
|
22
|
+
rescue LoadError => e
|
23
|
+
raise e unless e.message.match?(/selenium-webdriver/)
|
24
|
+
|
25
|
+
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."
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :specializations
|
22
29
|
|
23
|
-
|
30
|
+
def register_specialization(browser_name, specialization)
|
31
|
+
@specializations ||= {}
|
32
|
+
@specializations[browser_name] = specialization
|
33
|
+
end
|
24
34
|
end
|
25
35
|
|
26
36
|
def browser
|
27
|
-
@browser
|
37
|
+
unless @browser
|
28
38
|
options[:http_client] ||= begin
|
29
39
|
require 'capybara/selenium/patches/persistent_client'
|
30
40
|
if options[:timeout]
|
@@ -34,10 +44,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
34
44
|
end
|
35
45
|
end
|
36
46
|
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
37
|
-
Selenium::WebDriver.for(options[:browser], processed_options)
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
@browser = Selenium::WebDriver.for(options[:browser], processed_options)
|
48
|
+
|
49
|
+
specialize_driver
|
50
|
+
setup_exit_handler
|
41
51
|
end
|
42
52
|
@browser
|
43
53
|
end
|
@@ -115,7 +125,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
115
125
|
navigated = true
|
116
126
|
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
117
127
|
wait_for_empty_page(timer)
|
118
|
-
rescue
|
128
|
+
rescue *unhandled_alert_errors
|
119
129
|
# This error is thrown if an unhandled alert is on the page
|
120
130
|
# Firefox appears to automatically dismiss this alert, chrome does not
|
121
131
|
# We'll try to accept it
|
@@ -226,19 +236,27 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
226
236
|
end
|
227
237
|
|
228
238
|
def invalid_element_errors
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
239
|
+
@invalid_element_errors ||= begin
|
240
|
+
[
|
241
|
+
::Selenium::WebDriver::Error::StaleElementReferenceError,
|
242
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
243
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition
|
244
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
245
|
+
::Selenium::WebDriver::Error::NoSuchElementError, # IE
|
246
|
+
::Selenium::WebDriver::Error::InvalidArgumentError # IE
|
247
|
+
].tap do |errors|
|
248
|
+
unless selenium_4?
|
249
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
250
|
+
errors.concat [
|
251
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
252
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
253
|
+
::Selenium::WebDriver::Error::InvalidElementStateError,
|
254
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError
|
255
|
+
]
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
242
260
|
end
|
243
261
|
|
244
262
|
def no_such_window_error
|
@@ -247,6 +265,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
247
265
|
|
248
266
|
private
|
249
267
|
|
268
|
+
def selenium_4?
|
269
|
+
defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)
|
270
|
+
end
|
271
|
+
|
250
272
|
def native_args(args)
|
251
273
|
args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg }
|
252
274
|
end
|
@@ -254,12 +276,32 @@ private
|
|
254
276
|
def clear_browser_state
|
255
277
|
delete_all_cookies
|
256
278
|
clear_storage
|
257
|
-
rescue
|
279
|
+
rescue *clear_browser_state_errors # rubocop:disable Lint/HandleExceptions
|
258
280
|
# delete_all_cookies fails when we've previously gone
|
259
281
|
# to about:blank, so we rescue this error and do nothing
|
260
282
|
# instead.
|
261
283
|
end
|
262
284
|
|
285
|
+
def clear_browser_state_errors
|
286
|
+
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError].tap do |errors|
|
287
|
+
unless selenium_4?
|
288
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
289
|
+
errors << Selenium::WebDriver::Error::UnhandledError
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def unhandled_alert_errors
|
296
|
+
@unhandled_alert_errors ||= [Selenium::WebDriver::Error::UnexpectedAlertOpenError].tap do |errors|
|
297
|
+
unless selenium_4?
|
298
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
299
|
+
errors << Selenium::WebDriver::Error::UnhandledAlertError
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
263
305
|
def delete_all_cookies
|
264
306
|
@browser.manage.delete_all_cookies
|
265
307
|
end
|
@@ -332,13 +374,23 @@ private
|
|
332
374
|
regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
|
333
375
|
alert.text.match?(regexp) ? alert : nil
|
334
376
|
end
|
335
|
-
rescue
|
377
|
+
rescue *find_modal_errors
|
336
378
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
337
379
|
end
|
338
380
|
end
|
339
381
|
|
382
|
+
def find_modal_errors
|
383
|
+
@find_modal_errors ||= [Selenium::WebDriver::Error::TimeoutError].tap do |errors|
|
384
|
+
unless selenium_4?
|
385
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
386
|
+
errors << Selenium::WebDriver::Error::TimeOutError
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
340
392
|
def silenced_unknown_error_message?(msg)
|
341
|
-
silenced_unknown_error_messages.any? { |regex| msg
|
393
|
+
silenced_unknown_error_messages.any? { |regex| msg.match? regex }
|
342
394
|
end
|
343
395
|
|
344
396
|
def silenced_unknown_error_messages
|
@@ -366,24 +418,13 @@ private
|
|
366
418
|
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
367
419
|
end
|
368
420
|
|
369
|
-
def specialize_driver
|
370
|
-
|
371
|
-
|
372
|
-
extend
|
373
|
-
when :firefox
|
374
|
-
require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(sel_driver)
|
375
|
-
extend FirefoxDriver if sel_driver.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
376
|
-
when :ie, :internet_explorer
|
377
|
-
extend InternetExplorerDriver
|
378
|
-
when :safari, :Safari_Technology_Preview
|
379
|
-
extend SafariDriver
|
421
|
+
def specialize_driver
|
422
|
+
browser_type = browser.browser
|
423
|
+
self.class.specializations.select { |k, _v| k === browser_type }.each_value do |specialization| # rubocop:disable Style/CaseEquality
|
424
|
+
extend specialization
|
380
425
|
end
|
381
426
|
end
|
382
427
|
|
383
|
-
def pause_broken?(driver)
|
384
|
-
driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.')
|
385
|
-
end
|
386
|
-
|
387
428
|
def setup_exit_handler
|
388
429
|
main = Process.pid
|
389
430
|
at_exit do
|