9

I have a simple React component that will initially have a Tailwind CSS class of hidden which apply CSS display: none and will change the class to visible on button click. When I test with expect().not.toBeVisible() it tells me the element is already visible while it has a hidden class.

If I don't use Tailwind CSS and use a normal style={{display: 'none'}} it'll correctly identify that the element isn't visible. That means clearly the issue is with Tailwind CSS.

Here's my test:

test("Notification bar should be initially hidden but visible on click", async () => {
    render(<Notifications />);

    expect(await screen.findByTestId("list")).not.toBeVisible();
    // this test fails while the element already has a Tailwind CSS class of "hidden"
});

While this's my component:

<ul className="hidden" data-testid="list">
  <li>item 1</li>
</ul>
Ed Lucas
  • 5,955
  • 4
  • 30
  • 42
Ahmed Hossam
  • 161
  • 1
  • 3
  • 4

2 Answers2

4

The solution explained in this Stack Overflow: cannot check expectelm not tobevisible for semantic ui react component. Based on that thread, I extend the solution to make it works with TailwindCSS as the steps explained below,

Project structure

root/
   src/
      test/
         index.css
         test-utils.tsx
         component.test.tsx
      index.css

1. Generate CSS from the TailwindCSS template files

By issuing the command below, the CSS file called index.css will be generated in src/test directory

npx tailwindcss -i ./src/index.css -o ./src/test/index.css

Further reading: TailwindCSS installation

2. Create custom render function

Next we need to inject the generated CSS file into the JSDOM. Custom render function will be useful so we won't be needed to repeat this task for each test

import { render, RenderOptions } from '@testing-library/react';
import React, { FC, ReactElement } from 'react';
import fs from 'fs';

const wrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
  return <>{children}<>;
};

const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => {
  const view = render(ui, { wrapper, ...options });

  const style = document.createElement('style');
  style.innerHTML = fs.readFileSync('src/test/index.css', 'utf8');
  document.head.appendChild(style);

  return view;
};

export * from '@testing-library/react';
export { customRender as render };

Further reading: Testing Library Setup

3. Perform testing, unit test suppose to be success now

import React from 'react';
import { render, screen } from './test-utils';

test('Renders hidden hello world', () => {
  render(<span className="hidden">Hello World</span>);
  expect(screen.getByText('Hello World')).not.toBeVisible();
});

Why souldn't we use toHaveClass matchers instead?

it wouldn't align with the Testing Library guiding principle of “emphasize a focus on tests that closely resemble how your web pages are interacted by the users“ because by doing so, you are interacting with the component unnaturally

Varid Vaya
  • 194
  • 1
  • 15
0

If your are using Vite as your bundler and Vitest as the testing framework, you can use the css: true option in the Vitest options to enable processing CSS in test files. See more here: https://vitest.dev/config/#css

Additionally, you will have to import your index.css (or whatever path name it is) in your component test or, even better, in your Vitest setupTests file.

Robin
  • 1
  • 1