Skip to content

Commit

Permalink
Merge branch 'master' into atmos
Browse files Browse the repository at this point in the history
  • Loading branch information
brynary committed Feb 28, 2009
2 parents 120a53d + b7e0d6f commit 4115c99
Show file tree
Hide file tree
Showing 13 changed files with 552 additions and 433 deletions.
17 changes: 15 additions & 2 deletions History.txt
@@ -1,11 +1,24 @@
== Git
== 0.4.2 / 2009-02-24

* Major enhancements

* Significant improvements to have_selector. It now supports specifying
attributes in a hash and :count and :content options. See
have_selector_spec.rb for more.
* Add the same functionality mentioned above to have_xpath

* Minor enhancements

* Detect infinite redirects and raise a Webrat::InfiniteRedirectError (Daniel Lucraft)
* Squeeze extra whitespace out of failures messages from contain
matcher
* Detect infinite redirects and raise a Webrat::InfiniteRedirectError
(Daniel Lucraft)

* Bug fixes

* Properly quote single and double quotes strings in XPath
* Fix warning caused by Nokogiri deprecating CSS::Parser.parse
(Aaron Patterson)
* Accept do/end blocks in matchers. [#157] (Peter Jaros)
* Quote --chdir option to mongrel_rails to support RAILS_ROOTs with spaces
(T.J. VanSlyke)
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -28,7 +28,7 @@ spec = Gem::Specification.new do |s|
s.extra_rdoc_files = %w(README.rdoc MIT-LICENSE.txt)

# Dependencies
s.add_dependency "nokogiri", ">= 1.1.0"
s.add_dependency "nokogiri", ">= 1.2.0"

s.rubyforge_project = "webrat"
end
Expand Down
2 changes: 1 addition & 1 deletion lib/webrat.rb
Expand Up @@ -7,7 +7,7 @@ module Webrat
class WebratError < StandardError
end

VERSION = '0.4.1'
VERSION = '0.4.2'

def self.require_xml
gem "nokogiri", ">= 1.0.6"
Expand Down
10 changes: 7 additions & 3 deletions lib/webrat/core/matchers/have_content.rb
Expand Up @@ -26,15 +26,19 @@ def matches?(stringlike)
# ==== Returns
# String:: The failure message.
def failure_message
"expected the following element's content to #{content_message}:\n#{@element}"
"expected the following element's content to #{content_message}:\n#{squeeze_space(@element)}"
end

# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected the following element's content to not #{content_message}:\n#{@element}"
"expected the following element's content to not #{content_message}:\n#{squeeze_space(@element)}"
end


def squeeze_space(inner_text)
inner_text.gsub(/^\s*$/, "").squeeze("\n")
end

def content_message
case @content
when String
Expand Down
52 changes: 37 additions & 15 deletions lib/webrat/core/matchers/have_selector.rb
@@ -1,24 +1,46 @@
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 tag_inspect
options = @options.dup
count = options.delete(:count)
content = options.delete(:content)

html = "<#{@expected}"
options.each do |k,v|
html << " #{k}='#{v}'"
end

if content
html << ">#{content}</#{@expected}>"
else
html << "/>"
end

html
end

def query
Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
Nokogiri::CSS.parse(@expected.to_s).map do |ast|
ast.to_xpath
end.first
end

end

# Matches HTML content against a CSS 3 selector.
Expand All @@ -28,24 +50,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
Expand Down
66 changes: 8 additions & 58 deletions lib/webrat/core/matchers/have_tag.rb
@@ -1,70 +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 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}</#{@expected.first}>"
else
html << "/>"
end

html
end

def query
options = @expected.last.dup
selector = @expected.first.to_s

selector << ":contains('#{options.delete(:content)}')" if options[:content]

options.each do |key, value|
selector << "[#{key}='#{value}']"
end

Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }
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
Expand Down
90 changes: 71 additions & 19 deletions lib/webrat/core/matchers/have_xpath.rb
Expand Up @@ -5,55 +5,90 @@ module Webrat
module Matchers

