Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getByCSSSelector or something like that #108

Closed
peterbe opened this issue Sep 28, 2018 · 10 comments
Closed

getByCSSSelector or something like that #108

peterbe opened this issue Sep 28, 2018 · 10 comments

Comments

@peterbe
Copy link

peterbe commented Sep 28, 2018

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.

@peterbe
Copy link
Author

peterbe commented Sep 28, 2018

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);

@kentcdodds
Copy link
Member

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.

@peterbe
Copy link
Author

peterbe commented Sep 28, 2018

"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.

@kentcdodds
Copy link
Member

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!

@emily-ellevation
Copy link

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?

@alexkrolick
Copy link
Collaborator

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.

@peterbe
Copy link
Author

peterbe commented Jul 15, 2019

@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.

@afontcu
Copy link
Member

afontcu commented Jul 16, 2019

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!

@moshfeu
Copy link

moshfeu commented Apr 28, 2022

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);

Notice that this solution is sync so it's not equivalent to findBy queries.

@KaelWD
Copy link

KaelWD commented Sep 10, 2024

I needed this to migrate some old tests that would be way too much work to fully rewrite so I added *ByCSS to my fork.
You can install it with

"overrides": {
  "@testing-library/dom": "npm:@vuetify/[email protected]"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants