Title: What is the correct way to check a link in a cucumber test?
Tags: xpath;cucumber
Question: I have been unable to find an answer to this question. I want to write a simple step in Cucumber to check a link is present on a page and that it has the correct text and href. Something like this:
Then /^I see the page with link "(.*?)" to "(.*?)"$/ do |link,url|
page.should have_xpath("//a[text()='#{link}',@href='#{url}']")
Except that doesn't work. THe xpath syntax is incorrect and I have struggled to find a good explanation of the syntax.
Thanks!

Here is the accepted answer: That would be:
page.should have_link(link, :href =&gt; url)
See the capybara spec for details.
Comment for this answer: thanks for that. I knew there must have been a better way but have_link escaped me. Cucumber book mentions have_content but not have_link. much appreciated.
Comment for this answer: You can get ideas from the specs but they are not comprehensive. For example, at the moment, neither the specs nor the docs tell you that `has_link?` actually searches in the text field as well the id, title, and img alt attributes. I found this out after a long debugging session that took me to [this XPath source code](https://github.com/jnicklas/xpath/blob/master/lib/xpath/html.rb#L14).
Comment for this answer: Capybara methods are not well-documented. Directly checking specs is the most reliable way to get answers to these kinds of questions.
Comment for this answer: That's really unfortunate. Documentation is really important... you only notice when it's missing!

Here is another answer: I believe the XPath you want is
page.should have_xpath("//a[text()='#{link}' and @href='#{url}']")
I'm not familiar with Cucumber, but if it relies on Capybara, the has_link? alternative suggested by @shioyama is easier to read:
page.should have_link(link, :href =&gt; url)
However it's also deceptively simple. With has_link?, keep in mind:
It searches on substrings (your XPath example searches on an exact match).
It searches for "link" in the text value, but also in id, title, and img alt fields.
If you specify other attributes as options, e.g. :text or :class, they will be silently ignored. You may think you are narrowing your test but you are not.
Consider the following HTML:
<a href="/contacts/3" data-confirm="Are you sure?" data-method="delete"
title="Delete Contact 3"&gt;Delete</a&gt;
I was rather surprised to find that because has_link? searches for a substring of  title, both of the following conditions match that link:
has_link?("Contact 3", href: "/contacts/3")
has_link?("Delete", href: "/contacts/3")
I am currently experimenting with a custom has_exact_link matcher that eliminates the ambiguity by formatting the XPath explicitly. It searches for an exact match of the text value only, not a substring, and any options other than :href raise an error:
spec/support/matchers.rb
# Check for an <a&gt; tag with the exact text and optional :href and  :count.
# Same param syntax as has_link, but it only matches the link's TEXT, not id,
# label, etc., and it doesn't match substrings.
def has_exact_link?(locator, options={})
# Subtract :href, :count from array of options.keys
# Raise error if any options remain
raise ArgumentError.new \
"options, if supplied, must be a hash" if !options.is_a?(Hash)
raise ArgumentError.new \
"has_exact_link only supports 'href' and 'count' as options" unless
(options.keys - [:href] - [:count]).empty?
href = options[:href]
xpath = href ? "//a[normalize-space(text())='#{locator}' and @href='#{href}']" :
"//a[normalize-space(text())='#{locator}']/@href"
# pass original options has so test results will show options if present
# has_xpath apparently ignores :href in options but will use :count.
has_xpath?(xpath, options)
Update Sept. 19, 2012:  added "normalize-space" to has_exact_link above so it will ignore leading and trailing whitespace as HTML does. This is required if, for example, the text of your link is on a different line than the <a&gt; tag, e.g.
<a href="/contacts/3"&gt;
Contact 3
</a&gt;
It still does not match substrings. To match the above,  you must specify has_exact_link("Contact 3"), not just has_exact_link("Contact").
Update Sept. 20, 2012 Another has_exact_link update, above. Now it does type-checking on the options parameter, and handles the :count option as well as :href.
Comment for this answer: Mark Berry thanks for this... I had an issue with `has_xpath?` not accepting `href` in the options so I made the following change:
`href = options[:href]` became `href = options.delete(:href)`

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2012:00:08/31/12', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<xpath><cucumber>', 'Title': 'What is the correct way to check a link in a cucumber test?', 'ViewCount': 4781, 'id': 12214030, 'user_AboutMe': '', 'user_AccountId': 366765.0, 'user_DisplayName': 'starfry', 'user_Reputation': 8675.0, 'user_Views': 442.0, 'user_id': 712506.0}}