capybara 3.35.3 → 3.37.1
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 +57 -1
- data/README.md +5 -1
- data/lib/capybara/config.rb +16 -4
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/driver/node.rb +5 -1
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/helpers.rb +3 -12
- data/lib/capybara/minitest/spec.rb +2 -2
- data/lib/capybara/node/actions.rb +10 -5
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/element.rb +13 -2
- data/lib/capybara/node/finders.rb +9 -2
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +2 -1
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +34 -8
- data/lib/capybara/queries/sibling_query.rb +2 -1
- data/lib/capybara/rack_test/browser.rb +56 -7
- data/lib/capybara/rack_test/driver.rb +4 -4
- data/lib/capybara/rack_test/node.rb +10 -7
- data/lib/capybara/registration_container.rb +0 -3
- data/lib/capybara/registrations/drivers.rb +3 -3
- data/lib/capybara/rspec/matcher_proxies.rb +3 -3
- data/lib/capybara/rspec/matchers/have_selector.rb +5 -5
- data/lib/capybara/rspec/matchers.rb +14 -14
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
- data/lib/capybara/selector/css.rb +1 -1
- data/lib/capybara/selector/definition/button.rb +9 -4
- data/lib/capybara/selector/definition/checkbox.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/radio_button.rb +1 -1
- data/lib/capybara/selector/definition.rb +3 -1
- data/lib/capybara/selector/filter_set.rb +4 -6
- data/lib/capybara/selector/selector.rb +5 -1
- data/lib/capybara/selector.rb +1 -0
- data/lib/capybara/selenium/driver.rb +25 -11
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
- data/lib/capybara/selenium/node.rb +22 -8
- data/lib/capybara/selenium/nodes/chrome_node.rb +1 -1
- data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
- data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
- data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
- data/lib/capybara/server/animation_disabler.rb +35 -17
- data/lib/capybara/session/config.rb +1 -1
- data/lib/capybara/session.rb +20 -23
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +9 -13
- data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
- data/lib/capybara/spec/session/check_spec.rb +9 -0
- data/lib/capybara/spec/session/choose_spec.rb +6 -0
- data/lib/capybara/spec/session/find_spec.rb +6 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +4 -0
- data/lib/capybara/spec/session/has_button_spec.rb +24 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
- data/lib/capybara/spec/session/has_field_spec.rb +25 -1
- data/lib/capybara/spec/session/has_link_spec.rb +30 -0
- data/lib/capybara/spec/session/has_select_spec.rb +4 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
- data/lib/capybara/spec/session/has_text_spec.rb +2 -6
- data/lib/capybara/spec/session/node_spec.rb +43 -1
- data/lib/capybara/spec/session/scroll_spec.rb +4 -4
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/spec_helper.rb +4 -3
- data/lib/capybara/spec/test_app.rb +66 -8
- data/lib/capybara/spec/views/animated.erb +1 -1
- data/lib/capybara/spec/views/form.erb +11 -3
- data/lib/capybara/spec/views/frame_child.erb +1 -1
- data/lib/capybara/spec/views/frame_one.erb +1 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/offset.erb +2 -1
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +2 -2
- data/lib/capybara/spec/views/scroll.erb +2 -1
- data/lib/capybara/spec/views/spatial.erb +1 -1
- data/lib/capybara/spec/views/with_animation.erb +2 -3
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +2 -2
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +2 -2
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
- data/lib/capybara/spec/views/with_js.erb +2 -3
- data/lib/capybara/spec/views/with_jstree.erb +1 -1
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +19 -22
- data/spec/basic_node_spec.rb +16 -3
- data/spec/dsl_spec.rb +3 -3
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
- data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
- data/spec/rack_test_spec.rb +20 -10
- data/spec/result_spec.rb +32 -35
- data/spec/rspec/features_spec.rb +3 -3
- data/spec/rspec/scenarios_spec.rb +1 -1
- data/spec/rspec/shared_spec_matchers.rb +2 -2
- data/spec/sauce_spec_chrome.rb +3 -3
- data/spec/selector_spec.rb +2 -2
- data/spec/selenium_spec_chrome.rb +9 -10
- data/spec/selenium_spec_chrome_remote.rb +9 -8
- data/spec/selenium_spec_firefox.rb +8 -3
- data/spec/selenium_spec_firefox_remote.rb +2 -2
- data/spec/selenium_spec_ie.rb +3 -6
- data/spec/selenium_spec_safari.rb +31 -19
- data/spec/server_spec.rb +5 -5
- data/spec/shared_selenium_node.rb +0 -4
- data/spec/shared_selenium_session.rb +20 -10
- data/spec/spec_helper.rb +1 -1
- metadata +37 -14
- data/lib/capybara/spec/views/with_title.erb +0 -5
|
@@ -14,7 +14,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def all_text
|
|
17
|
-
text = driver.evaluate_script('arguments[0].textContent', self)
|
|
17
|
+
text = driver.evaluate_script('arguments[0].textContent', self) || ''
|
|
18
18
|
text.gsub(/[\u200b\u200e\u200f]/, '')
|
|
19
19
|
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
|
20
20
|
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
|
@@ -53,6 +53,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
53
53
|
# :none => append the new value to the existing value <br/>
|
|
54
54
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
|
55
55
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
|
56
|
+
# @option options [Boolean] :rapid (nil) Whether setting text inputs should use a faster "rapid" mode<br/>
|
|
57
|
+
# nil => Text inputs with length greater than 30 characters will be set using a faster driver script mode<br/>
|
|
58
|
+
# true => Rapid mode will be used regardless of input length<br/>
|
|
59
|
+
# false => Sends keys via conventional mode. This may be required to avoid losing key-presses if you have certain
|
|
60
|
+
# Javascript interactions on form inputs<br/>
|
|
56
61
|
def set(value, **options)
|
|
57
62
|
if value.is_a?(Array) && !multiple?
|
|
58
63
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
|
@@ -199,10 +204,6 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
199
204
|
native.attribute('isContentEditable') == 'true'
|
|
200
205
|
end
|
|
201
206
|
|
|
202
|
-
def ==(other)
|
|
203
|
-
native == other.native
|
|
204
|
-
end
|
|
205
|
-
|
|
206
207
|
def path
|
|
207
208
|
driver.evaluate_script GET_XPATH_SCRIPT, self
|
|
208
209
|
end
|
|
@@ -218,6 +219,13 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
218
219
|
native.rect
|
|
219
220
|
end
|
|
220
221
|
|
|
222
|
+
def shadow_root
|
|
223
|
+
raise_error 'You must be using Selenium 4.1+ for shadow_root support' unless native.respond_to? :shadow_root
|
|
224
|
+
|
|
225
|
+
root = native.shadow_root
|
|
226
|
+
root && build_node(native.shadow_root)
|
|
227
|
+
end
|
|
228
|
+
|
|
221
229
|
protected
|
|
222
230
|
|
|
223
231
|
def scroll_if_needed
|
|
@@ -274,7 +282,7 @@ private
|
|
|
274
282
|
elsif clear == :backspace
|
|
275
283
|
# Clear field by sending the correct number of backspace keys.
|
|
276
284
|
backspaces = [:backspace] * self.value.to_s.length
|
|
277
|
-
send_keys(
|
|
285
|
+
send_keys(:end, *backspaces, value)
|
|
278
286
|
elsif clear.is_a? Array
|
|
279
287
|
send_keys(*clear, value)
|
|
280
288
|
else
|
|
@@ -282,7 +290,7 @@ private
|
|
|
282
290
|
if rapid == true || ((value.length > auto_rapid_set_length) && rapid != false)
|
|
283
291
|
send_keys(value[0..3])
|
|
284
292
|
driver.execute_script RAPID_APPEND_TEXT, self, value[4...-3]
|
|
285
|
-
send_keys(value[-3
|
|
293
|
+
send_keys(value[-3..])
|
|
286
294
|
else
|
|
287
295
|
send_keys(value)
|
|
288
296
|
end
|
|
@@ -298,7 +306,7 @@ private
|
|
|
298
306
|
|
|
299
307
|
scroll_if_needed do
|
|
300
308
|
action_with_modifiers(click_options) do |action|
|
|
301
|
-
if
|
|
309
|
+
if block
|
|
302
310
|
yield action
|
|
303
311
|
else
|
|
304
312
|
click_options.coords? ? action.click : action.click(native)
|
|
@@ -488,6 +496,12 @@ private
|
|
|
488
496
|
JS
|
|
489
497
|
end
|
|
490
498
|
|
|
499
|
+
def native_id
|
|
500
|
+
# Selenium 3 -> 4 changed the return of ref
|
|
501
|
+
type_or_id, id = native.ref
|
|
502
|
+
id || type_or_id
|
|
503
|
+
end
|
|
504
|
+
|
|
491
505
|
GET_XPATH_SCRIPT = <<~'JS'
|
|
492
506
|
(function(el, xml){
|
|
493
507
|
var xpath = '';
|
|
@@ -65,7 +65,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
|
65
65
|
return super unless native_displayed?
|
|
66
66
|
|
|
67
67
|
begin
|
|
68
|
-
bridge.send(:execute, :is_element_displayed, id:
|
|
68
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
|
69
69
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
|
70
70
|
# If the is_element_displayed command is unknown, no point in trying again
|
|
71
71
|
driver.options[:native_displayed] = false
|
|
@@ -69,7 +69,7 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
|
|
|
69
69
|
return super unless chrome_edge? && native_displayed?
|
|
70
70
|
|
|
71
71
|
begin
|
|
72
|
-
bridge.send(:execute, :is_element_displayed, id:
|
|
72
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
|
73
73
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
|
74
74
|
# If the is_element_displayed command is unknown, no point in trying again
|
|
75
75
|
driver.options[:native_displayed] = false
|
|
@@ -76,7 +76,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
|
76
76
|
return super unless native_displayed?
|
|
77
77
|
|
|
78
78
|
begin
|
|
79
|
-
bridge.send(:execute, :is_element_displayed, id:
|
|
79
|
+
bridge.send(:execute, :is_element_displayed, id: native_id)
|
|
80
80
|
rescue Selenium::WebDriver::Error::UnknownCommandError
|
|
81
81
|
# If the is_element_displayed command is unknown, no point in trying again
|
|
82
82
|
driver.options[:native_displayed] = false
|
|
@@ -14,7 +14,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
|
14
14
|
warn 'You are attempting to click a table row which has issues in safaridriver - '\
|
|
15
15
|
'Your test should probably be clicking on a table cell like a user would. '\
|
|
16
16
|
'Clicking the first cell in the row instead.'
|
|
17
|
-
return find_css('th:first-child,td:first-child')[0].click(keys, options)
|
|
17
|
+
return find_css('th:first-child,td:first-child')[0].click(keys, **options)
|
|
18
18
|
end
|
|
19
19
|
raise
|
|
20
20
|
rescue ::Selenium::WebDriver::Error::WebDriverError => e
|
|
@@ -74,7 +74,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
|
74
74
|
if clear == :backspace
|
|
75
75
|
# Clear field by sending the correct number of backspace keys.
|
|
76
76
|
backspaces = [:backspace] * self.value.to_s.length
|
|
77
|
-
send_keys(
|
|
77
|
+
send_keys([:control, 'e'], *backspaces, value)
|
|
78
78
|
else
|
|
79
79
|
super.tap do
|
|
80
80
|
# React doesn't see the safaridriver element clear
|
|
@@ -16,16 +16,20 @@ module Capybara
|
|
|
16
16
|
|
|
17
17
|
def initialize(app)
|
|
18
18
|
@app = app
|
|
19
|
-
@
|
|
19
|
+
@disable_css_markup = format(DISABLE_CSS_MARKUP_TEMPLATE,
|
|
20
|
+
selector: self.class.selector_for(Capybara.disable_animation))
|
|
21
|
+
@disable_js_markup = format(DISABLE_JS_MARKUP_TEMPLATE,
|
|
22
|
+
selector: self.class.selector_for(Capybara.disable_animation))
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def call(env)
|
|
23
26
|
@status, @headers, @body = @app.call(env)
|
|
24
27
|
return [@status, @headers, @body] unless html_content?
|
|
25
28
|
|
|
29
|
+
nonces = directive_nonces.transform_values { |nonce| "nonce=\"#{nonce}\"" if nonce && !nonce.empty? }
|
|
26
30
|
response = Rack::Response.new([], @status, @headers)
|
|
27
31
|
|
|
28
|
-
@body.each { |html| response.write insert_disable(html) }
|
|
32
|
+
@body.each { |html| response.write insert_disable(html, nonces) }
|
|
29
33
|
@body.close if @body.respond_to?(:close)
|
|
30
34
|
|
|
31
35
|
response.finish
|
|
@@ -33,31 +37,45 @@ module Capybara
|
|
|
33
37
|
|
|
34
38
|
private
|
|
35
39
|
|
|
36
|
-
attr_reader :
|
|
40
|
+
attr_reader :disable_css_markup, :disable_js_markup
|
|
37
41
|
|
|
38
42
|
def html_content?
|
|
39
43
|
/html/.match?(@headers['Content-Type'])
|
|
40
44
|
end
|
|
41
45
|
|
|
42
|
-
def insert_disable(html)
|
|
43
|
-
html.sub(%r{(</
|
|
46
|
+
def insert_disable(html, nonces)
|
|
47
|
+
html.sub(%r{(</head>)}, "<style #{nonces['style-src']}>#{disable_css_markup}</style>\\1")
|
|
48
|
+
.sub(%r{(</body>)}, "<script #{nonces['script-src']}>#{disable_js_markup}</script>\\1")
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
def directive_nonces
|
|
52
|
+
@headers.fetch('Content-Security-Policy', '')
|
|
53
|
+
.split(';')
|
|
54
|
+
.map(&:split)
|
|
55
|
+
.to_h do |s|
|
|
56
|
+
[
|
|
57
|
+
s[0], s[1..].filter_map do |value|
|
|
58
|
+
/^'nonce-(?<nonce>.+)'/ =~ value
|
|
59
|
+
nonce
|
|
60
|
+
end[0]
|
|
61
|
+
]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
DISABLE_CSS_MARKUP_TEMPLATE = <<~CSS
|
|
66
|
+
%<selector>s, %<selector>s::before, %<selector>s::after {
|
|
67
|
+
transition: none !important;
|
|
68
|
+
animation-duration: 0s !important;
|
|
69
|
+
animation-delay: 0s !important;
|
|
70
|
+
scroll-behavior: auto !important;
|
|
71
|
+
}
|
|
72
|
+
CSS
|
|
73
|
+
|
|
74
|
+
DISABLE_JS_MARKUP_TEMPLATE = <<~SCRIPT
|
|
48
75
|
//<![CDATA[
|
|
49
76
|
(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);
|
|
50
77
|
//]]>
|
|
51
|
-
|
|
52
|
-
<style>
|
|
53
|
-
%<selector>s, %<selector>s::before, %<selector>s::after {
|
|
54
|
-
transition: none !important;
|
|
55
|
-
animation-duration: 0s !important;
|
|
56
|
-
animation-delay: 0s !important;
|
|
57
|
-
scroll-behavior: auto !important;
|
|
58
|
-
}
|
|
59
|
-
</style>
|
|
60
|
-
HTML
|
|
78
|
+
SCRIPT
|
|
61
79
|
end
|
|
62
80
|
end
|
|
63
81
|
end
|
|
@@ -100,7 +100,7 @@ module Capybara
|
|
|
100
100
|
remove_method :test_id=
|
|
101
101
|
##
|
|
102
102
|
#
|
|
103
|
-
# Set an
|
|
103
|
+
# Set an attribute to be optionally matched against the locator for builtin selector types.
|
|
104
104
|
# This attribute will be checked by builtin selector types whenever id would normally be checked.
|
|
105
105
|
# If `nil` then it will be ignored.
|
|
106
106
|
#
|
data/lib/capybara/session.rb
CHANGED
|
@@ -311,6 +311,16 @@ module Capybara
|
|
|
311
311
|
driver.send_keys(*args, **kw_args)
|
|
312
312
|
end
|
|
313
313
|
|
|
314
|
+
##
|
|
315
|
+
#
|
|
316
|
+
# Returns the element with focus.
|
|
317
|
+
#
|
|
318
|
+
# Not supported by Rack Test
|
|
319
|
+
#
|
|
320
|
+
def active_element
|
|
321
|
+
Capybara::Queries::ActiveElementQuery.new.resolve_for(self)[0].tap(&:allow_reload!)
|
|
322
|
+
end
|
|
323
|
+
|
|
314
324
|
##
|
|
315
325
|
#
|
|
316
326
|
# Executes the given block within the context of a node. {#within} takes the
|
|
@@ -408,11 +418,11 @@ module Capybara
|
|
|
408
418
|
idx = scopes.index(:frame)
|
|
409
419
|
top_level_scopes = [:frame, nil]
|
|
410
420
|
if idx
|
|
411
|
-
if scopes.slice(idx
|
|
421
|
+
if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
|
|
412
422
|
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
|
413
423
|
'`within` block.'
|
|
414
424
|
end
|
|
415
|
-
scopes.slice!(idx
|
|
425
|
+
scopes.slice!(idx..)
|
|
416
426
|
driver.switch_to_frame(:top)
|
|
417
427
|
end
|
|
418
428
|
else
|
|
@@ -755,33 +765,20 @@ module Capybara
|
|
|
755
765
|
end
|
|
756
766
|
|
|
757
767
|
NODE_METHODS.each do |method|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
def #{method}(...)
|
|
761
|
-
@touched = true
|
|
762
|
-
current_scope.#{method}(...)
|
|
763
|
-
end
|
|
764
|
-
METHOD
|
|
765
|
-
else
|
|
766
|
-
define_method method do |*args, &block|
|
|
768
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
769
|
+
def #{method}(...)
|
|
767
770
|
@touched = true
|
|
768
|
-
current_scope
|
|
771
|
+
current_scope.#{method}(...)
|
|
769
772
|
end
|
|
770
|
-
|
|
773
|
+
METHOD
|
|
771
774
|
end
|
|
772
775
|
|
|
773
776
|
DOCUMENT_METHODS.each do |method|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
document.#{method}(...)
|
|
778
|
-
end
|
|
779
|
-
METHOD
|
|
780
|
-
else
|
|
781
|
-
define_method method do |*args, &block|
|
|
782
|
-
document.send(method, *args, &block)
|
|
777
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
778
|
+
def #{method}(...)
|
|
779
|
+
document.#{method}(...)
|
|
783
780
|
end
|
|
784
|
-
|
|
781
|
+
METHOD
|
|
785
782
|
end
|
|
786
783
|
|
|
787
784
|
def inspect
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara::SpecHelper.spec '#active_element', requires: [:active_element] do
|
|
4
|
+
it 'should return the active element' do
|
|
5
|
+
@session.visit('/form')
|
|
6
|
+
@session.send_keys(:tab)
|
|
7
|
+
|
|
8
|
+
expect(@session.active_element).to match_selector(:css, '[tabindex="1"]')
|
|
9
|
+
|
|
10
|
+
@session.send_keys(:tab)
|
|
11
|
+
|
|
12
|
+
expect(@session.active_element).to match_selector(:css, '[tabindex="2"]')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should support reloading' do
|
|
16
|
+
@session.visit('/form')
|
|
17
|
+
expect(@session.active_element).to match_selector(:css, 'body')
|
|
18
|
+
@session.execute_script <<-JS
|
|
19
|
+
window.setTimeout(() => {
|
|
20
|
+
document.querySelector('#form_title').focus();
|
|
21
|
+
}, 1000)
|
|
22
|
+
JS
|
|
23
|
+
expect(@session.active_element).to match_selector(:css, 'body', wait: false)
|
|
24
|
+
expect(@session.active_element).to match_selector(:css, '#form_title', wait: 2)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should return a Capybara::Element' do
|
|
28
|
+
@session.visit('/form')
|
|
29
|
+
expect(@session.active_element).to be_a Capybara::Node::Element
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -132,7 +132,7 @@ Capybara::SpecHelper.spec '#all' do
|
|
|
132
132
|
it 'should use the sessions ignore_hidden_elements', psc: true do
|
|
133
133
|
Capybara.ignore_hidden_elements = true
|
|
134
134
|
@session.config.ignore_hidden_elements = false
|
|
135
|
-
expect(Capybara.ignore_hidden_elements).to
|
|
135
|
+
expect(Capybara.ignore_hidden_elements).to be(true)
|
|
136
136
|
expect(@session.all(:css, 'a.simple').size).to eq(2)
|
|
137
137
|
@session.config.ignore_hidden_elements = true
|
|
138
138
|
expect(@session.all(:css, 'a.simple').size).to eq(1)
|
|
@@ -203,19 +203,15 @@ Capybara::SpecHelper.spec '#all' do
|
|
|
203
203
|
expect { @session.all(:css, 'h1, p', between: 0..3) }.to raise_error(Capybara::ExpectationNotMet)
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
end
|
|
211
|
-
TEST
|
|
206
|
+
it 'treats an endless range as minimum' do
|
|
207
|
+
expect { @session.all(:css, 'h1, p', between: 2..) }.not_to raise_error
|
|
208
|
+
expect { @session.all(:css, 'h1, p', between: 5..) }.to raise_error(Capybara::ExpectationNotMet)
|
|
209
|
+
end
|
|
212
210
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
end
|
|
218
|
-
TEST
|
|
211
|
+
it 'treats a beginless range as maximum' do
|
|
212
|
+
expect { @session.all(:css, 'h1, p', between: ..7) }.not_to raise_error
|
|
213
|
+
expect { @session.all(:css, 'h1, p', between: ..3) }.to raise_error(Capybara::ExpectationNotMet)
|
|
214
|
+
end
|
|
219
215
|
end
|
|
220
216
|
|
|
221
217
|
context 'with multiple count filters' do
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
Capybara::SpecHelper.spec '#assert_text' do
|
|
4
4
|
it 'should be true if the given text is on the page' do
|
|
5
5
|
@session.visit('/with_html')
|
|
6
|
-
expect(@session.assert_text('est')).to
|
|
7
|
-
expect(@session.assert_text('Lorem')).to
|
|
8
|
-
expect(@session.assert_text('Redirect')).to
|
|
9
|
-
expect(@session.assert_text(:Redirect)).to
|
|
10
|
-
expect(@session.assert_text('text with whitespace')).to
|
|
6
|
+
expect(@session.assert_text('est')).to be(true)
|
|
7
|
+
expect(@session.assert_text('Lorem')).to be(true)
|
|
8
|
+
expect(@session.assert_text('Redirect')).to be(true)
|
|
9
|
+
expect(@session.assert_text(:Redirect)).to be(true)
|
|
10
|
+
expect(@session.assert_text('text with whitespace')).to be(true)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it 'should support collapsing whitespace' do
|
|
14
14
|
@session.visit('/with_html')
|
|
15
|
-
expect(@session.assert_text('text with whitespace', normalize_ws: true)).to
|
|
15
|
+
expect(@session.assert_text('text with whitespace', normalize_ws: true)).to be(true)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
context 'with enabled default collapsing whitespace' do
|
|
@@ -20,19 +20,19 @@ Capybara::SpecHelper.spec '#assert_text' do
|
|
|
20
20
|
|
|
21
21
|
it 'should be true if the given unnormalized text is on the page' do
|
|
22
22
|
@session.visit('/with_html')
|
|
23
|
-
expect(@session.assert_text('text with whitespace', normalize_ws: false)).to
|
|
23
|
+
expect(@session.assert_text('text with whitespace', normalize_ws: false)).to be(true)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it 'should support collapsing whitespace' do
|
|
27
27
|
@session.visit('/with_html')
|
|
28
|
-
expect(@session.assert_text('text with whitespace')).to
|
|
28
|
+
expect(@session.assert_text('text with whitespace')).to be(true)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it 'should take scopes into account' do
|
|
33
33
|
@session.visit('/with_html')
|
|
34
34
|
@session.within("//a[@title='awesome title']") do
|
|
35
|
-
expect(@session.assert_text('labore')).to
|
|
35
|
+
expect(@session.assert_text('labore')).to be(true)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -47,13 +47,13 @@ Capybara::SpecHelper.spec '#assert_text' do
|
|
|
47
47
|
|
|
48
48
|
it 'should be true if :all given and text is invisible.' do
|
|
49
49
|
@session.visit('/with_html')
|
|
50
|
-
expect(@session.assert_text(:all, 'Some of this text is hidden!')).to
|
|
50
|
+
expect(@session.assert_text(:all, 'Some of this text is hidden!')).to be(true)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
it 'should be true if `Capybara.ignore_hidden_elements = true` and text is invisible.' do
|
|
54
54
|
Capybara.ignore_hidden_elements = false
|
|
55
55
|
@session.visit('/with_html')
|
|
56
|
-
expect(@session.assert_text('Some of this text is hidden!')).to
|
|
56
|
+
expect(@session.assert_text('Some of this text is hidden!')).to be(true)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
it 'should raise error with a helpful message if the requested text is present but invisible' do
|
|
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#assert_text' do
|
|
|
88
88
|
|
|
89
89
|
it 'should be true if the text in the page matches given regexp' do
|
|
90
90
|
@session.visit('/with_html')
|
|
91
|
-
expect(@session.assert_text(/Lorem/)).to
|
|
91
|
+
expect(@session.assert_text(/Lorem/)).to be(true)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
it "should raise error if the text in the page doesn't match given regexp" do
|
|
@@ -109,13 +109,13 @@ Capybara::SpecHelper.spec '#assert_text' do
|
|
|
109
109
|
Capybara.default_max_wait_time = 2
|
|
110
110
|
@session.visit('/with_js')
|
|
111
111
|
@session.click_link('Click me')
|
|
112
|
-
expect(@session.assert_text('Has been clicked')).to
|
|
112
|
+
expect(@session.assert_text('Has been clicked')).to be(true)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
context 'with between' do
|
|
116
116
|
it 'should be true if the text occurs within the range given' do
|
|
117
117
|
@session.visit('/with_count')
|
|
118
|
-
expect(@session.assert_text('count', between: 1..3)).to
|
|
118
|
+
expect(@session.assert_text('count', between: 1..3)).to be(true)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
it 'should be false if the text occurs more or fewer times than range' do
|
|
@@ -203,13 +203,13 @@ Capybara::SpecHelper.spec '#assert_no_text' do
|
|
|
203
203
|
it 'should be true if scoped to an element which does not have the text' do
|
|
204
204
|
@session.visit('/with_html')
|
|
205
205
|
@session.within("//a[@title='awesome title']") do
|
|
206
|
-
expect(@session.assert_no_text('monkey')).to
|
|
206
|
+
expect(@session.assert_no_text('monkey')).to be(true)
|
|
207
207
|
end
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
it 'should be true if the given text is on the page but not visible' do
|
|
211
211
|
@session.visit('/with_html')
|
|
212
|
-
expect(@session.assert_no_text('Inside element with hidden ancestor')).to
|
|
212
|
+
expect(@session.assert_no_text('Inside element with hidden ancestor')).to be(true)
|
|
213
213
|
end
|
|
214
214
|
|
|
215
215
|
it 'should raise error if :all given and text is invisible.' do
|
|
@@ -236,7 +236,7 @@ Capybara::SpecHelper.spec '#assert_no_text' do
|
|
|
236
236
|
context 'with count' do
|
|
237
237
|
it 'should be true if the text occurs within the range given' do
|
|
238
238
|
@session.visit('/with_count')
|
|
239
|
-
expect(@session.assert_text('count', count: 2)).to
|
|
239
|
+
expect(@session.assert_text('count', count: 2)).to be(true)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
242
|
it 'should be false if the text occurs more or fewer times than range' do
|
|
@@ -236,6 +236,15 @@ Capybara::SpecHelper.spec '#check' do
|
|
|
236
236
|
expect(@session).to have_field('multi_label_checkbox', checked: true, visible: :hidden)
|
|
237
237
|
end
|
|
238
238
|
end
|
|
239
|
+
|
|
240
|
+
context 'with allow_label_click options', requires: [:js] do
|
|
241
|
+
it 'should allow offsets to click location on label' do
|
|
242
|
+
expect(@session.find(:checkbox, 'form_cars_lotus', unchecked: true, visible: :hidden)).to be_truthy
|
|
243
|
+
@session.check('form_cars_lotus', allow_label_click: { x: 90, y: 10 })
|
|
244
|
+
@session.click_button('awesome')
|
|
245
|
+
expect(extract_results(@session)['cars']).to include('lotus')
|
|
246
|
+
end
|
|
247
|
+
end
|
|
239
248
|
end
|
|
240
249
|
end
|
|
241
250
|
end
|
|
@@ -11,6 +11,12 @@ Capybara::SpecHelper.spec '#choose' do
|
|
|
11
11
|
expect(extract_results(@session)['gender']).to eq('male')
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
it 'ignores readonly attribute on radio buttons' do
|
|
15
|
+
@session.choose('gender_both')
|
|
16
|
+
@session.click_button('awesome')
|
|
17
|
+
expect(extract_results(@session)['gender']).to eq('both')
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
it 'should choose a radio button by label' do
|
|
15
21
|
@session.choose('Both')
|
|
16
22
|
@session.click_button('awesome')
|
|
@@ -528,4 +528,10 @@ Capybara::SpecHelper.spec '#find' do
|
|
|
528
528
|
expect(@session.find(:link, 'test-foo')[:id]).to eq 'foo'
|
|
529
529
|
end
|
|
530
530
|
end
|
|
531
|
+
|
|
532
|
+
it 'should warn if passed count options' do
|
|
533
|
+
allow(Capybara::Helpers).to receive(:warn)
|
|
534
|
+
@session.find('//h1', count: 44)
|
|
535
|
+
expect(Capybara::Helpers).to have_received(:warn).with(/'find' does not support count options/)
|
|
536
|
+
end
|
|
531
537
|
end
|
|
@@ -22,4 +22,8 @@ Capybara::SpecHelper.spec '#have_any_of_selectors' do
|
|
|
22
22
|
expect(@session).to have_any_of_selectors('p a#blah', 'h2#h2three')
|
|
23
23
|
end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
it 'should be negateable' do
|
|
27
|
+
expect(@session).not_to have_any_of_selectors(:css, 'span a#foo', 'h2#h2nope', 'h2#h2one_no')
|
|
28
|
+
end
|
|
25
29
|
end
|
|
@@ -65,6 +65,18 @@ Capybara::SpecHelper.spec '#has_button?' do
|
|
|
65
65
|
it 'should not affect other selectors when enable_aria_role: false' do
|
|
66
66
|
expect(@session).to have_button('Click me!', enable_aria_role: false)
|
|
67
67
|
end
|
|
68
|
+
|
|
69
|
+
context 'with focused:', requires: [:active_element] do
|
|
70
|
+
it 'should be true if a field has focus when focused: true' do
|
|
71
|
+
@session.send_keys(:tab)
|
|
72
|
+
|
|
73
|
+
expect(@session).to have_button('A Button', focused: true)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'should be true if a field does not have focus when focused: false' do
|
|
77
|
+
expect(@session).to have_button('A Button', focused: false)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
68
80
|
end
|
|
69
81
|
|
|
70
82
|
Capybara::SpecHelper.spec '#has_no_button?' do
|
|
@@ -117,4 +129,16 @@ Capybara::SpecHelper.spec '#has_no_button?' do
|
|
|
117
129
|
it 'should not affect other selectors when enable_aria_role: false' do
|
|
118
130
|
expect(@session).to have_no_button('Junk button that does not exist', enable_aria_role: false)
|
|
119
131
|
end
|
|
132
|
+
|
|
133
|
+
context 'with focused:', requires: [:active_element] do
|
|
134
|
+
it 'should be true if a button does not have focus when focused: true' do
|
|
135
|
+
expect(@session).to have_no_button('A Button', focused: true)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'should be false if a button has focus when focused: false' do
|
|
139
|
+
@session.send_keys(:tab)
|
|
140
|
+
|
|
141
|
+
expect(@session).to have_no_button('A Button', focused: false)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
120
144
|
end
|
|
@@ -16,8 +16,8 @@ Capybara::SpecHelper.spec '#has_current_path?' do
|
|
|
16
16
|
|
|
17
17
|
it 'should not raise an error when non-http' do
|
|
18
18
|
@session.reset_session!
|
|
19
|
-
expect(@session.has_current_path?(/monkey/)).to
|
|
20
|
-
expect(@session.has_current_path?('/with_js')).to
|
|
19
|
+
expect(@session.has_current_path?(/monkey/)).to be false
|
|
20
|
+
expect(@session.has_current_path?('/with_js')).to be false
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
it 'should handle non-escaped query options' do
|
|
@@ -110,6 +110,18 @@ Capybara::SpecHelper.spec '#has_field' do
|
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
context 'with focused:', requires: [:active_element] do
|
|
114
|
+
it 'should be true if a field has focus' do
|
|
115
|
+
2.times { @session.send_keys(:tab) }
|
|
116
|
+
|
|
117
|
+
expect(@session).to have_field('An Input', focused: true)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'should be false if a field does not have focus' do
|
|
121
|
+
expect(@session).to have_field('An Input', focused: false)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
113
125
|
context 'with valid', requires: [:js] do
|
|
114
126
|
it 'should be true if field is valid' do
|
|
115
127
|
@session.fill_in 'required', with: 'something'
|
|
@@ -184,6 +196,18 @@ Capybara::SpecHelper.spec '#has_no_field' do
|
|
|
184
196
|
expect(@session).to have_no_field('Languages', type: 'textarea')
|
|
185
197
|
end
|
|
186
198
|
end
|
|
199
|
+
|
|
200
|
+
context 'with focused:', requires: [:active_element] do
|
|
201
|
+
it 'should be true if a field does not have focus when focused: true' do
|
|
202
|
+
expect(@session).to have_no_field('An Input', focused: true)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it 'should be false if a field has focus when focused: true' do
|
|
206
|
+
2.times { @session.send_keys(:tab) }
|
|
207
|
+
|
|
208
|
+
expect(@session).not_to have_no_field('An Input', focused: true)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
187
211
|
end
|
|
188
212
|
|
|
189
213
|
Capybara::SpecHelper.spec '#has_checked_field?' do
|
|
@@ -359,7 +383,7 @@ Capybara::SpecHelper.spec '#has_no_unchecked_field?' do
|
|
|
359
383
|
end
|
|
360
384
|
|
|
361
385
|
it 'should support locator-less usage' do
|
|
362
|
-
expect(@session.has_no_unchecked_field?(disabled: false, id: 'form_disabled_unchecked_checkbox')).to
|
|
386
|
+
expect(@session.has_no_unchecked_field?(disabled: false, id: 'form_disabled_unchecked_checkbox')).to be true
|
|
363
387
|
expect(@session).to have_no_unchecked_field(disabled: false, id: 'form_disabled_unchecked_checkbox')
|
|
364
388
|
end
|
|
365
389
|
end
|