From 2296cadb934587e6c301e466b47555d8507dbddc Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 23 Feb 2009 19:51:50 -0500 Subject: [PATCH] Pulling features up from have_tag into have_selector --- lib/webrat/core/matchers/have_selector.rb | 85 ++++++++++++--- lib/webrat/core/matchers/have_tag.rb | 93 ++--------------- spec/public/matchers/have_selector_spec.rb | 83 +++++++++++---- spec/public/matchers/have_tag_spec.rb | 115 +-------------------- 4 files changed, 145 insertions(+), 231 deletions(-) diff --git a/lib/webrat/core/matchers/have_selector.rb b/lib/webrat/core/matchers/have_selector.rb index ea15d46e..bcdd2691 100644 --- a/lib/webrat/core/matchers/have_selector.rb +++ b/lib/webrat/core/matchers/have_selector.rb @@ -1,24 +1,79 @@ +require "webrat/core/matchers/have_xpath" + module Webrat module Matchers class HaveSelector < HaveXpath #:nodoc: - # ==== Returns # String:: The failure message. def failure_message - "expected following text to match selector #{@expected}:\n#{@document}" + "expected following output to contain a #{tag_inspect} tag:\n#{@document}" end - + # ==== Returns # String:: The failure message to be displayed in negative matches. def negative_failure_message - "expected following text to not match selector #{@expected}:\n#{@document}" + "expected following output to omit a #{tag_inspect}:\n#{@document}" end - + + def matches?(stringlike, &block) + @block ||= block + matched = matches(stringlike) + + options = @expected.last.dup + + if options[:count] + matched.size == options[:count] && (!@block || @block.call(matched)) + else + matched.any? && (!@block || @block.call(matched)) + end + end + + def tag_inspect + options = @expected.last.dup + content = options.delete(:content) + + html = "<#{@expected.first}" + options.each do |k,v| + html << " #{k}='#{v}'" + end + + if content + html << ">#{content}" + else + html << "/>" + end + + html + end + def query - Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath } + options = @expected.last.dup + selector = @expected.first.to_s + + options.each do |key, value| + next if [:content, :count].include?(key) + selector << "[#{key}='#{value}']" + end + + q = Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }.first + + if options[:content] && options[:content].include?("'") && options[:content].include?('"') + parts = options[:content].split("'").map do |part| + "'#{part}'" + end + + string = "concat(" + parts.join(", \"'\", ") + ")" + q << "[contains(., #{string})]" + elsif options[:content] && options[:content].include?("'") + q << "[contains(., \"#{options[:content]}\")]" + elsif options[:content] + q << "[contains(., '#{options[:content]}')]" + end + + q end - + end # Matches HTML content against a CSS 3 selector. @@ -28,24 +83,24 @@ def query # # ==== Returns # HaveSelector:: A new have selector matcher. - def have_selector(expected, &block) - HaveSelector.new(expected, &block) + def have_selector(name, attributes = {}, &block) + HaveSelector.new([name, attributes], &block) end alias_method :match_selector, :have_selector # Asserts that the body of the response contains # the supplied selector - def assert_have_selector(expected) - hs = HaveSelector.new(expected) - assert hs.matches?(response_body), hs.failure_message + def assert_have_selector(name, attributes = {}, &block) + matcher = HaveSelector.new([name, attributes], &block) + assert matcher.matches?(response_body), matcher.failure_message end # Asserts that the body of the response # does not contain the supplied string or regepx - def assert_have_no_selector(expected) - hs = HaveSelector.new(expected) - assert !hs.matches?(response_body), hs.negative_failure_message + def assert_have_no_selector(name, attributes = {}, &block) + matcher = HaveSelector.new([name, attributes], &block) + assert !matcher.matches?(response_body), matcher.negative_failure_message end end diff --git a/lib/webrat/core/matchers/have_tag.rb b/lib/webrat/core/matchers/have_tag.rb index 902a31a8..d233a48c 100644 --- a/lib/webrat/core/matchers/have_tag.rb +++ b/lib/webrat/core/matchers/have_tag.rb @@ -1,97 +1,20 @@ +require "webrat/core/matchers/have_selector" + module Webrat - module HaveTagMatcher - class HaveTag < ::Webrat::Matchers::HaveSelector #:nodoc: - # ==== Returns - # String:: The failure message. - def failure_message - "expected following output to contain a #{tag_inspect} tag:\n#{@document}" - end - - # ==== Returns - # String:: The failure message to be displayed in negative matches. - def negative_failure_message - "expected following output to omit a #{tag_inspect}:\n#{@document}" - end - - def matches?(stringlike, &block) - @block ||= block - matched = matches(stringlike) - - options = @expected.last.dup - - if options[:count] - matched.size == options[:count] && (!@block || @block.call(matched)) - else - matched.any? && (!@block || @block.call(matched)) - end - end - - def tag_inspect - options = @expected.last.dup - content = options.delete(:content) - - html = "<#{@expected.first}" - options.each do |k,v| - html << " #{k}='#{v}'" - end - - if content - html << ">#{content}" - else - html << "/>" - end - - html - end - - def query - options = @expected.last.dup - selector = @expected.first.to_s - - options.each do |key, value| - next if [:content, :count].include?(key) - selector << "[#{key}='#{value}']" - end - - q = Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }.first - - if options[:content] && options[:content].include?("'") && options[:content].include?('"') - parts = options[:content].split("'").map do |part| - "'#{part}'" - end - - string = "concat(" + parts.join(", \"'\", ") + ")" - q << "[contains(., #{string})]" - elsif options[:content] && options[:content].include?("'") - q << "[contains(., \"#{options[:content]}\")]" - elsif options[:content] - q << "[contains(., '#{options[:content]}')]" - end - - q - end - end - - def have_tag(name, attributes = {}, &block) - HaveTag.new([name, attributes], &block) + def have_tag(*args, &block) + have_selector(*args, &block) end alias_method :match_tag, :have_tag - # Asserts that the body of the response contains - # the supplied tag with the associated selectors - def assert_have_tag(name, attributes = {}) - ht = HaveTag.new([name, attributes]) - assert ht.matches?(response_body), ht.failure_message + def assert_have_tag(*args, &block) + assert_have_selector(*args, &block) end - # Asserts that the body of the response - # does not contain the supplied string or regepx - def assert_have_no_tag(name, attributes = {}) - ht = HaveTag.new([name, attributes]) - assert !ht.matches?(response_body), ht.negative_failure_message + def assert_have_no_tag(*args, &block) + assert_have_no_selector(*args, &block) end end diff --git a/spec/public/matchers/have_selector_spec.rb b/spec/public/matchers/have_selector_spec.rb index 24d9de9b..3b6f3b57 100644 --- a/spec/public/matchers/have_selector_spec.rb +++ b/spec/public/matchers/have_selector_spec.rb @@ -26,40 +26,85 @@ @body.should_not have_selector("p") end - it "should be able to loop over all the matched elements" do - @body.should have_selector("div") do |node| - node.first.name.should == "div" + describe "specifying attributes" do + it "should be able to specify the attributes of the tag" do + @body.should have_selector("div", :class => "inner") end end - it "should not match of any of the matchers in the block fail" do - lambda { - @body.should have_selector("div") do |node| - node.first.name.should == "p" - end - }.should raise_error(Spec::Expectations::ExpectationNotMetError) + describe "specifying content" do + it "should be able to specify the content of the tag" do + @body.should have_selector("div", :content => "hello, world!") + end + + it "should be able to specify the content of the tag with double quotes in it" do + @body.should have_selector("h2", :content => 'Welcome "Bryan"') + end + + it "should be able to specify the content of the tag with single quotes in it" do + @body.should have_selector("h3", :content => "Welcome 'Bryan'") + end + + it "should be able to specify the content of the tag with both kinds of quotes" do + @body.should have_selector("h4", :content => "Welcome 'Bryan\"") + end end - it "should be able to use #have_selector in the block" do - @body.should have_selector("#main") do |node| - node.should have_selector(".inner") + describe "specifying counts" do + it "should be able to specify the number of occurences of the tag" do + @body.should have_selector("li", :count => 2) + end + + it "should not match if the count is wrong" do + lambda { + @body.should have_selector("li", :count => 3) + }.should raise_error(Spec::Expectations::ExpectationNotMetError) end end - it "should not match any parent tags in the block" do - lambda { - @body.should have_selector(".inner") do |node| - node.should have_selector("#main") + describe "specifying nested elements" do + it "should be able to loop over all the matched elements" do + @body.should have_selector("div") do |node| + node.first.name.should == "div" end - }.should raise_error(Spec::Expectations::ExpectationNotMetError) + end + + it "should not match of any of the matchers in the block fail" do + lambda { + @body.should have_selector("div") do |node| + node.first.name.should == "p" + end + }.should raise_error(Spec::Expectations::ExpectationNotMetError) + end + + it "should be able to use #have_selector in the block" do + @body.should have_selector("#main") do |node| + node.should have_selector(".inner") + end + end + + it "should not match any parent tags in the block" do + lambda { + @body.should have_selector(".inner") do |node| + node.should have_selector("#main") + end + }.should raise_error(Spec::Expectations::ExpectationNotMetError) + end + + it "should work with items that have multiple child nodes" do + @body.should have_selector("ul") do |n| + n.should have_selector("li", :content => "First") + n.should have_selector("li", :content => "Second") + end + end end - describe "asserts for selector," do + describe "Test::Unit assertions" do include Test::Unit::Assertions before(:each) do - should_receive(:response_body).and_return @body require 'test/unit' + should_receive(:response_body).and_return @body end describe "assert_have_selector" do diff --git a/spec/public/matchers/have_tag_spec.rb b/spec/public/matchers/have_tag_spec.rb index 671d5e0f..583e8794 100644 --- a/spec/public/matchers/have_tag_spec.rb +++ b/spec/public/matchers/have_tag_spec.rb @@ -8,90 +8,14 @@ @body = <<-HTML
hello, world!
-

