Created
July 19, 2012 16:49
-
-
Save brynary/3145238 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def assert_select(*args, &block) | |
# Start with optional element followed by mandatory selector. | |
arg = args.shift | |
@selected ||= nil | |
if arg.is_a?(HTML::Node) | |
# First argument is a node (tag or text, but also HTML root), | |
# so we know what we're selecting from. | |
root = arg | |
arg = args.shift | |
elsif arg == nil | |
# This usually happens when passing a node/element that | |
# happens to be nil. | |
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?" | |
elsif @selected | |
root = HTML::Node.new(nil) | |
root.children.concat @selected | |
else | |
# Otherwise just operate on the response document. | |
root = response_from_page | |
end | |
# First or second argument is the selector: string and we pass | |
# all remaining arguments. Array and we pass the argument. Also | |
# accepts selector itself. | |
case arg | |
when String | |
selector = HTML::Selector.new(arg, args) | |
when Array | |
selector = HTML::Selector.new(*arg) | |
when HTML::Selector | |
selector = arg | |
else raise ArgumentError, "Expecting a selector as the first argument" | |
end | |
# Next argument is used for equality tests. | |
equals = {} | |
case arg = args.shift | |
when Hash | |
equals = arg | |
when String, Regexp | |
equals[:text] = arg | |
when Integer | |
equals[:count] = arg | |
when Range | |
equals[:minimum] = arg.begin | |
equals[:maximum] = arg.end | |
when FalseClass | |
equals[:count] = 0 | |
when NilClass, TrueClass | |
equals[:minimum] = 1 | |
else raise ArgumentError, "I don't understand what you're trying to match" | |
end | |
# By default we're looking for at least one match. | |
if equals[:count] | |
equals[:minimum] = equals[:maximum] = equals[:count] | |
else | |
equals[:minimum] = 1 unless equals[:minimum] | |
end | |
# Last argument is the message we use if the assertion fails. | |
message = args.shift | |
#- message = "No match made with selector #{selector.inspect}" unless message | |
if args.shift | |
raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type" | |
end | |
matches = selector.select(root) | |
# If text/html, narrow down to those elements that match it. | |
content_mismatch = nil | |
if match_with = equals[:text] | |
matches.delete_if do |match| | |
text = "" | |
stack = match.children.reverse | |
while node = stack.pop | |
if node.tag? | |
stack.concat node.children.reverse | |
else | |
content = node.content | |
text << content | |
end | |
end | |
text.strip! unless NO_STRIP.include?(match.name) | |
text.sub!(/\A\n/, '') if match.name == "textarea" | |
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s) | |
content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, text) | |
true | |
end | |
end | |
elsif match_with = equals[:html] | |
matches.delete_if do |match| | |
html = match.children.map(&:to_s).join | |
html.strip! unless NO_STRIP.include?(match.name) | |
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s) | |
content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, html) | |
true | |
end | |
end | |
end | |
# Expecting foo found bar element only if found zero, not if | |
# found one but expecting two. | |
message ||= content_mismatch if matches.empty? | |
# Test minimum/maximum occurrence. | |
min, max, count = equals[:minimum], equals[:maximum], equals[:count] | |
# FIXME: minitest provides messaging when we use assert_operator, | |
# so is this custom message really needed? | |
message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.) | |
if count | |
assert_equal matches.size, count, message | |
else | |
assert_operator matches.size, :>=, min, message if min | |
assert_operator matches.size, :<=, max, message if max | |
end | |
# If a block is given call that block. Set @selected to allow | |
# nested assert_select, which can be nested several levels deep. | |
if block_given? && !matches.empty? | |
begin | |
in_scope, @selected = @selected, matches | |
yield matches | |
ensure | |
@selected = in_scope | |
end | |
end | |
# Returns all matches elements. | |
matches | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment