How do I resolve ambiguity in Capybara? For some reason I need links with the same values in a page but I can't create a test since I get the error
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
The reason why I can't avoid this is because of the design. I'm trying to recreate the twitter page with tweets/tags on the right and the tags on the left of the page. Therefore it will be inevitable that identical links page shows up on the same page.
Such behavior of Capybara is intentional and I believe it shouldn't be fixed as suggested in most of other answers.
Versions of Capybara before 2.0 returned the first element instead of raising exception but later maintainers of Capybara decided that it's a bad idea and it's better to raise it. It was decided that in many situations returning first element leads to returning not the element that the developer wanted to be returned.
The most upvoted answer here recommend to use
first
orall
instead offind
but:all
andfirst
don't wait till element with such locator will appear on the page thoughfind
does waitall(...).first
andfirst
won't protect you from situation that in future another element with such locator may appear on the page and as the result you may find incorrect elementSo it's adviced to choose another, less ambiguous locator: for example select element by id, class or other css/xpath locator so that only one element will match it.
As a note here are some locators that I usually consider useful when resolving ambiguity:
find('ul > li:first-child')
It's more useful than
first('ul > li')
as it will wait till firstli
will appear on the page.click_link('Create Account', match: :first)
It's better than
first(:link, 'Create Account').click
as it will wait till at least one Create Account link will appear on the page. However I believe it's better to choose unique locator that doesn't appear on the page twice.fill_in('Password', with: 'secret', exact: true)
exact: true
tells Capybara to find only exact matches, i.e. not find "Password Confirmation"NEW ANSWER:
You can try something like
There may be a way to do this which makes better use of the available Capybara syntax -- something along the lines of
all("a[text='#tag1']").first.click
but I can't think of the correct syntax off hand and I can't find the appropriate documentation. That said it's a bit of a strange situation to begin with, having two<a>
tags with the sameid
,class
, and text. Is there any chance they are children of different divs, since you could then do yourfind
within
the appropriate segment of the DOM. (It would help to see a bit of your HTML source).OLD ANSWER: (where I thought '#tag1' meant the element had an
id
of "tag1")Which of the links do you want to click on? If it's the first (or it doesn't matter), you can do
Otherwise you can do
to click the second one.
To avoid ambiguous error in cucumber.
Solution 1
Solution 2
The above solution works great but for those curious you can also use the following syntax.
You can find more information here:
http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/
Due to this post, you can fix it via "match" option:
You can ensure that you find the first one using
match
:But importantly, you probably do not want to do this, as it will lead to brittle tests that are ignoring the duplicate-output code smell, which in turn leads to false positives that keep working when they should have failed, because you removed one matching element but the test happily found the other one.
The better bet is to use
within
:This ensures that you're finding the element you expect to find, while still leveraging the auto-wait and auto-retry capabilities of Capybara (which you lose if you use
find('.selector').click
), and it makes it much clearer what the intent is.