How to click first link in list of items after upgrading to Capybara 2.0?

82,581

Solution 1

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.

Solution 2

Try the following:

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

Sources:

Solution 3

This phrasing also works:

within first(".item") do
  click_link "Agree"
end

Solution 4

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.

Solution 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
Share:
82,581
tomekfranek
Author by

tomekfranek

Updated on June 18, 2020

Comments

  • tomekfranek
    tomekfranek almost 4 years

    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
    
  • Dono
    Dono over 10 years
    amazing. And it works on simple page.find('#{css}', :match => :first).click Cheers for the really helpful answer
  • Mauricio Moraes
    Mauricio Moraes over 9 years
    Worked for me, thanks. Capybara 2.4.1 and poltergeist.
  • dgtized
    dgtized over 7 years
    I would recommend against using #first, it doesn't wait for an element to exist: rubydoc.info/github/jnicklas/capybara/…. If the content was created at runtime with JS first will return nil if it runs the expectation before the link is created.
  • keoghpe
    keoghpe almost 7 years
    This example isn't relevant to capybara
  • emptywalls
    emptywalls almost 7 years
    Isn't that jQuery?
  • katericata
    katericata about 4 years
    I believe this is the most accurate answer.
  • notapatch
    notapatch over 2 years
    Confused. Looking at Capybara first it says it waits default_max_wait_time seconds and for 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?