Permalink
Browse files

add has_text matcher based on Element#text

  • Loading branch information...
mkdynamic committed Oct 16, 2011
1 parent 0c3f377 commit 9fb05c9c64a933e392fa252e145edc6a98af20bc
View
@@ -367,6 +367,7 @@ certain elements, and working with and manipulating those elements.
page.has_xpath?('//table/tr')
page.has_css?('table tr.foo')
page.has_content?('foo')
+ page.has_text?('foo')
You can use these with RSpec's magic matchers:
@@ -376,8 +377,14 @@ You can use these with RSpec's magic matchers:
page.should have_xpath('//table/tr')
page.should have_css('table tr.foo')
- page.should have_content('foo')
- page.should have_no_content('foo')
+ page.should have_text('foo')
+ page.should have_no_text('foo')
+
+Note that there are 2 matchers for checking content/text. <tt>page.has_text?('foo')</tt>
+will check only for text that is displayable, whereas <tt>page.has_content?('foo')</tt> will
+check for the content within any nodes (including the head section and within script tags).
+Most of the time you'll want the behaviour of <tt>page.has_text?('foo')</tt>, so go with that
+unless you have a specific reason to use <tt>page.has_content?('foo')</tt> instead.
Note that <tt>page.should have_no_xpath</tt> is preferred over
<tt>page.should_not have_xpath</tt>. Read the section on asynchronous JavaScript
@@ -483,7 +490,7 @@ When issuing instructions to the DSL such as:
click_link('foo')
click_link('bar')
- page.should have_content('baz')
+ page.should have_text('baz')
If clicking on the *foo* link triggers an asynchronous process, such as
an Ajax request, which, when complete will add the *bar* link to the page,
@@ -515,7 +522,7 @@ The two following statements are functionally equivalent:
Capybara's waiting behaviour is quite advanced, and can deal with situations
such as the following line of code:
- find('#sidebar').find('h1').should have_content('Something')
+ find('#sidebar').find('h1').should have_text('Something')
Even if JavaScript causes <tt>#sidebar</tt> to disappear off the page, Capybara
will automatically reload it and any elements it contains. So if an AJAX
@@ -201,35 +201,43 @@ def has_no_content?(content)
# Checks if the page or current node has the given text content,
# ignoring any HTML tags and normalizing whitespace.
#
- # Unlike has_content this only matches text displayed on the page
- # and specifically excludes text contained within script nodes.
+ # Unlike has_content this only matches displayable text and specifically
+ # excludes text contained within non-display nodes such as script or head tags.
#
# @param [String] content The text to check for
# @return [Boolean] Whether it exists
#
def has_text?(content)
- literal = XPath::Expression::StringLiteral.new(content).to_xpath
- expression = "./descendant-or-self::*[text()[contains(normalize-space(.), #{literal})] and not(ancestor-or-self::script)]"
+ normalized_content = normalize_whitespace(content)
- has_xpath?(expression)
+ wait_until do
+ normalize_whitespace(text).include?(normalized_content) or
+ raise ExpectationNotMet
+ end
+ rescue Capybara::ExpectationNotMet
+ return false
end
##
#
# Checks if the page or current node does not have the given text
# content, ignoring any HTML tags and normalizing whitespace.
#
- # Unlike has_content this only matches text displayed on the page
- # and specifically excludes text contained within script nodes.
+ # Unlike has_content this only matches displayable text and specifically
+ # excludes text contained within non-display nodes such as script or head tags.
#
# @param [String] content The text to check for
# @return [Boolean] Whether it exists
#
def has_no_text?(content)
- literal = XPath::Expression::StringLiteral.new(content).to_xpath
- expression = "./descendant-or-self::*[text()[contains(normalize-space(.), #{literal})] and not(ancestor-or-self::script)]"
+ normalized_content = normalize_whitespace(content)
- has_no_xpath?(expression)
+ wait_until do
+ !normalize_whitespace(text).include?(normalized_content) or
+ raise ExpectationNotMet
+ end
+ rescue Capybara::ExpectationNotMet
+ return false
end
##
@@ -448,6 +456,19 @@ def split_options(options, key)
options = options.dup
[options, if options.has_key?(key) then {key => options.delete(key)} else {} end]
end
+
+ ##
+ #
+ # Normalizes whitespace space by stripping leading and trailing
+ # whitespace and replacing sequences of whitespace characters
+ # with a single space.
+ #
+ # @param [String] text Text to normalize
+ # @return [String] Normalized text
+ #
+ def normalize_whitespace(text)
+ text.gsub(/\s+/, ' ').strip
+ end
end
end
end
@@ -1,15 +1,128 @@
shared_examples_for 'has_text' do
describe '#has_text?' do
- it 'works' do
+ it "should be true if the given text is on the page at least once" do
@session.visit('/with_html')
+ @session.should have_text('est')
@session.should have_text('Lorem')
+ @session.should have_text('Redirect')
+ end
+
+ it "should be true if scoped to an element which has the text" do
+ @session.visit('/with_html')
+ @session.within("//a[@title='awesome title']") do
+ @session.should have_text('labore')
+ end
+ end
+
+ it "should be false if scoped to an element which does not have the text" do
+ @session.visit('/with_html')
+ @session.within("//a[@title='awesome title']") do
+ @session.should_not have_text('monkey')
+ end
+ end
+
+ it "should ignore tags" do
+ @session.visit('/with_html')
+ @session.should_not have_text('exercitation <a href="/foo" id="foo">ullamco</a> laboris')
+ @session.should have_text('exercitation ullamco laboris')
+ end
+
+ it "should ignore extra whitespace and newlines" do
+ @session.visit('/with_html')
+ @session.should have_text('text with whitespace')
+ end
+
+ it "should be false if the given text is not on the page" do
+ @session.visit('/with_html')
+ @session.should_not have_text('xxxxyzzz')
+ @session.should_not have_text('monkey')
+ end
+
+ it 'should handle single quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should have_text("can't")
+ end
+
+ it 'should handle double quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should have_text(%q{"No," he said})
+ end
+
+ it 'should handle mixed single and double quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should have_text(%q{"you can't do that."})
+ end
+
+ it 'should be false if text is in the title tag in the head' do
+ @session.visit('/with_html')
+ @session.should_not have_text('with_js')
+ end
+
+ it 'should be false if text is inside a script tag in the body' do
+ @session.visit('/with_html')
+ @session.should_not have_text('a javascript comment')
+ @session.should_not have_text('aVar')
end
end
describe '#has_no_text?' do
- it 'works' do
+ it "should be false if the given text is on the page at least once" do
+ @session.visit('/with_html')
+ @session.should_not have_no_text('est')
+ @session.should_not have_no_text('Lorem')
+ @session.should_not have_no_text('Redirect')
+ end
+
+ it "should be false if scoped to an element which has the text" do
+ @session.visit('/with_html')
+ @session.within("//a[@title='awesome title']") do
+ @session.should_not have_no_text('labore')
+ end
+ end
+
+ it "should be true if scoped to an element which does not have the text" do
+ @session.visit('/with_html')
+ @session.within("//a[@title='awesome title']") do
+ @session.should have_no_text('monkey')
+ end
+ end
+
+ it "should ignore tags" do
+ @session.visit('/with_html')
+ @session.should have_no_text('exercitation <a href="/foo" id="foo">ullamco</a> laboris')
+ @session.should_not have_no_text('exercitation ullamco laboris')
+ end
+
+ it "should be true if the given text is not on the page" do
+ @session.visit('/with_html')
+ @session.should have_no_text('xxxxyzzz')
+ @session.should have_no_text('monkey')
+ end
+
+ it 'should handle single quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should_not have_no_text("can't")
+ end
+
+ it 'should handle double quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should_not have_no_text(%q{"No," he said})
+ end
+
+ it 'should handle mixed single and double quotes in the text' do
+ @session.visit('/with-quotes')
+ @session.should_not have_no_text(%q{"you can't do that."})
+ end
+
+ it 'should be true if text is in the title tag in the head' do
+ @session.visit('/with_html')
+ @session.should have_no_text('with_js')
+ end
+
+ it 'should be true if text is inside a script tag in the body' do
@session.visit('/with_html')
- @session.should have_no_text('Merol')
+ @session.should have_no_text('a javascript comment')
+ @session.should have_no_text('aVar')
end
end
end
@@ -274,6 +274,21 @@
end
end
+ describe '#has_text?' do
+ it "should wait for text to appear" do
+ @session.visit('/with_js')
+ @session.click_link('Click me')
+ @session.should have_text("Has been clicked")
+ end
+ end
+
+ describe '#has_no_text?' do
+ it "should wait for text to disappear" do
+ @session.visit('/with_js')
+ @session.click_link('Click me')
+ @session.should have_no_text("I changed it")
+ end
+ end
end
end
@@ -43,6 +43,11 @@
<a id="reload-link" href="#">Reload!</a>
<div id="reload-me"><em>waiting to be reloaded</em></div>
</p>
+
+ <script type="text/javascript">
+ // a javascript comment
+ var aVar = 123;
+ </script>
</body>
</html>
@@ -312,6 +312,78 @@
end
end
+ describe "have_text matcher" do
+ it "gives proper description" do
+ have_text('Text').description.should == "has text \"Text\""
+ end
+
+ context "on a string" do
+ context "with should" do
+ it "passes if has_text? returns true" do
+ "<h1>Text</h1>".should have_text('Text')
+ end
+
+ it "fails if has_text? returns false" do
+ expect do
+ "<h1>Text</h1>".should have_text('No such Text')
+ end.to raise_error(/expected there to be text "No such Text" in "Text"/)
+ end
+ end
+
+ context "with should_not" do
+ it "passes if has_no_text? returns true" do
+ "<h1>Text</h1>".should_not have_text('No such Text')
+ end
+
+ it "fails if has_no_text? returns false" do
+ expect do
+ "<h1>Text</h1>".should_not have_text('Text')
+ end.to raise_error(/expected text "Text" not to return anything/)
+ end
+ end
+ end
+
+ context "on a page or node" do
+ before do
+ visit('/with_html')
+ end
+
+ context "with should" do
+ it "passes if has_text? returns true" do
+ page.should have_text('This is a test')
+ end
+
+ it "fails if has_text? returns false" do
+ expect do
+ page.should have_text('No such Text')
+ end.to raise_error(/expected there to be text "No such Text" in "(.*)This is a test(.*)"/)
+ end
+
+ context "with default selector CSS" do
+ before { Capybara.default_selector = :css }
+ it "fails if has_text? returns false" do
+ expect do
+ page.should have_text('No such Text')
+ end.to raise_error(/expected there to be text "No such Text" in "(.*)This is a test(.*)"/)
+ end
+ after { Capybara.default_selector = :xpath }
+ end
+ end
+
+ context "with should_not" do
+ it "passes if has_no_text? returns true" do
+ page.should_not have_text('No such Text')
+ end
+
+ it "fails if has_no_text? returns false" do
+ expect do
+ page.should_not have_text('This is a test')
+ end.to raise_error(/expected text "This is a test" not to return anything/)
+ end
+ end
+ end
+ end
+
describe "have_link matcher" do
let(:html) { '<a href="#">Just a link</a>' }

0 comments on commit 9fb05c9

Please sign in to comment.