Permalink
Browse files

Merge pull request #1232 from centro/fix-count-query-options

Enable use of count query options with Capybara::Node::Finders#all
  • Loading branch information...
jnicklas committed Feb 20, 2014
2 parents 77320df + 5ae5fb0 commit 595cd4a89bc5c7873e74a3b05761d27c30cc198a
Showing with 137 additions and 18 deletions.
  1. +20 −12 lib/capybara/helpers.rb
  2. +22 −1 lib/capybara/node/finders.rb
  3. +24 −5 lib/capybara/node/matchers.rb
  4. +71 −0 lib/capybara/spec/session/all_spec.rb
View
@@ -51,8 +51,10 @@ def inject_asset_host(html)
##
#
- # Checks if the given count matches the given count options. By default,
- # when no options are given, count should be larger than zero.
+ # Checks if the given count matches the given count options.
+ # Defaults to true if no options are specified. If multiple
+ # options are provided, it tests that all conditions are met;
+ # however, if :count is supplied, all other options are ignored.
#
# @param [Integer] count The actual number. Should be coercible via Integer()
# @option [Range] between Count must be within the given range
@@ -61,17 +63,23 @@ def inject_asset_host(html)
# @option [Integer] minimum Count must be larger than or equal to this value
#
def matches_count?(count, options={})
- case
- when options[:between]
- options[:between] === count
- when options[:count]
- Integer(options[:count]) == count
- when options[:maximum]
- Integer(options[:maximum]) >= count
- when options[:minimum]
- Integer(options[:minimum]) <= count
+ return (Integer(options[:count]) == count) if options[:count]
+ return false if options[:maximum] && (Integer(options[:maximum]) < count)
+ return false if options[:minimum] && (Integer(options[:minimum]) > count)
+ return false if options[:between] && !(options[:between] === count)
+ return true
+ end
+
+ ##
+ #
+ # Checks if a count of 0 is valid for the given options hash.
+ # Returns false if options hash does not specify any count options.
+ #
+ def expects_none?(options={})
+ if [:count, :maximum, :minimum, :between].any? { |k| options.has_key? k }
+ matches_count?(0,options)
else
- count > 0
+ false
end
end
@@ -116,17 +116,38 @@ def find_by_id(id, options={})
# page.all('a', :text => 'Home')
# page.all('#menu li', :visible => true)
#
+ # By default if no elements are found, an empty array is returned;
+ # however, expectations can be set on the number of elements to be
+ # found using:
+ #
+ # page.assert_selector('p#foo', :count => 4)
+ # page.assert_selector('p#foo', :maximum => 10)
+ # page.assert_selector('p#foo', :minimum => 1)
+ # page.assert_selector('p#foo', :between => 1..10)
+ #
+ # See {Capybara::Helpers#matches_count?} for additional information about
+ # count matching.
+ #
# @overload all([kind], locator, options)
# @param [:css, :xpath] kind The type of selector
# @param [String] locator The selector
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
# @option options [Boolean] visible Only find elements that are visible on the page. Setting this to false
# finds invisible _and_ visible elements.
+ # @option options [Integer] count Exact number of matches that are expected to be found
+ # @option options [Integer] maximum Maximum number of matches that are expected to be found
+ # @option options [Integer] minimum Minimum number of matches that are expected to be found
+ # @option options [Range] between Number of matches found must be within the given range
# @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
# @return [Capybara::Result] A collection of found elements
#
def all(*args)
- resolve_query(Capybara::Query.new(*args))
+ query = Capybara::Query.new(*args)
+ synchronize(query.wait) do
+ result = resolve_query(query)
+ raise(Capybara::ExpectationNotMet, result.failure_message) unless result.matches_count?
+ result
+ end
end
##
@@ -68,7 +68,11 @@ def has_no_selector?(*args)
#
# page.assert_selector('p#foo', :count => 4)
#
- # This will check if the expression occurs exactly 4 times.
+ # This will check if the expression occurs exactly 4 times. See
+ # {Capybara::Node::Finders#all} for other available result size options.
+ #
+ # If a :count of 0 is specified, it will behave like {#assert_no_selector};
+ # however, use of that method is preferred over this one.
#
# It also accepts all options that {Capybara::Node::Finders#all} accepts,
# such as :text and :visible.
@@ -88,7 +92,7 @@ def assert_selector(*args)
query = Capybara::Query.new(*args)
synchronize(query.wait) do
result = all(*args)
- result.matches_count? or raise Capybara::ExpectationNotMet, result.failure_message
+ raise Capybara::ExpectationNotMet, result.failure_message if result.size == 0 && !Capybara::Helpers.expects_none?(query.options)
end
return true
end
@@ -98,14 +102,29 @@ def assert_selector(*args)
# Asserts that a given selector is not on the page or current node.
# Usage is identical to Capybara::Node::Matchers#assert_selector
#
+ # Query options such as :count, :minimum, :maximum, and :between are
+ # considered to be an integral part of the selector. This will return
+ # true, for example, if a page contains 4 anchors but the query expects 5:
+ #
+ # page.assert_no_selector('a', :minimum => 1) # Found, raises Capybara::ExpectationNotMet
+ # page.assert_no_selector('a', :count => 4) # Found, raises Capybara::ExpectationNotMet
+ # page.assert_no_selector('a', :count => 5) # Not Found, returns true
+ #
# @param (see Capybara::Node::Finders#assert_selector)
# @raise [Capybara::ExpectationNotMet] If the selector exists
#
def assert_no_selector(*args)
query = Capybara::Query.new(*args)
synchronize(query.wait) do
- result = all(*args)
- result.matches_count? and raise Capybara::ExpectationNotMet, result.negative_failure_message
+ begin
+ result = all(*args)
+ rescue Capybara::ExpectationNotMet => e
+ return true
+ else
+ if result.size > 0 || (result.size == 0 && Capybara::Helpers.expects_none?(query.options))
+ raise(Capybara::ExpectationNotMet, result.negative_failure_message)
+ end
+ end
end
return true
end
@@ -471,7 +490,7 @@ def text_found?(*args)
content, options = args
count = Capybara::Helpers.normalize_whitespace(text(type)).scan(Capybara::Helpers.to_regexp(content)).count
- Capybara::Helpers.matches_count?(count, options || {})
+ Capybara::Helpers.matches_count?(count, {:minimum=>1}.merge(options || {}))
end
end
end
@@ -67,6 +67,77 @@
end
end
+ context 'with element count filters' do
+ context ':count' do
+ it 'should succeed when the number of elements founds matches the expectation' do
+ expect { @session.all(:css, 'h1, p', :count => 4) }.to_not raise_error
+ end
+ it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
+ expect { @session.all(:css, 'h1, p', :count => 5) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ end
+ context ':minimum' do
+ it 'should succeed when the number of elements founds matches the expectation' do
+ expect { @session.all(:css, 'h1, p', :minimum => 0) }.to_not raise_error
+ end
+ it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
+ expect { @session.all(:css, 'h1, p', :minimum => 5) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ end
+ context ':maximum' do
+ it 'should succeed when the number of elements founds matches the expectation' do
+ expect { @session.all(:css, 'h1, p', :maximum => 4) }.to_not raise_error
+ end
+ it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
+ expect { @session.all(:css, 'h1, p', :maximum => 0) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ end
+ context ':between' do
+ it 'should succeed when the number of elements founds matches the expectation' do
+ expect { @session.all(:css, 'h1, p', :between => 2..7) }.to_not raise_error
+ end
+ it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
+ expect { @session.all(:css, 'h1, p', :between => 0..3) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ end
+
+ context 'with multiple count filters' do
+ it 'ignores other filters when :count is specified' do
+ o = {:count => 4,
+ :minimum => 5,
+ :maximum => 0,
+ :between => 0..3}
+ expect { @session.all(:css, 'h1, p', o) }.to_not raise_error
+ end
+ context 'with no :count expectation' do
+ it 'fails if :minimum is not met' do
+ o = {:minimum => 5,
+ :maximum => 4,
+ :between => 2..7}
+ expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ it 'fails if :maximum is not met' do
+ o = {:minimum => 0,
+ :maximum => 0,
+ :between => 2..7}
+ expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ it 'fails if :between is not met' do
+ o = {:minimum => 0,
+ :maximum => 4,
+ :between => 0..3}
+ expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
+ end
+ it 'succeeds if all combineable expectations are met' do
+ o = {:minimum => 0,
+ :maximum => 4,
+ :between => 2..7}
+ expect { @session.all(:css, 'h1, p', o) }.to_not raise_error
+ end
+ end
+ end
+ end
+
context "within a scope" do
before do
@session.visit('/with_scope')

0 comments on commit 595cd4a

Please sign in to comment.