reactjstypescriptjestjsmaterial-ui

Cannot read properties of undefined (reading 'mode')


I am using these packages:

"@mui/material": "^7.0.2",
"jest": "^29.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.8.3",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",

I am using a React Component 'Alert' from Material UI as shown below.

import Alert from "@mui/material/Alert";

<Alert
  key="error"
  icon={<ReportIcon />}
  onClose={() => setError(false)}
  severity="error"
  sx={{ alignItems: 'flex-start' }}>
  {errorMsgData ?? "Error Saving Configuration"} 
</Alert>

this works fine in application, but in unit test it fails with the console log shown below:


    Cannot read properties of undefined (reading 'mode')
    TypeError: Cannot read properties of undefined (reading 'mode')
    at /Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@mui/material/Alert/Alert.js:57:34
    at styleMemoized (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@mui/system/memoTheme.js:28:46)
    at processStyle (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@mui/system/createStyled/createStyled.js:47:55)
    at styleFunctionProcessor (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@mui/system/createStyled/createStyled.js:158:18)
    at handleInterpolation (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@emotion/serialize/dist/emotion-serialize.cjs.js:119:24)
    at Object.serializeStyles (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@emotion/serialize/dist/emotion-serialize.cjs.js:219:15)
    at /Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@emotion/styled/base/dist/emotion-styled-base.browser.cjs.js:141:34
    at /Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/@emotion/react/dist/emotion-element-25f9958c.browser.cjs.js:57:12
    at renderWithHooks (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:15486:18)
    at updateForwardRef (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:19245:20)
    at beginWork (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:21675:16)
    at beginWork$1 (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:27465:14)
    at performUnitOfWork (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:26599:12)
    at workLoopSync (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:26505:5)
    at renderRootSync (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:26473:7)
    at recoverFromConcurrentError (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:25889:20)
    at performConcurrentWorkOnRoot (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/react-dom/cjs/react-dom.development.js:25789:22)
    at workLoop (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/scheduler/cjs/scheduler.development.js:266:34)
    at flushWork (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/scheduler/cjs/scheduler.development.js:239:14)
    at performWorkUntilDeadline (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/scheduler/cjs/scheduler.development.js:533:21)
    at Timeout.task \[as \_onTimeout\] (/Users/asitu/Documents/repos/allstate/kitab-ui/node_modules/jsdom/lib/jsdom/browser/Window.js:520:19)
    at listOnTimeout (node:internal/timers:569:17)
    at processTimers (node:internal/timers:512:7)

What could be the reason for this issue?

Test case that I have written:

    beforeEach(async () => {
        const theme = createTheme();
        renderResult = render(
            <ThemeProvider theme={theme}> // suggestion from 1st answer implemented and it works but i have few questions
            <AlertProvider AlertComponent={AlertDialog}>
                <Configuration index={0}/>
            </AlertProvider></ThemeProvider>,
            { preloadedState: storePreLoadedData });
    });
    
    ...
    
    it('should be an invalid form on input change', async () => {
                const editButtonIcon = screen.getByTestId('editButton');
                fireEvent.click(editButtonIcon);
    
                const input = screen.getByRole('textbox', { name: "transactionTypeEntCd" });
                fireEvent.change(input, { target: { value: '' } });
    
                const formElement = document.querySelector(fieldsForm) as HTMLFormElement;
                expect(formElement.checkValidity()).toBe(false);
            });

Solution

  • You can fix this by adding a ThemeProvider to your test files before rendering the components.

    It looks similar to this code snippet:

    const theme = createTheme();
    
    test('renders error alert', () => {
    render(
      <ThemeProvider theme={theme}>
        <MyComponent />
      </ThemeProvider>
    );
    
    expect(screen.getByText(/error saving configuration/i)).toBeInTheDocument();
    });
    

    If you aren't using a ThemeProvider in your App code already, you can create another file that just accepts a children and injects the theme:

    const theme = createTheme()
    const LibProvider = ({children}) => {
      return (
        <ThemeProvider theme={theme}>
          {children}
        </ThemeProvider>
    
      )
    }
    

    Then you can import this file to both your app root file and your tests, to avoid duplicating code.