cssreactjsviteemotionvitest

Emotion css props are not getting the theme object when running tests with vitest


Context: I am migrating a project over from CRA to the Vite ecosystem and I am having some issues with the tests. I did not have this problem when these tests were run with Jest in the CRA environment.

The project I am working on makes use of emotion's css prop. It is a common occurrence to be passing down the theme to the css interpolation function, like so, for example:

//styles.ts
export const componentStyles = (theme: Theme) => {
  console.log(theme);

  return css`
    font-size: ${theme.typography.fontSizes['24']};
    color: ${theme.utils.getColor('lightGrey', 500)};
  `;
};
//Component.tsx
import { componentStyles } from './styles.ts';

export default function SomeComponent() {
  return (
    <div css={componentStyles}>
      Hello
    </div>
  );
}

During runtime the above prints the theme object correctly. The problem starts when I try to run tests using vitest, where theme comes in as {}

My vite.config.ts is the following:

/// <reference types="vitest" />

import react from '@vitejs/plugin-react';
import { defineConfig, loadEnv } from 'vite';
import { ViteEjsPlugin } from 'vite-plugin-ejs';
import svgr from 'vite-plugin-svgr';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), 'VITE_');

  return {
    define: {
      'process.env.NODE_ENV': JSON.stringify(mode),
    },
    plugins: [
      react({
        babel: {
          plugins: ['@emotion/babel-plugin'],
        },
      }),
      tsconfigPaths(),
      svgr(),
      ViteEjsPlugin(),
    ],
    build: {
      outDir: 'build',
    },
    test: {
      globals: true,
      environment: 'happy-dom',
      setupFiles: './src/setupTests.ts',
    },
  };
});

I cannot understand why there is this disparity between actually running the code and running the code for the tests. Isn't vitest taking into account any configuration you have made for vite, so as to keep parity? At least that is my understanding from the docs

Any pointers to the right direction are fine, thanks


Solution

  • I had a similar issue recently when migrating a legacy monolith from jest to vitest. I found the issue was that the context of the theme got lost when it came to styled components.

    I already had a withTheme wrapper for my tests which worked for jest but not for vitest

    A temporary workaround was to add an additional themeProvider only for my tests. Below is an example of how I passed both the MUI themeProvider and the @emotion/react theme provider.

    This fixed the theme object in my tests.

    Hopefully this offers a temporary solution

    import React from 'react';
    import { ThemeProvider } from '@mui/material/styles';
    import { ThemeProvider as EmotionThemeProvider } from '@emotion/react'
    import getTheme from './theme';
    
    const withTheme = (Component) => {
      const theme = getTheme('light');
    
      return (
        <ThemeProvider theme={theme}>
          <EmotionThemeProvider theme={theme}>
              {Component}
          </EmotionThemeProvider>
        </ThemeProvider>
      );
    };
    export default withTheme;