0

It's easy to use Capybara Matcher on an element with single check like expect(element).to have_selector('#selector')|| BUT How do I achieve the same when asserting on an element that is part of a list and multiple checks need to be done to assert. I want to do something like:

<products>
  <product>
  <product>
  <product>
</products>

def have_product(name,price)
 products.any? {|product| product.have_text(name) && product.have_text(price)} # pseudo code
end

I want to be able to check whether a product with given name and price(both need to match) exists in the list.

Rahul
  • 321
  • 2
  • 14
  • This isn't a duplicate - The OPs issue can't actually be solved by doing anything linked in the issue provided as the duplicate – Thomas Walpole Sep 11 '19 at 15:25

1 Answers1

0

Simplest solution assuming you have already found the <products> element and don't need it to be the same product would be

expect(products).to have_css('product', text: name).and(have_css('product', text: price))

if it needs to be both texts in the same product element then use a regex

expect(products).to have_css('product', text: /#{product}.*#{price}/)

More advanced would be creating custom selector types for dealing with the types of objects in your UI.

UPDATE:

With the clarification of the original issue provided, the best solution is probably to use an optional filter block

expect(products).to have_css('product', text: name) { |node|
  node.has_field?(with: price)
}
Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
  • Thanks Thomas. The second approach is something similar to what I am after as the product needs to have both selectors to match in order to be the right assertion. The text regex won't work in my case as the each product on the page is a form element and price is an editable field where the text is the [value] attr. So I am looking for something like `expect(products).to have_css('product', text: {product}).and have_field('input', with: price)` - The issue is that both checks need to happen on the same element. BUT your answer is correct if it was to be done on text. – Rahul Sep 11 '19 at 07:02
  • @Rahul Updated to provide a solution using the optional filter block. Given the exact HTML there may be more performant ways, but assuming you don't have 100s of product elements in the products the filter block should be fine. – Thomas Walpole Sep 11 '19 at 15:32
  • Thanks Thomas. I have reworked my solution now another way but this is still interesting to know. The block will yield a boolean though right? Can have_field be used instead? – Rahul Sep 12 '19 at 06:13
  • @Rahul The block is used by the matcher (in this case `have_css` to filter the nodes that the matcher matched (in this case all `product` nodes containing the name text). For the filtering to work the block needs to return a boolean (think of it as like Array#filter) – Thomas Walpole Sep 12 '19 at 17:37
  • Thanks Thomas. I get what you mean now. so `have_css('product', text: name)` will only match products for which the block yields true. Interesting! Never knew matchers could be used this way! Thanks again. – Rahul Sep 13 '19 at 11:51