Splitting matchers into separate files
This commit is contained in:
parent
d54ae99e27
commit
b9bbc91240
@ -1,239 +1,4 @@
|
||||
module Webrat
|
||||
module Matchers
|
||||
|
||||
class HaveXpath
|
||||
def initialize(expected, &block)
|
||||
# Require nokogiri and fall back on rexml
|
||||
begin
|
||||
require "nokogiri"
|
||||
require "webrat/nokogiri"
|
||||
rescue LoadError => e
|
||||
if require "rexml/document"
|
||||
require "webrat/vendor/nokogiri/css"
|
||||
warn("Standard REXML library is slow. Please consider installing nokogiri.\nUse \"sudo gem install nokogiri\"")
|
||||
end
|
||||
end
|
||||
|
||||
@expected = expected
|
||||
@block = block
|
||||
end
|
||||
|
||||
def matches?(stringlike)
|
||||
if defined?(Nokogiri::XML)
|
||||
matches_nokogiri?(stringlike)
|
||||
else
|
||||
matches_rexml?(stringlike)
|
||||
end
|
||||
end
|
||||
|
||||
def matches_rexml?(stringlike)
|
||||
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
||||
|
||||
@document = case stringlike
|
||||
when REXML::Document
|
||||
stringlike.root
|
||||
when REXML::Node
|
||||
stringlike
|
||||
when StringIO, String
|
||||
begin
|
||||
REXML::Document.new(stringlike.to_s).root
|
||||
rescue REXML::ParseException => e
|
||||
if e.message.include?("second root element")
|
||||
REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
query.all? do |q|
|
||||
matched = REXML::XPath.match(@document, q)
|
||||
matched.any? && (!block_given? || matched.all?(&@block))
|
||||
end
|
||||
end
|
||||
|
||||
def matches_nokogiri?(stringlike)
|
||||
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
||||
|
||||
@document = case stringlike
|
||||
when Nokogiri::HTML::Document, Nokogiri::XML::NodeSet
|
||||
stringlike
|
||||
when StringIO
|
||||
Nokogiri::HTML(stringlike.string)
|
||||
else
|
||||
Nokogiri::HTML(stringlike.to_s)
|
||||
end
|
||||
@document.xpath(*query).any?
|
||||
end
|
||||
|
||||
def query
|
||||
[@expected].flatten.compact
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected following text to match xpath #{@expected}:\n#{@document}"
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# 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
|
||||
|
||||
class HaveSelector < HaveXpath
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected following text to match selector #{@expected}:\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}"
|
||||
end
|
||||
|
||||
def query
|
||||
Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HasContent
|
||||
def initialize(content)
|
||||
@content = content
|
||||
end
|
||||
|
||||
def matches?(element)
|
||||
element = element.body.to_s if element.respond_to?(:body)
|
||||
@element = element
|
||||
|
||||
case @content
|
||||
when String
|
||||
@element.include?(@content)
|
||||
when Regexp
|
||||
@element.match(@content)
|
||||
end
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected the following element's content to #{content_message}:\n#{@element.inner_text}"
|
||||
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.inner_text}"
|
||||
end
|
||||
|
||||
def content_message
|
||||
case @content
|
||||
when String
|
||||
"include \"#{@content}\""
|
||||
when Regexp
|
||||
"match #{@content.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Matches HTML content against a CSS 3 selector.
|
||||
#
|
||||
# ==== Parameters
|
||||
# expected<String>:: The CSS selector to look for.
|
||||
#
|
||||
# ==== Returns
|
||||
# HaveSelector:: A new have selector matcher.
|
||||
# ---
|
||||
# @api public
|
||||
def have_selector(expected)
|
||||
HaveSelector.new(expected)
|
||||
end
|
||||
alias_method :match_selector, :have_selector
|
||||
|
||||
# Matches HTML content against an XPath query
|
||||
#
|
||||
# ==== Parameters
|
||||
# expected<String>:: The XPath query to look for.
|
||||
#
|
||||
# ==== Returns
|
||||
# HaveXpath:: A new have xpath matcher.
|
||||
# ---
|
||||
# @api public
|
||||
def have_xpath(expected)
|
||||
HaveXpath.new(expected)
|
||||
end
|
||||
alias_method :match_xpath, :have_xpath
|
||||
|
||||
# Matches the contents of an HTML document with
|
||||
# whatever string is supplied
|
||||
#
|
||||
# ---
|
||||
# @api public
|
||||
def contain(content)
|
||||
HasContent.new(content)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module HaveTagMatcher
|
||||
|
||||
class HaveTag < ::Webrat::Matchers::HaveSelector
|
||||
|
||||
# ==== 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 = {})
|
||||
HaveTag.new([name, attributes])
|
||||
end
|
||||
alias_method :match_tag, :have_tag
|
||||
|
||||
end
|
||||
end
|
||||
require "webrat/core/matchers/have_xpath"
|
||||
require "webrat/core/matchers/have_selector"
|
||||
require "webrat/core/matchers/have_tag"
|
||||
require "webrat/core/matchers/have_content"
|
||||
|
53
lib/webrat/core/matchers/have_content.rb
Normal file
53
lib/webrat/core/matchers/have_content.rb
Normal file
@ -0,0 +1,53 @@
|
||||
module Webrat
|
||||
module Matchers
|
||||
|
||||
class HasContent
|
||||
def initialize(content)
|
||||
@content = content
|
||||
end
|
||||
|
||||
def matches?(element)
|
||||
element = element.body.to_s if element.respond_to?(:body)
|
||||
@element = element
|
||||
|
||||
case @content
|
||||
when String
|
||||
@element.include?(@content)
|
||||
when Regexp
|
||||
@element.match(@content)
|
||||
end
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected the following element's content to #{content_message}:\n#{@element.inner_text}"
|
||||
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.inner_text}"
|
||||
end
|
||||
|
||||
def content_message
|
||||
case @content
|
||||
when String
|
||||
"include \"#{@content}\""
|
||||
when Regexp
|
||||
"match #{@content.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Matches the contents of an HTML document with
|
||||
# whatever string is supplied
|
||||
#
|
||||
# ---
|
||||
# @api public
|
||||
def contain(content)
|
||||
HasContent.new(content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
39
lib/webrat/core/matchers/have_selector.rb
Normal file
39
lib/webrat/core/matchers/have_selector.rb
Normal file
@ -0,0 +1,39 @@
|
||||
module Webrat
|
||||
module Matchers
|
||||
|
||||
class HaveSelector < HaveXpath
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected following text to match selector #{@expected}:\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}"
|
||||
end
|
||||
|
||||
def query
|
||||
Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Matches HTML content against a CSS 3 selector.
|
||||
#
|
||||
# ==== Parameters
|
||||
# expected<String>:: The CSS selector to look for.
|
||||
#
|
||||
# ==== Returns
|
||||
# HaveSelector:: A new have selector matcher.
|
||||
# ---
|
||||
# @api public
|
||||
def have_selector(expected)
|
||||
HaveSelector.new(expected)
|
||||
end
|
||||
alias_method :match_selector, :have_selector
|
||||
|
||||
end
|
||||
end
|
58
lib/webrat/core/matchers/have_tag.rb
Normal file
58
lib/webrat/core/matchers/have_tag.rb
Normal file
@ -0,0 +1,58 @@
|
||||
module Webrat
|
||||
|
||||
module HaveTagMatcher
|
||||
|
||||
class HaveTag < ::Webrat::Matchers::HaveSelector
|
||||
|
||||
# ==== 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 = {})
|
||||
HaveTag.new([name, attributes])
|
||||
end
|
||||
alias_method :match_tag, :have_tag
|
||||
|
||||
end
|
||||
end
|
110
lib/webrat/core/matchers/have_xpath.rb
Normal file
110
lib/webrat/core/matchers/have_xpath.rb
Normal file
@ -0,0 +1,110 @@
|
||||
module Webrat
|
||||
module Matchers
|
||||
|
||||
class HaveXpath
|
||||
def initialize(expected, &block)
|
||||
# Require nokogiri and fall back on rexml
|
||||
begin
|
||||
require "nokogiri"
|
||||
require "webrat/nokogiri"
|
||||
rescue LoadError => e
|
||||
if require "rexml/document"
|
||||
require "webrat/vendor/nokogiri/css"
|
||||
warn("Standard REXML library is slow. Please consider installing nokogiri.\nUse \"sudo gem install nokogiri\"")
|
||||
end
|
||||
end
|
||||
|
||||
@expected = expected
|
||||
@block = block
|
||||
end
|
||||
|
||||
def matches?(stringlike)
|
||||
if defined?(Nokogiri::XML)
|
||||
matches_nokogiri?(stringlike)
|
||||
else
|
||||
matches_rexml?(stringlike)
|
||||
end
|
||||
end
|
||||
|
||||
def matches_rexml?(stringlike)
|
||||
@document = rexml_document(stringlike)
|
||||
|
||||
query.all? do |q|
|
||||
matched = REXML::XPath.match(@document, q)
|
||||
matched.any? && (!block_given? || matched.all?(&@block))
|
||||
end
|
||||
end
|
||||
|
||||
def matches_nokogiri?(stringlike)
|
||||
@document = nokogiri_document(stringlike)
|
||||
@document.xpath(*query).any?
|
||||
end
|
||||
|
||||
def rexml_document(stringlike)
|
||||
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
||||
|
||||
case stringlike
|
||||
when REXML::Document
|
||||
stringlike.root
|
||||
when REXML::Node
|
||||
stringlike
|
||||
when StringIO, String
|
||||
begin
|
||||
REXML::Document.new(stringlike.to_s).root
|
||||
rescue REXML::ParseException => e
|
||||
if e.message.include?("second root element")
|
||||
REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nokogiri_document(stringlike)
|
||||
return stringlike.dom if stringlike.respond_to?(:dom)
|
||||
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
||||
|
||||
case stringlike
|
||||
when Nokogiri::HTML::Document, Nokogiri::XML::NodeSet
|
||||
stringlike
|
||||
when StringIO
|
||||
Nokogiri::HTML(stringlike.string)
|
||||
else
|
||||
Nokogiri::HTML(stringlike.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def query
|
||||
[@expected].flatten.compact
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# String:: The failure message.
|
||||
def failure_message
|
||||
"expected following text to match xpath #{@expected}:\n#{@document}"
|
||||
end
|
||||
|
||||
# ==== Returns
|
||||
# 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
|
||||
|
||||
# Matches HTML content against an XPath query
|
||||
#
|
||||
# ==== Parameters
|
||||
# expected<String>:: The XPath query to look for.
|
||||
#
|
||||
# ==== Returns
|
||||
# HaveXpath:: A new have xpath matcher.
|
||||
# ---
|
||||
# @api public
|
||||
def have_xpath(expected)
|
||||
HaveXpath.new(expected)
|
||||
end
|
||||
alias_method :match_xpath, :have_xpath
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user