capybara 3.16.2 → 3.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|