Welcome "Bryan"

-

Welcome 'Bryan'

-

Welcome 'Bryan"

-
HTML end - it "should be able to match a tag" do + it "should be an alias for have_selector" do @body.should have_tag("div") end - it "should not match the tag when it should not match" do - @body.should_not have_tag("p") - end - - it "should be able to specify the content of the tag" do - @body.should have_tag("div", :content => "hello, world!") - end - - it "should be able to specify the content of the tag with double quotes in it" do - @body.should have_tag("h2", :content => 'Welcome "Bryan"') - end - - it "should be able to specify the content of the tag with single quotes in it" do - @body.should have_tag("h3", :content => "Welcome 'Bryan'") - end - - it "should be able to specify the content of the tag with both kinds of quotes" do - @body.should have_tag("h4", :content => "Welcome 'Bryan\"") - end - - it "should be able to specify the number of occurences of the tag" do - @body.should have_tag("li", :count => 2) - end - - it "should not match if the count is wrong" do - lambda { - @body.should have_tag("li", :count => 3) - }.should raise_error(Spec::Expectations::ExpectationNotMetError) - end - - it "should be able to specify the attributes of the tag" do - @body.should have_tag("div", :class => "inner") - end - - it "should be able to loop over all the matched elements" do - @body.should have_tag("div") do |node| - node.first.name.should == "div" - end - end - - it "should not match of any of the matchers in the block fail" do - lambda { - @body.should have_tag("div") do |node| - node.first.name.should == "p" - end - }.should raise_error(Spec::Expectations::ExpectationNotMetError) - end - - it "should be able to use #have_tag in the block" do - @body.should have_tag("div", :id => "main") do |node| - node.should have_tag("div", :class => "inner") - end - end - - it "should not match any parent tags in the block" do - lambda { - @body.should have_tag("div", :class => "inner") do |node| - node.should have_tag("div", :id => "main") - end - }.should raise_error(Spec::Expectations::ExpectationNotMetError) - end - - it "should work with items that have multiple child nodes" do - @body.should have_tag("ul") do |n| - n.should have_tag("li", :content => "First") - n.should have_tag("li", :content => "Second") - end - end - describe "asserts for tags" do include Test::Unit::Assertions @@ -101,48 +25,15 @@ end describe "assert_have_tag" do - it "should pass when body contains the tag" do + it "should be an alias for assert_have_selector" do assert_have_tag("div") end - - it "should pass when finding with additional selectors" do - assert_have_tag("div", :class => "inner") - end - - - it "should throw an exception when the body doesn't have matching tag" do - lambda { - assert_have_tag("p") - }.should raise_error(Test::Unit::AssertionFailedError) - end - - it "should throw an exception when the body doesn't have a tag matching the attributes" do - lambda { - assert_have_tag("div", :class => "nope") - }.should raise_error(Test::Unit::AssertionFailedError) - end end describe "assert_have_no_tag" do - it "should pass when the body doesn't contan the tag" do + it "should be an alias for assert_have_no_selector" do assert_have_no_tag("p") end - - it "should pass when the body doesn't contain the tag due to additional selectors missing" do - assert_have_no_tag("div", :class => "nope") - end - - it "should throw an exception when the body does contain the tag" do - lambda { - assert_have_no_tag("div") - }.should raise_error(Test::Unit::AssertionFailedError) - end - - it "should throw an exception when the body contains the tag with additional selectors" do - lambda { - assert_have_no_tag("div", :class => "inner") - }.should raise_error(Test::Unit::AssertionFailedError) - end end end end