Dom-testing-library: getByCSSSelector or something like that

Created on 28 Sep 2018  路  8Comments  路  Source: testing-library/dom-testing-library

Describe the feature you'd like:

Ability to find elements from a rendered DOM by some sort of CSS selector, a la jQuery.

For example, I have this DOM:

<button disabled="" type="button" class="ant-btn ant-btn-primary ant-btn-circle ant-btn-icon-only"><i class="anticon anticon-plus"><svg viewBox="64 64 896 896" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path snip></path></svg></i></button>

It's a <Button/> component from Ant Design but not important.

Now of the existing methods allow me to get this DOM element. I could modify the main code and inject a data-testid="add-button" and then use getByTestId but then it feels like the code has to work for the tests rather than the other way around.

Suggested implementation:

const { getByCSSSelector } = render(<Button disabled={false} icon="plus"/>)
fireEvent(getByCSSSelector('button')).click()

// or...
console.log(getByCSSSelector('button i.anticon'))
// or...
console.log(getByCSSSelector('button i svg'))

Describe alternatives you've considered:

Another challenge I found was I was trying to see if a DOM element existed based on it's classList. The DOM element, when rendered would (depending on business logic) set (or not set) className="spin-animation".

Then I'd be able to do:

const { getByCSSSelector } = render(<MyComponent key={value}/>)
expect(getByCSSSelector('div.spin-animation')).toBeInTheDocument()

Teachability, Documentation, Adoption, Migration Strategy:

No migration needed. A better name that CSSSelector maybe. Perhaps just querySelector or getBySelector.

Most helpful comment

Just out of curiosity, I'm using a 3rd party framework for a select component. How would you handle querying for elements in the case that the mark up isn't proprietary and you can't add test id's etc?

Class seems to be only option and likely more stable bc it's a framework?

All 8 comments

An existing solution would be to render to a container I guess.

const { container } = render(<ComponentWithButtonSomewhere />);
const button = container.querySelector('button');
expect(button).not.toBe(null);

Hi @peterbe!

Correct, that's the solution if you're going to use CSS selectors.

Also, I strongly recommend against using CSS selectors in your tests. Use the other queries. If you can't, use data-testids: https://www.youtube.com/watch?v=N4ZDBOc2tX8&list=PLH3mK1NZn9QqOSj3ObrP3xL8tEJQ12-vL

In any case, we're definitely not going to make it any easier to write tests this way.

"We're going to be talking about writing Science Fiction novels".
I think you pasted the wrong youtube link.

I'm guessing you meant a video that explains why you should put in data-testid attributes on DOM nodes just for the sole benefit of being able to write tests. Looking forward to taking that in.

By the way, I spent an hour last night watching your videos on react-testing-library. Excellent work! I'm still getting used to things and I'm getting there very slowly.

Whoops! Mixed up my clipboard. lol

Here's the blog post I wanted to reference: https://blog.kentcdodds.com/making-your-ui-tests-resilient-to-change-d37a6ee37269

Good luck!

Just out of curiosity, I'm using a 3rd party framework for a select component. How would you handle querying for elements in the case that the mark up isn't proprietary and you can't add test id's etc?

Class seems to be only option and likely more stable bc it's a framework?

If it's an accessible component you can probably use labels, roles, and text for many of the interactions. If not and the classnames are component-oriented they might be an OK fallback.

@emily-ellevation As Kent pointed out, use container.querySelector() as mentioned above but we aware of the disadvantages which could quite possibly be out of your control. It was for me.

Just to add to the mix, you can also provide a custom matcher function to your queries, and use it to properly traverse your component's DOM nodes!

import { render } from "@testing-library/react"

it("test", () => {
  const { getByText } = render(<Component />)

  getByText((content, node) => {
    // my custom function
  })
})

hope it helps!

Was this page helpful?
0 / 5 - 0 ratings