Giesburts
Giesburts

Reputation: 7658

How to test a className with the Jest and React testing library

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

Answers (10)

Yazan Najjar
Yazan Najjar

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

jebbie
jebbie

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:

enter image description here 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

ahmnouira
ahmnouira

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

kasongoyo
kasongoyo

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

Abdul Mazood
Abdul Mazood

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

Shamaz saeed
Shamaz saeed

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

YinPeng.Wei
YinPeng.Wei

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

jbmilgrom
jbmilgrom

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

glenrothes
glenrothes

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

Gio Polvara
Gio Polvara

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

Related Questions