Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed that assert_select selects the wrong tag by its contents (closes

…#6332) [jgarber]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@5240 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 3c6b7a219142907416780501dd8e7d068a802bc4 1 parent 717e26d
David Heinemeier Hansson dhh authored
130 actionpack/lib/action_controller/assertions/selector_assertions.rb
View
@@ -8,6 +8,10 @@
module ActionController
module Assertions
+ unless const_defined?(:NO_STRIP)
+ NO_STRIP = %w{pre script style textarea}
+ end
+
# Adds the #assert_select method for use in Rails functional
# test cases.
#
@@ -118,27 +122,28 @@ def css_select(*args)
# === Equality Tests
#
# The equality test may be one of the following:
- # * <tt>nil/true</tt> -- Assertion is true if at least one element is
- # selected.
- # * <tt>String</tt> -- Assertion is true if the text value of all
- # selected elements equals to the string.
- # * <tt>Regexp</tt> -- Assertion is true if the text value of all
- # selected elements matches the regular expression.
- # * <tt>false</tt> -- Assertion is true if no element is selected.
+ # * <tt>true</tt> -- Assertion is true if at least one element selected.
+ # * <tt>false</tt> -- Assertion is true if no element selected.
+ # * <tt>String/Regexp</tt> -- Assertion is true if the text value of at least
+ # one element matches the string or regular expression.
# * <tt>Integer</tt> -- Assertion is true if exactly that number of
# elements are selected.
# * <tt>Range</tt> -- Assertion is true if the number of selected
# elements fit the range.
- #
- # To perform more than one equality tests, use a hash the following keys:
- # * <tt>:text</tt> -- Assertion is true if the text value of each
- # selected elements equals to the value (+String+ or +Regexp+).
- # * <tt>:count</tt> -- Assertion is true if the number of matched elements
- # is equal to the value.
- # * <tt>:minimum</tt> -- Assertion is true if the number of matched
- # elements is at least that value.
- # * <tt>:maximum</tt> -- Assertion is true if the number of matched
- # elements is at most that value.
+ # If no equality test specified, the assertion is true if at least one
+ # element selected.
+ #
+ # To perform more than one equality tests, use a hash with the following keys:
+ # * <tt>:text</tt> -- Narrow the selection to elements that have this text
+ # value (string or regexp).
+ # * <tt>:html</tt> -- Narrow the selection to elements that have this HTML
+ # content (string or regexp).
+ # * <tt>:count</tt> -- Assertion is true if the number of selected elements
+ # is equal to this value.
+ # * <tt>:minimum</tt> -- Assertion is true if the number of selected
+ # elements is at least this value.
+ # * <tt>:maximum</tt> -- Assertion is true if the number of selected
+ # elements is at most this value.
#
# If the method is called with a block, once all equality tests are
# evaluated the block is called with an array of all matched elements.
@@ -224,15 +229,11 @@ def assert_select(*args, &block)
else raise ArgumentError, "I don't understand what you're trying to match"
end
- # If we have a text test, by default we're looking for at least one match.
- # Without this statement text tests pass even if nothing is selected.
- # Can always override by specifying minimum or count.
- if equals[:text]
- equals[:minimum] ||= 1
- end
- # If a count is specified, it takes precedence over minimum/maximum.
+ # By default we're looking for at least one match.
if equals[:count]
- equals[:minimum] = equals[:maximum] = equals.delete(: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.
@@ -243,50 +244,47 @@ def assert_select(*args, &block)
end
matches = selector.select(root)
- # Equality test.
- equals.each do |type, value|
- case type
- when :text
- for match in matches
- text = ""
- stack = match.children.reverse
- while node = stack.pop
- if node.tag?
- stack.concat node.children.reverse
- else
- text << node.content
- end
- end
- text.strip! unless match.name == "pre"
- if value.is_a?(Regexp)
- assert text =~ value, build_message(message, <<EOT, value, text)
-<?> expected but was
-<?>.
-EOT
- else
- assert_equal value.to_s, text, message
- end
- end
- when :html
- for match in matches
- html = match.children.map(&:to_s).join
- html.strip! unless match.name == "pre"
- if value.is_a?(Regexp)
- assert html =~ value, build_message(message, <<EOT, value, html)
-<?> expected but was
-<?>.
-EOT
- else
- assert_equal value.to_s, html, message
- end
+ # 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
+ text << node.content
end
- when :minimum
- assert matches.size >= value, message || "Expecting at least #{value} selected elements, found #{matches.size}"
- when :maximum
- assert matches.size <= value, message || "Expecting at most #{value} selected elements, found #{matches.size}"
- else raise ArgumentError, "I don't support the equality test #{key}"
+ end
+ text.strip! unless NO_STRIP.include?(match.name)
+ unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
+ content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", 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 ||= build_message(message, "<?> expected but was\n<?>.", 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.
+ if equals[:minimum]
+ assert matches.size >= equals[:minimum], message ||
+ "Expected at least #{equals[:minimum]} elements, found #{matches.size}."
+ end
+ if equals[:maximum]
+ assert matches.size <= equals[:maximum], message ||
+ "Expected at most #{equals[:maximum]} elements, found #{matches.size}."
+ end
# If a block is given call that block. Set @selected to allow
# nested assert_select, which can be nested several levels deep.
17 actionpack/test/controller/assert_select_test.rb
View
@@ -186,6 +186,23 @@ def test_nested_assert_select
end
+ def test_assert_select_text_match
+ render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>}
+ assert_select "div" do
+ assert_nothing_raised { assert_select "div", "foo" }
+ assert_nothing_raised { assert_select "div", "bar" }
+ assert_nothing_raised { assert_select "div", /\w*/ }
+ assert_nothing_raised { assert_select "div", /\w*/, :count=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :text=>"foo", :count=>2 }
+ assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
+ assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
+ assert_nothing_raised { assert_select "div", :html=>/\w*/ }
+ assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :html=>"<span>foo</span>", :count=>2 }
+ end
+ end
+
+
def test_assert_select_from_rjs
render_rjs do |page|
page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
Please sign in to comment.
Something went wrong with that request. Please try again.