class HaveXpath #:nodoc:
def initialize(expected, &block)
def initialize(expected, options = {}, &block)
@expected = expected
@options = options
@block = block
end

def matches?(stringlike, &block)
@block ||= block
matched = matches(stringlike)

if @options[:count]
matched.size == @options[:count] && (!@block || @block.call(matched))
else
matched.any? && (!@block || @block.call(matched))
end
end

def matches(stringlike)
if Webrat.configuration.parse_with_nokogiri?
matches_nokogiri?(stringlike)
nokogiri_matches(stringlike)
else
matches_rexml?(stringlike)
rexml_matches(stringlike)
end
end

def matches_rexml?(stringlike)
def rexml_matches(stringlike)
if REXML::Node === stringlike || Array === stringlike
@query = query.map { |q| q.gsub(%r'//', './') }
else
@query = query
end

add_options_conditions_to(@query)

@document = Webrat.rexml_document(stringlike)

matched = @query.map do |q|
@query.map do |q|
if @document.is_a?(Array)
@document.map { |d| REXML::XPath.match(d, q) }
else
REXML::XPath.match(@document, q)
end
end.flatten.compact

matched.any? && (!@block || @block.call(matched))
end

def matches_nokogiri?(stringlike)
def nokogiri_matches(stringlike)
if Nokogiri::XML::NodeSet === stringlike
@query = query.map { |q| q.gsub(%r'//', './') }
@query = query.gsub(%r'//', './')
else
@query = query
end

add_options_conditions_to(@query)

@document = Webrat::XML.document(stringlike)
matched = @document.xpath(*@query)
matched.any? && (!@block || @block.call(matched))
@document.xpath(*@query)
end

def add_options_conditions_to(query)
add_attributes_conditions_to(query)
add_content_condition_to(query)
end

def add_attributes_conditions_to(query)
attribute_conditions = []

@options.each do |key, value|
next if [:content, :count].include?(key)
attribute_conditions << "@#{key} = #{xpath_escape(value)}"
end

if attribute_conditions.any?
query << "[#{attribute_conditions.join(' and ')}]"
end
end

def add_content_condition_to(query)
if @options[:content]
query << "[contains(., #{xpath_escape(@options[:content])})]"
end
end

def query
[@expected].flatten.compact
@expected
end

# ==== Returns
Expand All @@ -66,7 +101,24 @@ def failure_message
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following text to not match xpath #{@expected}:\n#{@document}"
end
end

protected

def xpath_escape(string)
if string.include?("'") && string.include?('"')
parts = string.split("'").map do |part|
"'#{part}'"
end

"concat(" + parts.join(", \"'\", ") + ")"
elsif string.include?("'")
"\"#{string}\""
else
"'#{string}'"
end
end

end

# Matches HTML content against an XPath query
Expand All @@ -76,18 +128,18 @@ def negative_failure_message
#
# ==== Returns
# HaveXpath:: A new have xpath matcher.
def have_xpath(expected, &block)
HaveXpath.new(expected, &block)
def have_xpath(expected, options = {}, &block)
HaveXpath.new(expected, options, &block)
end
alias_method :match_xpath, :have_xpath

def assert_have_xpath(expected, &block)
hs = HaveXpath.new(expected, &block)
def assert_have_xpath(expected, options = {}, &block)
hs = HaveXpath.new(expected, options, &block)
assert hs.matches?(response_body), hs.failure_message
end

def assert_have_no_xpath(expected, &block)
hs = HaveXpath.new(expected, &block)
def assert_have_no_xpath(expected, options = {}, &block)
hs = HaveXpath.new(expected, options, &block)
assert !hs.matches?(response_body), hs.negative_failure_message
end

Expand Down
2 changes: 1 addition & 1 deletion lib/webrat/selenium/matchers/have_tag.rb
Expand Up @@ -43,7 +43,7 @@ def query
selector << "[#{key}='#{value}']"
end

Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }
Nokogiri::CSS.parse(selector).map { |ast| ast.to_xpath }
end
end

Expand Down

0 comments on commit 4115c99

Please sign in to comment.