chaance / vitest-dom

Custom Vitest matchers to test the state of the DOM, forked from jest-dom.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 */
}