Reputation: 7658
I am totally new to JavaScript testing and am working in a new codebase. I would like to write a test that is checking for a className on the element. I am working with Jest and React Testing Library. Below I have a test that will render a button based on the variant
prop. It also contains a className and I would like to test that.
it('Renders with a className equal to the variant', () => {
const { container } = render(<Button variant="default" />)
expect(container.firstChild) // Check for className here
})
I tried to google for a property like Enzyme has with hasClass
, but I couldn't find anything. How can I solve this with the current libraries (React Testing Library and Jest)?
Upvotes: 192
Views: 466647
Reputation: 2206
You should use toHaveClass
from Jest. No need to add more logic.
it('Renders with a className equal to the variant', () => {
const { container } = render(<Button variant="default" />)
expect(container.firstChild).toHaveClass(add you className);
// You can also use screen instead of container because container is not recommended as per Documentation
expect(screen.getByRole('button')).toHaveClass(add you className)
})
Upvotes: 18
Reputation: 1457
Well, nobody should use container
for all these cases, especially when you start working with the eslint plugins there are available for react-testing-library (more info: https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
Ok, without container it's getting a bit hard to select basic elements. But there is a documentation about all ARIA roles here: https://www.w3.org/TR/html-aria/#docconformance
And so we stumble on this:
https://www.w3.org/TR/html-aria/#docconformance
Yeah of course you SHOULD NOT use this - but for testing it's totally fine i would say. I mean, you should not use it in the real application, but this way, i can rewrite my test to make eslint happy again, and i think this should be the correct answer here:
const body = screen.getByRole("generic");
expect(body.classList.contains("bodyScrollLock-all")).toBe(true);
const paragraph = screen.getByRole("paragraph");
expect(paragraph.classList.contains("text-primary")).toBe(true);
Upvotes: 0
Reputation: 3481
This a Real-World Storybook example with Jest and React Testing Library for testing <p>
element, by its class name in this case, it's text-primary
.
Empty.play = async ({ canvasElement, step }) => {
const canvas = within(canvasElement)
const paragraph = container.querySelector('p')
await step('Should render Empty correctly', async () => {
expect(paragraph).toBeInTheDocument()
expect(paragraph.classList.contains('text-primary')).toBeFalsy()
})
await step('Should handle click', async () => {
userEvent.click(paragraph)
await waitFor(() => {
expect(
container.querySelector('p').classList.contains('text-primary')
).toBeTruthy()
})
})
}
Upvotes: 0
Reputation: 1876
You need to understand the philosophy behind react-testing-library to understand what you can do and what you can't do with it;
The goal behind react-testing-library is for the tests to avoid including implementation details of your components and rather focus on writing tests that give you the confidence for which they are intended.
So querying element by classname is not aligned with the react-testing-library philosophy as it includes implementation details. The classname is actual the implementation detail of an element and is not something the end user will see, and it is subjected to change at anytime in the lifecycle of the element.
So instead of searching an element by what the user cannot see, and something that can change at anytime, just try to search by using something that the user can see, such as text, label or something that will remain constant in the life cycle of the element like data-id.
So to answer your question, it is not advised to test classname and hence you cannot do that with react-testing-library. Try with other test libraries such as Enzyme or react-dom test utils.
Upvotes: 51
Reputation: 1
it('check FAQ link is working or not', () => {
const mockStore = configureStore({ reducer: Reducers });
const { container } = render(
<GraphqlProvider>
<Provider store={mockStore}>
<BrowserRouter>
<FAQ />
</BrowserRouter>
</Provider>
</GraphqlProvider>,
);
const faqLink = container.getElementsByClassName('breadcrumb-item active');
expect(faqLink[0].textContent).toBe('FAQ /');
});
});
Upvotes: -1
Reputation: 446
You can use toHaveClass from jest DOM
it('renders textinput with optional classes', () => {
const { container } = render(<TextArea {...props} className='class1' />)
expect(container.children[1]).toHaveClass('class1')
})
Don't forgot to destructure response like this {container}
Because By default, React Testing Library will create a div and append that div to the document.body
and this is where your React component will be rendered. If you provide your own HTMLElement
container via this option, it will not be appended to the document.body
automatically.
Upvotes: 7
Reputation: 598
// Link.react.test.js
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import App from './../../src/App'
describe('React', () => {
it('className', () => {
const renderer = new ShallowRenderer();
renderer.render(<App />);
const result = renderer.getRenderOutput();
expect(result.props.className.split(' ').includes('welcome-framework')).toBe(true);
});
});
Upvotes: -2
Reputation: 23291
The library gives access to normal DOM selectors, so we can also simply do this:
it('Renders with a className equal to the variant', () => {
const { container } = render(<Button variant="default" />)
expect(container.getElementsByClassName('default').length).toBe(1);
});
Upvotes: 106
Reputation: 1743
You can use testing-library/jest-dom custom matchers.
The @testing-library/jest-dom library provides a set of custom jest matchers that you can use to extend jest. These will make your tests more declarative, clear to read and to maintain.
https://github.com/testing-library/jest-dom#tohaveclass
it('Renders with a className equal to the variant', () => {
const { container } = render(<Button variant="default" />)
expect(container.firstChild).toHaveClass('class-you-are-testing')
})
This can be set up globally in a setupTest.js
file
import '@testing-library/jest-dom/extend-expect';
import 'jest-axe/extend-expect';
// etc
Upvotes: 35
Reputation: 27038
You can easily do that with react-testing-library.
First, you have to understand that container
or the result of getByText
etc. are merely DOM nodes. You can interact with them in the same way you would do in a browser.
So, if you want to know what class is applied to container.firstChild
you can just do it like this container.firstChild.className
.
If you read more about className
in MDN you'll see that it returns all the classes applied to your element separated by a space, that is:
<div class="foo"> => className === 'foo'
<div class="foo bar"> => className === 'foo bar'
This might not be the best solution depending on your case. No worries, you can use another browser API, for example classList
.
expect(container.firstChild.classList.contains('foo')).toBe(true)
That's it! No need to learn a new API that works only for tests. It's just as in the browser.
If checking for a class is something you do often you can make the tests easier by adding jest-dom to your project.
The test then becomes:
expect(container.firstChild).toHaveClass('foo')
There are a bunch of other handy methods like toHaveStyle
that could help you.
As a side note, react-testing-library is a proper JavaScript testing utility. It has many advantages over other libraries. I encourage you to join the spectrum forum if you're new to JavaScript testing.
Upvotes: 237