Styles from CSS modules are not being detected
danascheider opened this issue · comments
Hello lovely maintainers! I've got an issue I can't seem to resolve and wanted to surface it with you in case it's a bug. The same issue occurs if I use testing-library/jest-dom
instead, so I think whatever it is predates this package. I have come across this issue trying to beef up test coverage on working code, so the problems I'm describing here only occur in the test.
I have simplified the code below to provide as much of a minimal example as possible. Any help with this would be much appreciated as I won't be able to use this tool until I get to the bottom of this. I'm happy to provide more information and can also offer some help figuring out the problem/fixing it but might not have time to do all of it by myself.
The Problem
I've got a React component with an element whose display
property is set to either flex
or none
depending on the presence of a class, "hidden"
. When I use the toBeVisible()
matcher, it does not pick up when the display
is none
and says the element is visible. When I change it to use the toHaveStyle({ display: 'none' })
matcher, it reveals that the display
is always being read as block
and never as either flex
or none
. Nowhere in the CSS is display
ever set to block
, which is the default style for the element in question (div
). If I check another property with a default value, say toHaveStyle({ position: 'static' })
that fails, indicating that no position
style applies (one does).
On the other hand, if I check toHaveClass('hidden')
, that passes or fails correctly - classes are set as expected.
Tech Stack
I'm using:
- Vite 4.1.4
- Vitest 0.29.2
- React 18.2.0
- JSDOM 21.1.1
Code
In sharing my app code and Vite config, it is important to emphasise that this code is working in both dev and production environments.
Config
First, here is my Vite and Vitest config (vite.config.ts):
/// <reference types='vitest' />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePluginFonts } from 'vite-plugin-fonts'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
VitePluginFonts({
google: {
families: [
{
name: 'Cinzel Decorative',
styles: 'wght@700',
},
{
name: 'Quattrocento Sans',
styles: 'ital,wght@0,400;0,700;1,400',
},
],
},
}),
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['src/support/vitest-setup.ts'],
},
})
Test Setup and Utils
My vitest-setup.ts
file:
import 'vitest-dom/extend-expect'
My renderWithRouter
function used in the test (src/support/testUtils.tsx):
import { type ReactElement } from 'react'
import { BrowserRouter } from 'react-router-dom'
import { render } from '@testing-library/react'
import { JSDOM } from 'jsdom'
const renderWithRouter = (ui: ReactElement, url?: string) => {
const dom = new JSDOM('<!doctype html><html><body></body></html>'), {
url: url || 'http://localhost:5173'
}
global.window = dom.window as unknown as Window & typeof globalThis
global.document = dom.window.document
render(<BrowserRouter>{ui}</BrowserRouter>, url)
}
Test Code
src/components/modal/modal.test.tsx:
import { describe, test, expect } from 'vitest'
import { renderWithRouter } from '../../support/testUtils'
import GameForm from '../gameForm/gameForm'
import Modal from './modal'
describe('Modal', () => {
test("isn't visible when hidden is set to true", () => {
const wrapper = renderWithRouter(
<Modal hidden={true}>
<GameForm submitForm={() => {}} />
</Modal>
const modal = wrapper.getByTestId('modal')
expect(modal).not.toBeVisible() // fails, says it is visible
expect(modal).toHaveStyle({ display: 'none' }) // fails, says display is "block"
)
})
})
App Code
src/components/modal/modal.tsx:
import { type ReactElement } from 'react'
import classNames from 'classnames'
import styles from './modal.module.css'
interface ModalProps {
hidden: boolean
children: ReactElement
}
const Modal = ({ hidden, children }: ModalProps) => (
<div
className={classNames(styles.root, { [styles.hidden]: hidden })}
data-testid="modal"
>
<div className={styles.container}>
<div className={styles.content}>
{children}
</div>
</div>
</div>
)
Note that, even if I take away classNames
and just apply the hidden
styles all the time, the failure is the same.
CSS Module
src/components/modal/modal.module.css:
.root {
display: flex;
align-items: center;
/* other styles */
}
.hidden {
display: none;
}
.container {
/* styles */
}
.content {
/* styles */
}