React testing library can't read styles using Tailwind CSS classes
Asked Answered
Z

4

14

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>
Zingale answered 6/2, 2022 at 18:27 Comment(2)
Why not test the class, given the underlying styles won't be applied in unit tests?Belmonte
@Belmonte because you might change how you hide your component but the test should still run plus because you do not want to couple your test with implementation details but rather with how users are expecting this to work. Totally disappointment that we can not test react styles with Tailwind using RTL... Since what 2 years if not more...Veritable
G
6

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

Gauffer answered 22/10, 2022 at 3:5 Comment(3)
it doesn't work for me :( I guess without configuring tailwind css, can't test tailwind css classes anymore!Argillaceous
While this works I find that it makes the test performance too slow. Most end up timing out.Coxa
I find it unacceptable that two tools we are using a lot in React world do not work together...Veritable
A
4

This solution works well for NextJS 13 application

Use css:true in vitest.config.ts:

import { defineConfig } from 'vite';
import { configDefaults } from 'vitest/config';

export default defineConfig({
  ...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './'),
    },
  },
  test: {
    // this option
    css: true,
    setupFiles: ['./tests/setupTests.ts'],
    ...
  }
});

See more here: https://vitest.dev/config/#css

and then import your main CSS file in setupTests.ts

import '@/src/styles/globals.css';

where global.css starts with:

@tailwind base;
@tailwind components;
@tailwind utilities;
Austronesia answered 15/11, 2023 at 17:37 Comment(0)
F
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.

Fer answered 8/8, 2023 at 8:59 Comment(0)
A
0

I also encountered the same situation and tried to follow this upvoted answer many times but nothing happened.

For me what it worked I tried to solve this by mocking the tailwind CSS from node modules, like this:

  • jest.config.js
module.exports = {
      moduleNameMapper: {
        //.. all mocks names
        '\\.(css|less)$': '<rootDir>/node_modules/tailwindcss',
      },
}

I hope this someone helps!

Argillaceous answered 2/1, 2024 at 12:41 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.