capybara 3.18.0 → 3.19.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 +16 -0
- data/README.md +14 -44
- data/lib/capybara/node/actions.rb +2 -2
- data/lib/capybara/node/element.rb +3 -5
- data/lib/capybara/queries/selector_query.rb +30 -11
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/result.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +2 -0
- data/lib/capybara/rspec/matchers/base.rb +2 -2
- data/lib/capybara/rspec/matchers/count_sugar.rb +36 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +3 -0
- data/lib/capybara/rspec/matchers/have_text.rb +3 -0
- data/lib/capybara/selector.rb +196 -599
- data/lib/capybara/selector/css.rb +2 -0
- data/lib/capybara/selector/definition.rb +276 -0
- data/lib/capybara/selector/definition/button.rb +46 -0
- data/lib/capybara/selector/definition/checkbox.rb +23 -0
- data/lib/capybara/selector/definition/css.rb +5 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +27 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +43 -0
- data/lib/capybara/selector/definition/link.rb +45 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +24 -0
- data/lib/capybara/selector/definition/select.rb +62 -0
- data/lib/capybara/selector/definition/table.rb +106 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/filters/base.rb +4 -0
- data/lib/capybara/selector/filters/locator_filter.rb +12 -2
- data/lib/capybara/selector/selector.rb +40 -452
- data/lib/capybara/selenium/driver.rb +4 -10
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -9
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +8 -0
- data/lib/capybara/selenium/extensions/find.rb +1 -1
- data/lib/capybara/selenium/logger_suppressor.rb +5 -0
- data/lib/capybara/selenium/node.rb +19 -13
- data/lib/capybara/selenium/nodes/chrome_node.rb +30 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +14 -12
- data/lib/capybara/selenium/nodes/ie_node.rb +11 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +7 -12
- data/lib/capybara/server/checker.rb +7 -3
- data/lib/capybara/session.rb +2 -2
- data/lib/capybara/spec/session/all_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/has_css_spec.rb +7 -0
- data/lib/capybara/spec/session/has_text_spec.rb +6 -0
- data/lib/capybara/spec/session/save_screenshot_spec.rb +11 -0
- data/lib/capybara/spec/session/select_spec.rb +0 -5
- data/lib/capybara/spec/test_app.rb +8 -3
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/minitest_spec_spec.rb +1 -0
- data/spec/selector_spec.rb +12 -6
- data/spec/selenium_spec_firefox.rb +0 -3
- data/spec/selenium_spec_firefox_remote.rb +0 -3
- data/spec/selenium_spec_ie.rb +3 -1
- data/spec/server_spec.rb +1 -1
- data/spec/shared_selenium_session.rb +1 -1
- data/spec/spec_helper.rb +9 -2
- metadata +54 -2
@@ -226,10 +226,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
226
226
|
@browser&.quit
|
227
227
|
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED # rubocop:disable Lint/HandleExceptions
|
228
228
|
# Browser must have already gone
|
229
|
-
rescue Selenium::WebDriver::Error::UnknownError =>
|
230
|
-
unless silenced_unknown_error_message?(
|
229
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
230
|
+
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
231
231
|
# probably already gone but not sure - so warn
|
232
|
-
warn "Ignoring Selenium UnknownError during driver quit: #{
|
232
|
+
warn "Ignoring Selenium UnknownError during driver quit: #{e.message}"
|
233
233
|
end
|
234
234
|
ensure
|
235
235
|
@browser = nil
|
@@ -283,13 +283,7 @@ private
|
|
283
283
|
end
|
284
284
|
|
285
285
|
def clear_browser_state_errors
|
286
|
-
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
|
287
|
-
unless selenium_4?
|
288
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
289
|
-
errors << Selenium::WebDriver::Error::UnhandledError
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
286
|
+
@clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError]
|
293
287
|
end
|
294
288
|
|
295
289
|
def unhandled_alert_errors
|
@@ -18,8 +18,8 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
18
18
|
|
19
19
|
def resize_window_to(handle, width, height)
|
20
20
|
super
|
21
|
-
rescue Selenium::WebDriver::Error::UnknownError =>
|
22
|
-
raise unless
|
21
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
22
|
+
raise unless e.message.match?(/failed to change window state/)
|
23
23
|
|
24
24
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
25
25
|
# and raises unnecessary error. Wait a bit and try again.
|
@@ -53,13 +53,7 @@ private
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def cdp_unsupported_errors
|
56
|
-
@cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
|
57
|
-
unless selenium_4?
|
58
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
59
|
-
errors << Selenium::WebDriver::Error::UnhandledError
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
56
|
+
@cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
|
63
57
|
end
|
64
58
|
|
65
59
|
def execute_cdp(cmd, params = {})
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'capybara/selenium/nodes/ie_node'
|
4
|
+
|
3
5
|
module Capybara::Selenium::Driver::InternetExplorerDriver
|
4
6
|
def switch_to_frame(frame)
|
5
7
|
return super unless frame == :parent
|
@@ -10,6 +12,12 @@ module Capybara::Selenium::Driver::InternetExplorerDriver
|
|
10
12
|
browser.switch_to.default_content
|
11
13
|
handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh) }
|
12
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_node(native_node, initial_cache = {})
|
19
|
+
::Capybara::Selenium::IENode.new(self, native_node, initial_cache)
|
20
|
+
end
|
13
21
|
end
|
14
22
|
|
15
23
|
module Capybara::Selenium
|
@@ -87,7 +87,7 @@ module Capybara
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
90
|
-
@@is_displayed_atom ||= begin
|
90
|
+
@@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
|
91
91
|
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
92
92
|
rescue StandardError
|
93
93
|
# If the atom doesn't exist or other error
|
@@ -56,10 +56,12 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
56
56
|
def set(value, **options)
|
57
57
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
58
58
|
|
59
|
-
tag_name, type = attrs(:tagName, :type)
|
60
|
-
|
59
|
+
tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
|
60
|
+
@tag_name ||= tag_name
|
61
|
+
|
62
|
+
case tag_name
|
61
63
|
when 'input'
|
62
|
-
case type
|
64
|
+
case type
|
63
65
|
when 'radio'
|
64
66
|
click
|
65
67
|
when 'checkbox'
|
@@ -78,7 +80,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
78
80
|
when 'textarea'
|
79
81
|
set_text(value, options)
|
80
82
|
else
|
81
|
-
set_content_editable(value)
|
83
|
+
set_content_editable(value)
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
@@ -136,7 +138,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
136
138
|
end
|
137
139
|
|
138
140
|
def tag_name
|
139
|
-
native.tag_name.downcase
|
141
|
+
@tag_name ||= native.tag_name.downcase
|
140
142
|
end
|
141
143
|
|
142
144
|
def visible?; boolean_attr(native.displayed?); end
|
@@ -291,20 +293,24 @@ private
|
|
291
293
|
# Ensure we are focused on the element
|
292
294
|
click
|
293
295
|
|
294
|
-
driver.execute_script <<-JS, self
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
296
|
+
editable = driver.execute_script <<-JS, self
|
297
|
+
if (arguments[0].isContentEditable) {
|
298
|
+
var range = document.createRange();
|
299
|
+
var sel = window.getSelection();
|
300
|
+
arguments[0].focus();
|
301
|
+
range.selectNodeContents(arguments[0]);
|
302
|
+
sel.removeAllRanges();
|
303
|
+
sel.addRange(range);
|
304
|
+
return true;
|
305
|
+
}
|
306
|
+
return false;
|
301
307
|
JS
|
302
308
|
|
303
309
|
# The action api has a speed problem but both chrome and firefox 58 raise errors
|
304
310
|
# if we use the faster direct send_keys. For now just send_keys to the element
|
305
311
|
# we've already focused.
|
306
312
|
# native.send_keys(value.to_s)
|
307
|
-
browser_action.send_keys(value.to_s).perform
|
313
|
+
browser_action.send_keys(value.to_s).perform if editable
|
308
314
|
end
|
309
315
|
|
310
316
|
def action_with_modifiers(click_options)
|
@@ -13,6 +13,14 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
16
|
+
# In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
|
17
|
+
if browser_version >= 75.0
|
18
|
+
driver.execute_script(<<~JS, self)
|
19
|
+
if (arguments[0].multiple && (arguments[0].files.length > 0)){
|
20
|
+
arguments[0].value = null;
|
21
|
+
}
|
22
|
+
JS
|
23
|
+
end
|
16
24
|
super(value)
|
17
25
|
rescue *file_errors => e
|
18
26
|
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
|
@@ -35,6 +43,18 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
35
43
|
raise
|
36
44
|
end
|
37
45
|
|
46
|
+
def disabled?
|
47
|
+
driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def select_option
|
51
|
+
# To optimize to only one check and then click
|
52
|
+
selected_or_disabled = driver.evaluate_script(<<~JS, self)
|
53
|
+
arguments[0].matches(':disabled, select:disabled *, :checked')
|
54
|
+
JS
|
55
|
+
click unless selected_or_disabled
|
56
|
+
end
|
57
|
+
|
38
58
|
private
|
39
59
|
|
40
60
|
def file_errors
|
@@ -46,4 +66,14 @@ private
|
|
46
66
|
def bridge
|
47
67
|
driver.browser.send(:bridge)
|
48
68
|
end
|
69
|
+
|
70
|
+
def w3c?
|
71
|
+
(defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= 4)) ||
|
72
|
+
driver.browser.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
73
|
+
end
|
74
|
+
|
75
|
+
def browser_version
|
76
|
+
caps = driver.browser.capabilities
|
77
|
+
(caps[:browser_version] || caps[:version]).to_f
|
78
|
+
end
|
49
79
|
end
|
@@ -18,22 +18,16 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def disabled?
|
21
|
-
|
22
|
-
return super unless browser_version < 61.0
|
23
|
-
|
24
|
-
return true if super
|
25
|
-
|
26
|
-
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
27
|
-
if %w[option optgroup].include? tag_name
|
28
|
-
find_xpath('parent::*[self::optgroup or self::select]')[0].disabled?
|
29
|
-
else
|
30
|
-
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
|
31
|
-
end
|
21
|
+
driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
|
32
22
|
end
|
33
23
|
|
34
24
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
35
25
|
# By default files are appended so we have to clear here if its multiple and already set
|
36
|
-
|
26
|
+
driver.execute_script(<<~JS, self)
|
27
|
+
if (arguments[0].multiple && (arguments[0].files.length > 0)){
|
28
|
+
arguments[0].value = null;
|
29
|
+
}
|
30
|
+
JS
|
37
31
|
return super if browser_version >= 62.0
|
38
32
|
|
39
33
|
# Workaround lack of support for multiple upload by uploading one at a time
|
@@ -65,6 +59,14 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
65
59
|
scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
|
66
60
|
end
|
67
61
|
|
62
|
+
def select_option
|
63
|
+
# To optimize to only one check and then click
|
64
|
+
selected_or_disabled = driver.evaluate_script(<<~JS, self)
|
65
|
+
arguments[0].matches(':disabled, select:disabled *, :checked')
|
66
|
+
JS
|
67
|
+
click unless selected_or_disabled
|
68
|
+
end
|
69
|
+
|
68
70
|
private
|
69
71
|
|
70
72
|
def click_with_options(click_options)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/selenium/extensions/html5_drag'
|
4
|
+
|
5
|
+
class Capybara::Selenium::IENode < Capybara::Selenium::Node
|
6
|
+
def disabled?
|
7
|
+
# TODO: Doesn't work for a bunch of cases - need to get IE running to see if it can be done like this
|
8
|
+
# driver.evaluate_script("arguments[0].msMatchesSelector(':disabled, select:disabled *')", self)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
@@ -19,8 +19,12 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def select_option
|
22
|
-
|
23
|
-
|
22
|
+
# To optimize to only one check and then click
|
23
|
+
selected_or_disabled = driver.execute_script(<<~JS, self)
|
24
|
+
arguments[0].closest('select').scrollIntoView();
|
25
|
+
return arguments[0].matches(':disabled, select:disabled *, :checked');
|
26
|
+
JS
|
27
|
+
click unless selected_or_disabled
|
24
28
|
end
|
25
29
|
|
26
30
|
def unselect_option
|
@@ -40,16 +44,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
40
44
|
end
|
41
45
|
|
42
46
|
def disabled?
|
43
|
-
|
44
|
-
|
45
|
-
# workaround for safaridriver reporting elements as enabled when they are nested in disabling elements
|
46
|
-
if %w[option optgroup].include? tag_name
|
47
|
-
return true if self[:disabled] == 'true'
|
48
|
-
|
49
|
-
find_xpath('parent::*[self::optgroup or self::select]')[0].disabled?
|
50
|
-
else
|
51
|
-
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
|
52
|
-
end
|
47
|
+
driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
|
53
48
|
end
|
54
49
|
|
55
50
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
@@ -12,7 +12,7 @@ module Capybara
|
|
12
12
|
|
13
13
|
def request(&block)
|
14
14
|
ssl? ? https_request(&block) : http_request(&block)
|
15
|
-
rescue *TRY_HTTPS_ERRORS #
|
15
|
+
rescue *TRY_HTTPS_ERRORS # _rubocop:disable Naming/RescuedExceptionsVariableName
|
16
16
|
res = https_request(&block)
|
17
17
|
@ssl = true
|
18
18
|
res
|
@@ -25,11 +25,15 @@ module Capybara
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def http_request(&block)
|
28
|
-
|
28
|
+
make_request(read_timeout: 2, &block)
|
29
29
|
end
|
30
30
|
|
31
31
|
def https_request(&block)
|
32
|
-
|
32
|
+
make_request(ssl_options, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def make_request(**options, &block)
|
36
|
+
Net::HTTP.start(@host, @port, options.merge(max_retries: 0), &block)
|
33
37
|
end
|
34
38
|
|
35
39
|
def ssl_options
|
data/lib/capybara/session.rb
CHANGED
@@ -77,7 +77,7 @@ module Capybara
|
|
77
77
|
def initialize(mode, app = nil)
|
78
78
|
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.' if app && !app.respond_to?(:call)
|
79
79
|
|
80
|
-
@@instance_created = true
|
80
|
+
@@instance_created = true # rubocop:disable Style/ClassVars
|
81
81
|
@mode = mode
|
82
82
|
@app = app
|
83
83
|
if block_given?
|
@@ -807,7 +807,7 @@ module Capybara
|
|
807
807
|
|
808
808
|
private
|
809
809
|
|
810
|
-
@@instance_created = false
|
810
|
+
@@instance_created = false # rubocop:disable Style/ClassVars
|
811
811
|
|
812
812
|
def driver_args(args)
|
813
813
|
args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
|
@@ -30,7 +30,7 @@ Capybara::SpecHelper.spec '#all' do
|
|
30
30
|
|
31
31
|
it 'should accept an XPath instance', :exact_false do
|
32
32
|
@session.visit('/form')
|
33
|
-
@xpath = Capybara::Selector
|
33
|
+
@xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('Name')
|
34
34
|
expect(@xpath).to be_a(::XPath::Union)
|
35
35
|
@result = @session.all(@xpath).map(&:value)
|
36
36
|
expect(@result).to include('Smith', 'John', 'John Smith')
|
@@ -235,7 +235,7 @@ Capybara::SpecHelper.spec '#find' do
|
|
235
235
|
|
236
236
|
it 'should accept an XPath instance' do
|
237
237
|
@session.visit('/form')
|
238
|
-
@xpath = Capybara::Selector
|
238
|
+
@xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('First Name')
|
239
239
|
expect(@xpath).to be_a(::XPath::Union)
|
240
240
|
expect(@session.find(@xpath).value).to eq('John')
|
241
241
|
end
|
@@ -24,7 +24,7 @@ Capybara::SpecHelper.spec '#first' do
|
|
24
24
|
|
25
25
|
it 'should accept an XPath instance' do
|
26
26
|
@session.visit('/form')
|
27
|
-
@xpath = Capybara::Selector
|
27
|
+
@xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('First Name')
|
28
28
|
expect(@xpath).to be_a(::XPath::Union)
|
29
29
|
expect(@session.first(@xpath).value).to eq('John')
|
30
30
|
end
|
@@ -146,7 +146,9 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
146
146
|
context 'with count' do
|
147
147
|
it 'should be true if the content occurs the given number of times' do
|
148
148
|
expect(@session).to have_css('p', count: 3)
|
149
|
+
expect(@session).to have_css('p').exactly(3).times
|
149
150
|
expect(@session).to have_css('p a#foo', count: 1)
|
151
|
+
expect(@session).to have_css('p a#foo').once
|
150
152
|
expect(@session).to have_css('p a.doesnotexist', count: 0)
|
151
153
|
expect(@session).to have_css('li', class: /guitar|drummer/, count: 4)
|
152
154
|
expect(@session).to have_css('li', id: /john|paul/, class: /guitar|drummer/, count: 2)
|
@@ -161,6 +163,7 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
161
163
|
|
162
164
|
it 'should be false if the content occurs a different number of times than the given' do
|
163
165
|
expect(@session).not_to have_css('p', count: 6)
|
166
|
+
expect(@session).not_to have_css('p').exactly(5).times
|
164
167
|
expect(@session).not_to have_css('p a#foo', count: 2)
|
165
168
|
expect(@session).not_to have_css('p a.doesnotexist', count: 1)
|
166
169
|
end
|
@@ -175,6 +178,7 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
175
178
|
it 'should be true when content occurs same or fewer times than given' do
|
176
179
|
expect(@session).to have_css('h2.head', maximum: 5) # edge case
|
177
180
|
expect(@session).to have_css('h2', maximum: 10)
|
181
|
+
expect(@session).to have_css('h2').at_most(10).times
|
178
182
|
expect(@session).to have_css('p a.doesnotexist', maximum: 1)
|
179
183
|
expect(@session).to have_css('p a.doesnotexist', maximum: 0)
|
180
184
|
end
|
@@ -182,6 +186,7 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
182
186
|
it 'should be false when content occurs more times than given' do
|
183
187
|
expect(@session).not_to have_css('h2.head', maximum: 4) # edge case
|
184
188
|
expect(@session).not_to have_css('h2', maximum: 3)
|
189
|
+
expect(@session).not_to have_css('h2').at_most(3).times
|
185
190
|
expect(@session).not_to have_css('p', maximum: 1)
|
186
191
|
end
|
187
192
|
|
@@ -195,12 +200,14 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
195
200
|
it 'should be true when content occurs same or more times than given' do
|
196
201
|
expect(@session).to have_css('h2.head', minimum: 5) # edge case
|
197
202
|
expect(@session).to have_css('h2', minimum: 3)
|
203
|
+
expect(@session).to have_css('h2').at_least(2).times
|
198
204
|
expect(@session).to have_css('p a.doesnotexist', minimum: 0)
|
199
205
|
end
|
200
206
|
|
201
207
|
it 'should be false when content occurs fewer times than given' do
|
202
208
|
expect(@session).not_to have_css('h2.head', minimum: 6) # edge case
|
203
209
|
expect(@session).not_to have_css('h2', minimum: 8)
|
210
|
+
expect(@session).not_to have_css('h2').at_least(8).times
|
204
211
|
expect(@session).not_to have_css('p', minimum: 10)
|
205
212
|
expect(@session).not_to have_css('p a.doesnotexist', minimum: 1)
|
206
213
|
end
|
@@ -166,12 +166,14 @@ Capybara::SpecHelper.spec '#has_text?' do
|
|
166
166
|
it 'should be true if the text occurs the given number of times' do
|
167
167
|
@session.visit('/with_count')
|
168
168
|
expect(@session).to have_text('count', count: 2)
|
169
|
+
expect(@session).to have_text('count').exactly(2).times
|
169
170
|
end
|
170
171
|
|
171
172
|
it 'should be false if the text occurs a different number of times than the given' do
|
172
173
|
@session.visit('/with_count')
|
173
174
|
expect(@session).not_to have_text('count', count: 0)
|
174
175
|
expect(@session).not_to have_text('count', count: 1)
|
176
|
+
expect(@session).not_to have_text('count').once
|
175
177
|
expect(@session).not_to have_text(/count/, count: 3)
|
176
178
|
end
|
177
179
|
|
@@ -186,12 +188,14 @@ Capybara::SpecHelper.spec '#has_text?' do
|
|
186
188
|
it 'should be true when text occurs same or fewer times than given' do
|
187
189
|
@session.visit('/with_count')
|
188
190
|
expect(@session).to have_text('count', maximum: 2)
|
191
|
+
expect(@session).to have_text('count').at_most(2).times
|
189
192
|
expect(@session).to have_text(/count/, maximum: 3)
|
190
193
|
end
|
191
194
|
|
192
195
|
it 'should be false when text occurs more times than given' do
|
193
196
|
@session.visit('/with_count')
|
194
197
|
expect(@session).not_to have_text('count', maximum: 1)
|
198
|
+
expect(@session).not_to have_text('count').at_most(1).times
|
195
199
|
expect(@session).not_to have_text('count', maximum: 0)
|
196
200
|
end
|
197
201
|
|
@@ -206,12 +210,14 @@ Capybara::SpecHelper.spec '#has_text?' do
|
|
206
210
|
it 'should be true when text occurs same or more times than given' do
|
207
211
|
@session.visit('/with_count')
|
208
212
|
expect(@session).to have_text('count', minimum: 2)
|
213
|
+
expect(@session).to have_text('count').at_least(2).times
|
209
214
|
expect(@session).to have_text(/count/, minimum: 0)
|
210
215
|
end
|
211
216
|
|
212
217
|
it 'should be false when text occurs fewer times than given' do
|
213
218
|
@session.visit('/with_count')
|
214
219
|
expect(@session).not_to have_text('count', minimum: 3)
|
220
|
+
expect(@session).not_to have_text('count').at_least(3).times
|
215
221
|
end
|
216
222
|
|
217
223
|
it 'should coerce minimum to an integer' do
|