Sig
Sig

Reputation: 5916

Specify the element when multiple elements matching

How can I with capybara select an item using within?

I have the following HTML

<div id ="projects"> 
  ...
  <div class="card">lorem</div>
  <div class="card">ipsum</div>
</div>

And I need to select the second div with class='card'.

  within(:css, '#attachments') do
    within(:css, '.card') do
     expect(page).to have_text('ipsum')
    end
  end

But not sure how to do that.

Upvotes: 1

Views: 1637

Answers (2)

fabdurso
fabdurso

Reputation: 2444

You can use

within all(:css, ".card")[1] do
  expect(page).to have_text('ipsum')
end

By the way, if you haven't set a different default_selector in your spec_helper.rb, like

Capybara.default_selector = :xpath

you can just use

within all(".card")[1] do
 expect(page).to have_text('ipsum')
end

since by default Capybara will use :css as default_selector.

Upvotes: 4

Thomas Walpole
Thomas Walpole

Reputation: 49870

There are many ways to select that element that would depend on what exactly the ... document structure before the two divs is (are there other divs, etc). One method that wouldn't depend on that structure is shown in the answer by @fabersky, which is to use all and index into the results (all('.card')[1]) - and if the page is highly dynamic you might want to specify a minimum on that like (all('.card', minimum: 2)[1]). Another option would be to write an XPath to match only the correct element (it's generally better practice to prefer selectors that return a single element over using all and indexing into the results whenever possible).

within(:css, '#attachments') do
  within(:xpath, XPath.css('.card')[2]) do
    expect(page).to have_text('ipsum')
  end
end    

In this case the [2] is part of the XPath expression (which becomes .//*[contains(concat(' ', normalize-space(@class), ' '), ' card ')][2]) so the indexing does start at 1

Upvotes: 0

Related Questions