Aleksandr Shvalev
Aleksandr Shvalev

Reputation: 1035

How to click on the second link with the same text using Capybara in Rails 3?

In a view file I have:

= link_to 'View', post
= link_to 'View', comment

In a spec file (I'm using Capybara):

click_on 'View'

It clicks on the first link, but I want it to click on the second one. How can I do it?

Upvotes: 44

Views: 44926

Answers (7)

steel
steel

Reputation: 12580

If you use capybara-ui you could define the widget, or reusable DOM reference, for each widget.

# define your widget. in this case,
# we're defining it in a role
widget :view_post, ['.post', text: 'View']
widget :view_comment, ['.comment', text: 'View']

# then click that widget in the test
role.click :view_post
role.click :view_comment

Upvotes: 1

installero
installero

Reputation: 9786

The worst thing about "the second" link is that it can become the third or the first or even the twenty fifth someday. So, scoping with a within block is the best way. Example:

within(".comment") do
  click_on("View")
end

But if it is difficult to specify the link with a within scope (which sometimes it really is), I guess the way to click the second link with a certain text is:

 find(:xpath, "(//a[text()='View'])[2]").click

In later versions of capybara (2.0.2, for example) both click_on 'View' and click_link 'View' will raise an ambiguous match error:

 Failure/Error: click_on 'View'
 Capybara::Ambiguous:
   Ambiguous match, found 2 elements matching link or button "View"

So this won't do even if you want to click the first link (or if any link would be ok, which is my case).

As far as I understand this is made to force people write more specific tests where particular links are clicked.

It definitely could be tricky to debug the code if you accidentally placed two or more links with identical text and try to see what is happening. It's good to rely on something that is unlikely to change and specifying a link with a within block is a nice way to do this.

Upvotes: 18

Jwan622
Jwan622

Reputation: 11659

This works for me if you have several rows of identical classes and you want to find the second row. Like a previous author mentioned, capybara indexing starts at 1.

within all(".trip-row")[2] do
   assert page.has_content?("content")
end

Upvotes: 4

RohitPorwal
RohitPorwal

Reputation: 1065

There are many ways for solving this type of problems.
Do it like this

if(page.find("a")[:href] == "comment")
    click_on("View")

or

page.find("a:eq(2)").click

Remember javascript indexing starts with 0 while In Capybara, indexing starts with 1. So use a:eq(2) here for second href.

Upvotes: 17

miry
miry

Reputation: 328

For capybara 2 solution:

within(".comment") do
  click_on("View")
end

would not help if you have a few .comment. So simple use: page.first(:link, "View").click

Upvotes: 8

Gabriel F. Engel
Gabriel F. Engel

Reputation: 990

You could try to find all entries and deal with an array:

page.all('a')[1].click

Would help to have a class or use within to scope your search ;)

Upvotes: 68

Keith Gaddis
Keith Gaddis

Reputation: 4113

There's probably a few ways but I usually scope something like this.

within(".comment") do
  click_on("View")
end

There's quite possibly/probably alternatives as well. I usually do my acceptance testing from cucumber, so my steps typically look like

When I follow "View" within the comment element

Where I have a step that translates within the comment element to a scoped call to the step itself (which I think is built into the latest capybara web_steps)

Upvotes: 39

Related Questions