Reputation: 7109
How to click first link in that case:
<div class="item">
<a href="/agree/">Agree</a>
</div>
<div class="item">
<a href="/agree/">Agree</a>
</div>
within ".item" do
first(:link, "Agree").click
end
and I get this error:
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching css ".item"
And without the within
I get this error:
Failure/Error: first(:link, "Agree").click
NoMethodError:
undefined method `click' for nil:NilClass
Upvotes: 146
Views: 89982
Reputation: 955
most of those solutions will not use Capybara's brilliant waiting features
better do as this link suggests:
https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara#find-the-first-matching-element
first(".active").click
If there isn’t an .active element on the page yet, first will return nil and the click will fail.
If you want to make sure there's exactly one
find(".active").click
If you just want the first element
find(".active", match: :first).click
Capybara will wait for the element to appear before trying to click.
Note that match: :first
is more brittle, because it will silently click on a different element if you introduce new elements which match.
Upvotes: 9
Reputation: 1401
Try the following:
within ".item" do
click_link("Agree", :match => :first)
end
Sources:
Upvotes: 140
Reputation: 1810
Since first() doesn't always wait, perhaps this is useful:
expect(page).to have_css("selector")
first("selector").click
Upvotes: 2
Reputation: 2247
This phrasing also works:
within first(".item") do
click_link "Agree"
end
Upvotes: 29
Reputation: 21096
You can just use:
first('.item').click_link('Agree')
or
first('.item > a').click
(if your default selector is :css)
Code in your question doesn't work as:
within ".item" do
first(:link, "Agree").click
end
is equivalent to:
find('.item').first(:link, "Agree").click
Capybara finds several .item
's so it raises an exception. I consider this behavior of Capybara 2 very good.
Upvotes: 191
Reputation: 26979
Xpath can address the element. I'm not very good with it yet, but something like //div[@class='active'][1]/a
That may or may not work, but the point is that xpath can address an array of matches and pull out a particular one. You should be able to match with this.
A working example example from one of my projects:
within page.find("div.panel", text: /Proposals/) do within page.find('tr', text: /Foo/) do page.should have_xpath('td[3]', text: @today) end end
Upvotes: 5