diff --git a/lib/capybara/rspec/matchers.rb b/lib/capybara/rspec/matchers.rb index 51f83a78..870f09a9 100644 --- a/lib/capybara/rspec/matchers.rb +++ b/lib/capybara/rspec/matchers.rb @@ -12,14 +12,16 @@ module Capybara end end - def matches?(actual) + private + + def wrap_matches?(actual) yield(wrap(actual)) rescue Capybara::ExpectationNotMet => e @failure_message = e.message return false end - def does_not_match?(actual) + def wrap_does_not_match?(actual) yield(wrap(actual)) rescue Capybara::ExpectationNotMet => e @failure_message_when_negated = e.message @@ -36,11 +38,11 @@ module Capybara end def matches?(actual) - super(actual){ |el| el.assert_selector(*@args) } + wrap_matches?(actual){ |el| el.assert_selector(*@args, &@filter_block) } end def does_not_match?(actual) - super(actual){ |el| el.assert_no_selector(*@args) } + wrap_does_not_match?(actual){ |el| el.assert_no_selector(*@args, &@filter_block) } end def description @@ -56,6 +58,24 @@ module Capybara alias_method :failure_message_for_should_not, :failure_message_when_negated end + class MatchSelector < HaveSelector + def matches?(actual) + wrap_matches?(actual) { |el| el.assert_matches_selector(*@args, &@filter_block) } + end + + def does_not_match?(actual) + wrap_does_not_match?(actual) { |el| el.assert_not_matches_selector(*@args, &@filter_block) } + end + + def description + "match #{query.description}" + end + + def query + @query ||= Capybara::Queries::MatchQuery.new(*@args) + end + end + class HaveText < Matcher attr_reader :type, :content, :options @@ -71,11 +91,11 @@ module Capybara end def matches?(actual) - super(actual) { |el| el.assert_text(*@args) } + wrap_matches?(actual) { |el| el.assert_text(*@args) } end def does_not_match?(actual) - super(actual) { |el| el.assert_no_text(*@args) } + wrap_does_not_match?(actual) { |el| el.assert_no_text(*@args) } end def description @@ -105,11 +125,11 @@ module Capybara end def matches?(actual) - super(actual) { |el| el.assert_title(*@args) } + wrap_matches?(actual) { |el| el.assert_title(*@args) } end def does_not_match?(actual) - super(actual) { |el| el.assert_no_title(*@args) } + wrap_does_not_match?(actual) { |el| el.assert_no_title(*@args) } end def description @@ -134,11 +154,11 @@ module Capybara end def matches?(actual) - super(actual) { |el| el.assert_current_path(*@args) } + wrap_matches?(actual) { |el| el.assert_current_path(*@args) } end def does_not_match?(actual) - super(actual) { |el| el.assert_no_current_path(*@args) } + wrap_does_not_match?(actual) { |el| el.assert_no_current_path(*@args) } end def description @@ -178,40 +198,12 @@ module Capybara alias_method :failure_message_for_should_not, :failure_message_when_negated end - class MatchSelector < Matcher - attr_reader :failure_message, :failure_message_when_negated - - def initialize(*args) - @args = args - end - - def matches?(actual) - super(actual) { |el| el.assert_matches_selector(*@args) } - end - - def does_not_match?(actual) - super(actual) { |el| el.assert_not_matches_selector(*@args) } - end - - def description - "match #{query.description}" - end - - def query - @query ||= Capybara::Queries::MatchQuery.new(*@args) - end - - # RSpec 2 compatibility: - alias_method :failure_message_for_should, :failure_message - alias_method :failure_message_for_should_not, :failure_message_when_negated - end - def have_selector(*args, &optional_filter_block) HaveSelector.new(*args, &optional_filter_block) end - def match_selector(*args) - MatchSelector.new(*args) + def match_selector(*args, &optional_filter_block) + MatchSelector.new(*args, &optional_filter_block) end # defined_negated_matcher was added in RSpec 3.1 - it's syntactic sugar only since a user can do # expect(page).not_to match_selector, so not sure we really need to support not_match_selector for prior to RSpec 3.1 @@ -222,16 +214,16 @@ module Capybara HaveSelector.new(:xpath, xpath, options, &optional_filter_block) end - def match_xpath(xpath, options={}) - MatchSelector.new(:xpath, xpath, options) + def match_xpath(xpath, options={}, &optional_filter_block) + MatchSelector.new(:xpath, xpath, options, &optional_filter_block) end def have_css(css, options={}, &optional_filter_block) HaveSelector.new(:css, css, options, &optional_filter_block) end - def match_css(css, options={}) - MatchSelector.new(:css, css, options) + def match_css(css, options={}, &optional_filter_block) + MatchSelector.new(:css, css, options, &optional_filter_block) end def have_text(*args) diff --git a/lib/capybara/spec/session/element/assert_match_selector.rb b/lib/capybara/spec/session/element/assert_match_selector.rb index f52b8c7f..68c0f47f 100644 --- a/lib/capybara/spec/session/element/assert_match_selector.rb +++ b/lib/capybara/spec/session/element/assert_match_selector.rb @@ -28,4 +28,9 @@ Capybara::SpecHelper.spec '#assert_matches_selector' do it "should not accept count options" do expect { @element.assert_matches_selector(:css, '.number', count: 1) }.to raise_error(ArgumentError) end + + it "should accept a filter block" do + @element.assert_matches_selector(:css, 'span') { |el| el[:class] == "number" } + @element.assert_not_matches_selector(:css,'span') { |el| el[:class] == "not_number" } + end end diff --git a/lib/capybara/spec/session/element/match_css_spec.rb b/lib/capybara/spec/session/element/match_css_spec.rb index 12e58d4c..45d5fbe0 100644 --- a/lib/capybara/spec/session/element/match_css_spec.rb +++ b/lib/capybara/spec/session/element/match_css_spec.rb @@ -14,4 +14,10 @@ Capybara::SpecHelper.spec '#match_css?' do expect(@element).not_to match_css("p a#doesnotexist") expect(@element).not_to match_css("p.nosuchclass") end + + it "should accept an optional filter block" do + # This would be better done with + expect(@element).to match_css('span') { |el| el[:class] == "number" } + expect(@element).not_to match_css('span') { |el| el[:class] == "not_number" } + 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 36c37dbe..ed1cdf80 100644 --- a/lib/capybara/spec/session/element/matches_selector_spec.rb +++ b/lib/capybara/spec/session/element/matches_selector_spec.rb @@ -52,6 +52,8 @@ Capybara::SpecHelper.spec '#match_selector?' do it 'should accept a custom filter block' do @session.visit('/form') cbox = @session.find(:css, '#form_pets_dog') + expect(cbox).to match_selector(:checkbox){ |node| node[:id] == "form_pets_dog"} + expect(cbox).not_to match_selector(:checkbox){ |node| node[:id] != "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 diff --git a/lib/capybara/spec/session/has_selector_spec.rb b/lib/capybara/spec/session/has_selector_spec.rb index 27d5a722..23469efa 100644 --- a/lib/capybara/spec/session/has_selector_spec.rb +++ b/lib/capybara/spec/session/has_selector_spec.rb @@ -29,6 +29,10 @@ Capybara::SpecHelper.spec '#has_selector?' do end end + it "should accept a filter block" do + expect(@session).to have_selector(:css, "a", count: 1) { |el| el[:id] == "foo" } + end + context "with count" do it "should be true if the content is on the page the given number of times" do expect(@session).to have_selector("//p", count: 3) @@ -105,6 +109,13 @@ Capybara::SpecHelper.spec '#has_no_selector?' do end end + it "should accept a filter block" do + if !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0')) + skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher" + end + expect(@session).to have_no_selector(:css, "a#foo") { |el| el[:id] != "foo" } + end + context "with count" do it "should be false if the content is on the page the given number of times" do expect(@session).not_to have_no_selector("//p", count: 3)