142

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
Andrei Botalov
  • 20,686
  • 11
  • 89
  • 123
tomekfranek
  • 6,852
  • 8
  • 45
  • 80

7 Answers7

191

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.

Andrei Botalov
  • 20,686
  • 11
  • 89
  • 123
  • 4
    I would recommend against using #first, it doesn't wait for an element to exist: http://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FNode%2FFinders%3Afirst. If the content was created at runtime with JS first will return nil if it runs the expectation before the link is created. – dgtized Aug 05 '16 at 22:33
137

Try the following:

within ".item" do
  click_link("Agree", :match => :first)
end

Sources:

gnclmorais
  • 4,897
  • 5
  • 30
  • 41
adamdboudreau
  • 1,371
  • 1
  • 8
  • 3
  • 1
    amazing. And it works on simple page.find('#{css}', :match => :first).click Cheers for the really helpful answer – Dono Jan 24 '14 at 13:36
26

This phrasing also works:

within first(".item") do
  click_link "Agree"
end
Elle Mundy
  • 2,099
  • 1
  • 19
  • 12
7

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

Bad:

first(".active").click
If there isn’t an .active element on the page yet, first will return nil and the click will fail.

Good:

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.

Salomanuel
  • 897
  • 10
  • 22
  • I believe this is the most accurate answer. – katericata Feb 04 '20 at 15:56
  • Confused. Looking at Capybara [`first`](https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:first) it says it waits `default_max_wait_time` seconds and for [`find`](https://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Finders:find) it also waits the same amount of time. I mention this as it's an old article that's been updated and I wonder if something might have changed? – notapatch Jan 17 '22 at 10:37
  • Definitely this helper was the best option for me `find(".active", match: :first).click`. – alexventuraio Aug 02 '22 at 15:56
5

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
DGM
  • 26,629
  • 7
  • 58
  • 79
2

Since first() doesn't always wait, perhaps this is useful:

expect(page).to have_css("selector")                               
first("selector").click
nroose
  • 1,689
  • 2
  • 21
  • 28
-4

Simple you can use:

$('.item').find('a').first().click();
Dũng IT
  • 2,751
  • 30
  • 29