diff --git a/lib/capybara/node/finders.rb b/lib/capybara/node/finders.rb index eb7ee606..7b582373 100644 --- a/lib/capybara/node/finders.rb +++ b/lib/capybara/node/finders.rb @@ -28,8 +28,8 @@ module Capybara # @return [Capybara::Node::Element] The found element # @raise [Capybara::ElementNotFound] If the element can't be found before time expires # - def find(*args) - query = Capybara::Queries::SelectorQuery.new(*args) + def find(*args, &optional_filter_block) + query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block) synchronize(query.wait) do if (query.match == :smart or query.match == :prefer_exact) and query.supports_exact? result = query.resolve_for(self, true) @@ -74,9 +74,9 @@ module Capybara # @return [Capybara::Node::Element] The found element # - def find_field(locator=nil, options={}) + def find_field(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - find(:field, locator, options) + find(:field, locator, options, &optional_filter_block) end alias_method :field_labeled, :find_field @@ -96,9 +96,9 @@ module Capybara # @option options [String, Array] class Match links that match the class(es) provided # @return [Capybara::Node::Element] The found element # - def find_link(locator=nil, options={}) + def find_link(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - find(:link, locator, options) + find(:link, locator, options, &optional_filter_block) end ## @@ -125,9 +125,9 @@ module Capybara # @option options [String, Array] class Match links that match the class(es) provided # @return [Capybara::Node::Element] The found element # - def find_button(locator=nil, options={}) + def find_button(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - find(:button, locator, options) + find(:button, locator, options, &optional_filter_block) end ## @@ -140,11 +140,12 @@ module Capybara # # @return [Capybara::Node::Element] The found element # - def find_by_id(id, options={}) - find(:id, id, options) + def find_by_id(id, options={}, &optional_filter_block) + find(:id, id, options, &optional_filter_block) end ## + # @!method all([kind = Capybara.default_selector], locator = nil, options = {}) # # Find all elements on the page matching the given selector # and options. @@ -184,26 +185,29 @@ module Capybara # 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, Symbol] visible Only find elements with the specified visibility: + # @param [Symbol] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to Capybara.default_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, Symbol] visible Only find elements with the specified visibility: # * true - only finds visible elements. # * false - finds invisible _and_ visible elements. # * :all - same as false; finds visible and invisible elements. # * :hidden - only finds invisible elements. # * :visible - same as true; only finds 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 - # @option options [Integer] wait (Capybara.default_max_wait_time) The time to wait for element count expectations to become true + # @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 + # @option options [Integer] wait (Capybara.default_max_wait_time) The time to wait for element count expectations to become true + # @overload all([kind = Capybara.default_selector], locator = nil, options = {}) + # @overload all([kind = Capybara.default_selector], locator = nil, options = {}, &filter_block) + # @yieldparam element [Capybara::Node::Element] The element being considered for inclusion in the results + # @yieldreturn [Boolean] Should the element be considered in the results? # @return [Capybara::Result] A collection of found elements # - def all(*args) - query = Capybara::Queries::SelectorQuery.new(*args) + def all(*args, &optional_filter_block) + query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block) synchronize(query.wait) do result = query.resolve_for(self) raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count? @@ -227,15 +231,16 @@ module Capybara # @param [Hash] options Additional options; see {#all} # @return [Capybara::Node::Element] The found element or nil # - def first(*args) + def first(*args, &optional_filter_block) if Capybara.wait_on_first_by_default options = if args.last.is_a?(Hash) then args.pop.dup else {} end args.push({minimum: 1}.merge(options)) end - all(*args).first + all(*args, &optional_filter_block).first rescue Capybara::ExpectationNotMet nil end + end end end diff --git a/lib/capybara/node/matchers.rb b/lib/capybara/node/matchers.rb index a5443b5c..222a0718 100644 --- a/lib/capybara/node/matchers.rb +++ b/lib/capybara/node/matchers.rb @@ -36,8 +36,8 @@ module Capybara # @option args [Range] :between (nil) Range of times that should contain number of times text occurs # @return [Boolean] If the expression exists # - def has_selector?(*args) - assert_selector(*args) + def has_selector?(*args, &optional_filter_block) + assert_selector(*args, &optional_filter_block) rescue Capybara::ExpectationNotMet return false end @@ -50,83 +50,12 @@ module Capybara # @param (see Capybara::Node::Finders#has_selector?) # @return [Boolean] # - def has_no_selector?(*args) - assert_no_selector(*args) + def has_no_selector?(*args, &optional_filter_block) + assert_no_selector(*args, &optional_filter_block) rescue Capybara::ExpectationNotMet return false end - ## - # - # Checks if the current node matches given selector - # - # @param (see Capybara::Node::Finders#has_selector?) - # @return [Boolean] - # - def matches_selector?(*args) - assert_matches_selector(*args) - rescue Capybara::ExpectationNotMet - return false - end - - ## - # - # Checks if the current node matches given XPath expression - # - # @param [String, XPath::Expression] xpath The XPath expression to match against the current code - # @return [Boolean] - # - def matches_xpath?(xpath, options={}) - matches_selector?(:xpath, xpath, options) - end - - ## - # - # Checks if the current node matches given CSS selector - # - # @param [String] css The CSS selector to match against the current code - # @return [Boolean] - # - def matches_css?(css, options={}) - matches_selector?(:css, css, options) - end - - ## - # - # Checks if the current node does not match given selector - # Usage is identical to Capybara::Node::Matchers#has_selector? - # - # @param (see Capybara::Node::Finders#has_selector?) - # @return [Boolean] - # - def not_matches_selector?(*args) - assert_not_matches_selector(*args) - rescue Capybara::ExpectationNotMet - return false - end - - ## - # - # Checks if the current node does not match given XPath expression - # - # @param [String, XPath::Expression] xpath The XPath expression to match against the current code - # @return [Boolean] - # - def not_matches_xpath?(xpath, options={}) - not_matches_selector?(:xpath, xpath, options) - end - - ## - # - # Checks if the current node does not match given CSS selector - # - # @param [String] css The CSS selector to match against the current code - # @return [Boolean] - # - def not_matches_css?(css, options={}) - not_matches_selector?(:css, css, options) - end - ## # # Asserts that a given selector is on the page or a descendant of the current node. @@ -160,8 +89,8 @@ module Capybara # @option options [Integer] :count (nil) Number of times the expression should occur # @raise [Capybara::ExpectationNotMet] If the selector does not exist # - def assert_selector(*args) - query = Capybara::Queries::SelectorQuery.new(*args) + def assert_selector(*args, &optional_filter_block) + query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block) synchronize(query.wait) do result = query.resolve_for(self) unless result.matches_count? && ((!result.empty?) || query.expects_none?) @@ -187,8 +116,8 @@ module Capybara # @param (see Capybara::Node::Finders#assert_selector) # @raise [Capybara::ExpectationNotMet] If the selector exists # - def assert_no_selector(*args) - query = Capybara::Queries::SelectorQuery.new(*args) + def assert_no_selector(*args, &optional_filter_block) + query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block) synchronize(query.wait) do result = query.resolve_for(self) if result.matches_count? && ((!result.empty?) || query.expects_none?) @@ -199,45 +128,6 @@ module Capybara end alias_method :refute_selector, :assert_no_selector - ## - # - # Asserts that the current_node matches a given selector - # - # node.assert_matches_selector('p#foo') - # node.assert_matches_selector(:xpath, '//p[@id="foo"]') - # node.assert_matches_selector(:foo) - # - # It also accepts all options that {Capybara::Node::Finders#all} accepts, - # such as :text and :visible. - # - # node.assert_matches_selector('li', :text => 'Horse', :visible => true) - # - # @param (see Capybara::Node::Finders#all) - # @raise [Capybara::ExpectationNotMet] If the selector does not match - # - def assert_matches_selector(*args) - query = Capybara::Queries::MatchQuery.new(*args) - synchronize(query.wait) do - result = query.resolve_for(self.query_scope) - unless result.include? self - raise Capybara::ExpectationNotMet, "Item does not match the provided selector" - end - end - return true - end - - def assert_not_matches_selector(*args) - query = Capybara::Queries::MatchQuery.new(*args) - synchronize(query.wait) do - result = query.resolve_for(self.query_scope) - if result.include? self - raise Capybara::ExpectationNotMet, 'Item matched the provided selector' - end - end - return true - end - alias_method :refute_matches_selector, :assert_not_matches_selector - ## # # Checks if a given XPath expression is on the page or a descendant of the current node. @@ -267,8 +157,8 @@ module Capybara # @option options [Integer] :count (nil) Number of times the expression should occur # @return [Boolean] If the expression exists # - def has_xpath?(path, options={}) - has_selector?(:xpath, path, options) + def has_xpath?(path, options={}, &optional_filter_block) + has_selector?(:xpath, path, options, &optional_filter_block) end ## @@ -279,8 +169,8 @@ module Capybara # @param (see Capybara::Node::Finders#has_xpath?) # @return [Boolean] # - def has_no_xpath?(path, options={}) - has_no_selector?(:xpath, path, options) + def has_no_xpath?(path, options={}, &optional_filter_block) + has_no_selector?(:xpath, path, options, &optional_filter_block) end ## @@ -306,8 +196,8 @@ module Capybara # @option options [Integer] :count (nil) Number of times the selector should occur # @return [Boolean] If the selector exists # - def has_css?(path, options={}) - has_selector?(:css, path, options) + def has_css?(path, options={}, &optional_filter_block) + has_selector?(:css, path, options, &optional_filter_block) end ## @@ -318,8 +208,8 @@ module Capybara # @param (see Capybara::Node::Finders#has_css?) # @return [Boolean] # - def has_no_css?(path, options={}) - has_no_selector?(:css, path, options) + def has_no_css?(path, options={}, &optional_filter_block) + has_no_selector?(:css, path, options, &optional_filter_block) end ## @@ -332,9 +222,9 @@ module Capybara # @option options [String, Regexp] :href The value the href attribute must be # @return [Boolean] Whether it exists # - def has_link?(locator=nil, options={}) + def has_link?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:link, locator, options) + has_selector?(:link, locator, options, &optional_filter_block) end ## @@ -345,9 +235,9 @@ module Capybara # @param (see Capybara::Node::Finders#has_link?) # @return [Boolean] Whether it doesn't exist # - def has_no_link?(locator=nil, options={}) + def has_no_link?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:link, locator, options) + has_no_selector?(:link, locator, options, &optional_filter_block) end ## @@ -358,9 +248,9 @@ module Capybara # @param [String] locator The text, value or id of a button to check for # @return [Boolean] Whether it exists # - def has_button?(locator=nil, options={}) + def has_button?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:button, locator, options) + has_selector?(:button, locator, options, &optional_filter_block) end ## @@ -371,9 +261,9 @@ module Capybara # @param [String] locator The text, value or id of a button to check for # @return [Boolean] Whether it doesn't exist # - def has_no_button?(locator=nil, options={}) + def has_no_button?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:button, locator, options) + has_no_selector?(:button, locator, options, &optional_filter_block) end ## @@ -398,9 +288,9 @@ module Capybara # @option options [String] :type The type attribute of the field # @return [Boolean] Whether it exists # - def has_field?(locator=nil, options={}) + def has_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:field, locator, options) + has_selector?(:field, locator, options, &optional_filter_block) end ## @@ -413,9 +303,9 @@ module Capybara # @option options [String] :type The type attribute of the field # @return [Boolean] Whether it doesn't exist # - def has_no_field?(locator=nil, options={}) + def has_no_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:field, locator, options) + has_no_selector?(:field, locator, options, &optional_filter_block) end ## @@ -427,9 +317,9 @@ module Capybara # @param [String] locator The label, name or id of a checked field # @return [Boolean] Whether it exists # - def has_checked_field?(locator=nil, options={}) + def has_checked_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:field, locator, options.merge(:checked => true)) + has_selector?(:field, locator, options.merge(:checked => true), &optional_filter_block) end ## @@ -441,7 +331,7 @@ module Capybara # @param [String] locator The label, name or id of a checked field # @return [Boolean] Whether it doesn't exist # - def has_no_checked_field?(locator=nil, options={}) + def has_no_checked_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash has_no_selector?(:field, locator, options.merge(:checked => true)) end @@ -455,9 +345,9 @@ module Capybara # @param [String] locator The label, name or id of an unchecked field # @return [Boolean] Whether it exists # - def has_unchecked_field?(locator=nil, options={}) + def has_unchecked_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:field, locator, options.merge(:unchecked => true)) + has_selector?(:field, locator, options.merge(:unchecked => true), &optional_filter_block) end ## @@ -469,9 +359,9 @@ module Capybara # @param [String] locator The label, name or id of an unchecked field # @return [Boolean] Whether it doesn't exist # - def has_no_unchecked_field?(locator=nil, options={}) + def has_no_unchecked_field?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:field, locator, options.merge(:unchecked => true)) + has_no_selector?(:field, locator, options.merge(:unchecked => true), &optional_filter_block) end ## @@ -502,9 +392,9 @@ module Capybara # @option options [String, Array] :selected Options which should be selected # @return [Boolean] Whether it exists # - def has_select?(locator=nil, options={}) + def has_select?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:select, locator, options) + has_selector?(:select, locator, options, &optional_filter_block) end ## @@ -515,9 +405,9 @@ module Capybara # @param (see Capybara::Node::Matchers#has_select?) # @return [Boolean] Whether it doesn't exist # - def has_no_select?(locator=nil, options={}) + def has_no_select?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:select, locator, options) + has_no_selector?(:select, locator, options, &optional_filter_block) end ## @@ -530,9 +420,9 @@ module Capybara # @param [String] locator The id or caption of a table # @return [Boolean] Whether it exist # - def has_table?(locator=nil, options={}) + def has_table?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_selector?(:table, locator, options) + has_selector?(:table, locator, options, &optional_filter_block) end ## @@ -543,11 +433,122 @@ module Capybara # @param (see Capybara::Node::Matchers#has_table?) # @return [Boolean] Whether it doesn't exist # - def has_no_table?(locator=nil, options={}) + def has_no_table?(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - has_no_selector?(:table, locator, options) + has_no_selector?(:table, locator, options, &optional_filter_block) end + ## + # + # Asserts that the current_node matches a given selector + # + # node.assert_matches_selector('p#foo') + # node.assert_matches_selector(:xpath, '//p[@id="foo"]') + # node.assert_matches_selector(:foo) + # + # It also accepts all options that {Capybara::Node::Finders#all} accepts, + # such as :text and :visible. + # + # node.assert_matches_selector('li', :text => 'Horse', :visible => true) + # + # @param (see Capybara::Node::Finders#all) + # @raise [Capybara::ExpectationNotMet] If the selector does not match + # + def assert_matches_selector(*args, &optional_filter_block) + query = Capybara::Queries::MatchQuery.new(*args, &optional_filter_block) + synchronize(query.wait) do + result = query.resolve_for(self.query_scope) + unless result.include? self + raise Capybara::ExpectationNotMet, "Item does not match the provided selector" + end + end + return true + end + + def assert_not_matches_selector(*args, &optional_filter_block) + query = Capybara::Queries::MatchQuery.new(*args) + synchronize(query.wait) do + result = query.resolve_for(self.query_scope) + if result.include? self + raise Capybara::ExpectationNotMet, 'Item matched the provided selector' + end + end + return true + end + alias_method :refute_matches_selector, :assert_not_matches_selector + + ## + # + # Checks if the current node matches given selector + # + # @param (see Capybara::Node::Finders#has_selector?) + # @return [Boolean] + # + def matches_selector?(*args, &optional_filter_block) + assert_matches_selector(*args, &optional_filter_block) + rescue Capybara::ExpectationNotMet + return false + end + + ## + # + # Checks if the current node matches given XPath expression + # + # @param [String, XPath::Expression] xpath The XPath expression to match against the current code + # @return [Boolean] + # + def matches_xpath?(xpath, options={}, &optional_filter_block) + matches_selector?(:xpath, xpath, options, &optional_filter_block) + end + + ## + # + # Checks if the current node matches given CSS selector + # + # @param [String] css The CSS selector to match against the current code + # @return [Boolean] + # + def matches_css?(css, options={}, &optional_filter_block) + matches_selector?(:css, css, options) + end + + ## + # + # Checks if the current node does not match given selector + # Usage is identical to Capybara::Node::Matchers#has_selector? + # + # @param (see Capybara::Node::Finders#has_selector?) + # @return [Boolean] + # + def not_matches_selector?(*args, &optional_filter_block) + assert_not_matches_selector(*args, &optional_filter_block) + rescue Capybara::ExpectationNotMet + return false + end + + ## + # + # Checks if the current node does not match given XPath expression + # + # @param [String, XPath::Expression] xpath The XPath expression to match against the current code + # @return [Boolean] + # + def not_matches_xpath?(xpath, options={}, &optional_filter_block) + not_matches_selector?(:xpath, xpath, options, &optional_filter_block) + end + + ## + # + # Checks if the current node does not match given CSS selector + # + # @param [String] css The CSS selector to match against the current code + # @return [Boolean] + # + def not_matches_css?(css, options={}, &optional_filter_block) + not_matches_selector?(:css, css, options, &optional_filter_block) + end + + ## # Asserts that the page or current node has the given text content, # ignoring any HTML tags. @@ -643,6 +644,7 @@ module Capybara def ==(other) self.eql?(other) || (other.respond_to?(:base) && base == other.base) end + end end end diff --git a/lib/capybara/queries/selector_query.rb b/lib/capybara/queries/selector_query.rb index 9061ef49..3ce97b5d 100644 --- a/lib/capybara/queries/selector_query.rb +++ b/lib/capybara/queries/selector_query.rb @@ -7,8 +7,9 @@ module Capybara VALID_KEYS = COUNT_KEYS + [:text, :id, :class, :visible, :exact, :match, :wait, :filter_set] VALID_MATCH = [:first, :smart, :prefer_exact, :one] - def initialize(*args) + def initialize(*args, &filter_block) @options = if args.last.is_a?(Hash) then args.pop.dup else {} end + @filter_block = filter_block if args[0].is_a?(Symbol) @selector = Selector.all.fetch(args.shift) do |selector_type| @@ -45,6 +46,7 @@ module Capybara @description << " with id #{options[:id]}" if options[:id] @description << " with classes #{Array(options[:class]).join(',')}]" if options[:class] @description << selector.description(options) + @description << " that also matches the custom filter block" if @filter_block @description end @@ -59,7 +61,7 @@ module Capybara when :hidden then return false if node.visible? end - query_filters.all? do |name, filter| + res = query_filters.all? do |name, filter| if options.has_key?(name) filter.matches?(node, options[name]) elsif filter.default? @@ -68,6 +70,9 @@ module Capybara true end end + + res &&= @filter_block.call(node) unless @filter_block.nil? + res end def visible diff --git a/lib/capybara/rspec/matchers.rb b/lib/capybara/rspec/matchers.rb index 5071850a..30386103 100644 --- a/lib/capybara/rspec/matchers.rb +++ b/lib/capybara/rspec/matchers.rb @@ -16,8 +16,9 @@ module Capybara class HaveSelector < Matcher attr_reader :failure_message, :failure_message_when_negated - def initialize(*args) + def initialize(*args, &filter_block) @args = args + @filter_block = filter_block end def matches?(actual) @@ -39,7 +40,7 @@ module Capybara end def query - @query ||= Capybara::Queries::SelectorQuery.new(*@args) + @query ||= Capybara::Queries::SelectorQuery.new(*@args, &@filter_block) end # RSpec 2 compatibility: @@ -221,8 +222,8 @@ module Capybara alias_method :failure_message_for_should_not, :failure_message_when_negated end - def have_selector(*args) - HaveSelector.new(*args) + def have_selector(*args, &optional_filter_block) + HaveSelector.new(*args, &optional_filter_block) end def match_selector(*args) @@ -233,16 +234,16 @@ module Capybara ::RSpec::Matchers.define_negated_matcher :not_match_selector, :match_selector if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.1')) - def have_xpath(xpath, options={}) - HaveSelector.new(:xpath, xpath, options) + def have_xpath(xpath, options={}, &optional_filter_block) + HaveSelector.new(:xpath, xpath, options, &optional_filter_block) end def match_xpath(xpath, options={}) MatchSelector.new(:xpath, xpath, options) end - def have_css(css, options={}) - HaveSelector.new(:css, css, options) + def have_css(css, options={}, &optional_filter_block) + HaveSelector.new(:css, css, options, &optional_filter_block) end def match_css(css, options={}) @@ -262,39 +263,39 @@ module Capybara HaveCurrentPath.new(path, options) end - def have_link(locator=nil, options={}) + def have_link(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:link, locator, options) + HaveSelector.new(:link, locator, options, &optional_filter_block) end - def have_button(locator=nil, options={}) + def have_button(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:button, locator, options) + HaveSelector.new(:button, locator, options, &optional_filter_block) end - def have_field(locator=nil, options={}) + def have_field(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:field, locator, options) + HaveSelector.new(:field, locator, options, &optional_filter_block) end - def have_checked_field(locator=nil, options={}) + def have_checked_field(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:field, locator, options.merge(:checked => true)) + HaveSelector.new(:field, locator, options.merge(:checked => true), &optional_filter_block) end - def have_unchecked_field(locator=nil, options={}) + def have_unchecked_field(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:field, locator, options.merge(:unchecked => true)) + HaveSelector.new(:field, locator, options.merge(:unchecked => true), &optional_filter_block) end - def have_select(locator=nil, options={}) + def have_select(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:select, locator, options) + HaveSelector.new(:select, locator, options, &optional_filter_block) end - def have_table(locator=nil, options={}) + def have_table(locator=nil, options={}, &optional_filter_block) locator, options = nil, locator if locator.is_a? Hash - HaveSelector.new(:table, locator, options) + HaveSelector.new(:table, locator, options, &optional_filter_block) end ## @@ -306,5 +307,6 @@ module Capybara def become_closed(options = {}) BecomeClosed.new(options) end + end end diff --git a/lib/capybara/spec/session/element/matches_selector_spec.rb b/lib/capybara/spec/session/element/matches_selector_spec.rb index 10f7e737..9173cba7 100644 --- a/lib/capybara/spec/session/element/matches_selector_spec.rb +++ b/lib/capybara/spec/session/element/matches_selector_spec.rb @@ -48,6 +48,13 @@ Capybara::SpecHelper.spec '#match_selector?' do cbox = @session.find(:css, '#form_pets_dog') expect(cbox.matches_selector?(:checkbox, id: 'form_pets_dog', option: 'dog', name: 'form[pets][]', checked: true)).to be true end + + it 'should accept a custom filter block' do + @session.visit('/form') + cbox = @session.find(:css, '#form_pets_dog') + expect(cbox.matches_selector?(:checkbox){ |node| node[:id] == "form_pets_dog"}).to be true + expect(cbox.matches_selector?(:checkbox){ |node| node[:id] != "form_pets_dog"}).to be false + end end Capybara::SpecHelper.spec '#not_matches_selector?' do diff --git a/lib/capybara/spec/session/find_field_spec.rb b/lib/capybara/spec/session/find_field_spec.rb index 6d79bd2c..50a36e54 100644 --- a/lib/capybara/spec/session/find_field_spec.rb +++ b/lib/capybara/spec/session/find_field_spec.rb @@ -8,7 +8,6 @@ Capybara::SpecHelper.spec '#find_field' do expect(@session.find_field('Dog').value).to eq('dog') expect(@session.find_field('form_description').text).to eq('Descriptive text goes here') expect(@session.find_field('Region')[:name]).to eq('form[region]') - end context "aria_label attribute with Capybara.enable_aria_label" do @@ -107,4 +106,9 @@ Capybara::SpecHelper.spec '#find_field' do expect(@session.find_field(with: 'dog')['id']).to eq "form_pets_dog" end end + + it "should accept an optional filter block" do + # this would be better done with the :with option but this is just a test + expect(@session.find_field('form[pets][]'){ |node| node.value == 'dog' }[:id]).to eq "form_pets_dog" + end end diff --git a/lib/capybara/spec/session/find_spec.rb b/lib/capybara/spec/session/find_spec.rb index b118307a..61ff06d2 100644 --- a/lib/capybara/spec/session/find_spec.rb +++ b/lib/capybara/spec/session/find_spec.rb @@ -399,6 +399,10 @@ Capybara::SpecHelper.spec '#find' do end end + it "supports a custom filter block" do + expect(@session.find(:css, 'input'){|node| node.disabled? }[:name]).to eq('disabled_text') + end + context "within a scope" do before do @session.visit('/with_scope')