teamcapybara/capybara

Add a has_test_id? matcher

tmaier opened this issue · 3 comments

tmaier commented

I want to use the test_id feature more.
I configured Capybara.test_id = "data-testid"

I see that there is support for test ids in many matchers.
(In geeneral, it would be great to have support for it in all matchers.)

I want to test, if a certain element with a test id exists.
Currently, I can only test for it with the has_css matcher.
like expect(page).to have_css("[data-testid='my-id']"))

This is tedious to write and it hardcodes data-testid.

I would prefer to have a dedicated matcher for test_id called has_test_id?
One could use it like this: expect(page).to have_test_id('my-id')

Pseudo code implementation:

def has_test_id?(test_id)
  has_css?("[#{Capybara.test_id}='#{test_id}']")
end

Meta

Capybara Version: capybara (3.39.2)
Driver Information (and browser if relevant):
N/A

You can use Capybara.add_selector to define any custom selectors you like in your project

tmaier commented

Fair enough.

I solved it with some Rspec matchers. They are called have_test_id and have_test_id_and_css.
I hope they are helpful for someone. See my blog post introducing them: https://tobiasmaier.info/posts/2023/06/19/rspec-capybara-data-testid.html

@tmaier The way you've implemented (via predicates) has a number of downsides, not least of which you can't use it with any of the finders, which is what you really want test ids for. Nobody really wants to know, or assert that a test id exists in the document (end users don't know about test ids), what you want is to be able to find the element with the test id so you can scope interactions with the page. You'll be much better off if you add a custom Capybara selector, and then build any matchers based on the have_selector matcher. By creating a selector called testid (or whatever you want) then you can do things like find(:testid, 'my_test_id').select('some option') etc. (or create a find_testid helper based off find). That way you are extending Capybara functionality not building a couple of pieces beside it which won't work well in conjunction.