React testing library and styled components - cannot read properties of undefined
Asked Answered
E

3

10

Am trying to use react testing library on my project, but it isn't happy about the theme being passed to styled components in props, and I am getting this error:"TypeError: Cannot read properties of undefined". The full error message can be seen below.

error message

This is the component I am testing (a very simple one as I was trying to check testing library was working):

import React from "react";
import styled from "styled-components/macro";
import { rem } from "polished";
import DoneIcon from "@mui/icons-material/Done";

const Flag = styled.div`
  background-color: ${(props) => props.theme.colors.primary};
  font-size: ${rem("14px")};
  display: inline-flex;
  align-items: center;
  height: 32px;
  border-radius: 16px;
  padding: 0 12px 0 8px;
  margin-bottom: ${rem("16px")};
  color: #ffffff;
`;

const StyledDoneIcon = styled(DoneIcon)`
  margin-right: 4px;
`;

const CompletionFlag = (props) => {
  console.log("props: ", props);
  return (
    <Flag>
      <StyledDoneIcon />
      <span>Complete</span>
    </Flag>
  );
};

export default CompletionFlag;

And this is the test that is failing (I am yet to write the full test as the render throws the error):

import { render } from "../../../test-utils/testing-library-utils";
import CompletionFlag from "../CompletionFlag";

test("check text in completion flag", () => {
  render(<CompletionFlag />);
});

And here is my custom render taken from the testing library docs:

import React from "react";
import { render } from "@testing-library/react";
import { ThemeProvider } from "@mui/material/styles";
import { theme } from "../global-styles/global";

const AllTheProviders = ({ children }) => {
  console.log("theme: ", theme);
  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
};

const customRender = (ui, options) =>
  render(ui, { wrapper: AllTheProviders, ...options });

export * from "@testing-library/react";

export { customRender as render };

Probably not necessary, but for reference this is how we are actually using the theme in our app component:

import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { StyledEngineProvider } from "@mui/material/styles";
import { ThemeProvider } from "@mui/material/styles";

import { GlobalStyle, theme } from "./global-styles/global";
import Login from "./components/pages/Login";
import Home from "./components/pages/Home";
import PageNotFound from "./components/pages/PageNotFound";
import { PrivateRoute } from "./components/organisms/PrivateRoute";

function App() {
  return (
    <StyledEngineProvider injectFirst>
      <ThemeProvider theme={theme}>
        <GlobalStyle />
        <BrowserRouter
          basename={
            process.env.REACT_APP_ENVIRONMENT === "development"
              ? "/"
              : process.env.PUBLIC_URL
          }
        >
          <Routes>
            <Route path="/login" element={<Login />} />
            <Route path="/" element={<PrivateRoute />}>
              <Route path="/:collectionId/home" element={<Home />} />
              <Route path="/*" element={<PageNotFound />} />
              <Route path="*" element={<PageNotFound />} />
            </Route>
          </Routes>
        </BrowserRouter>
      </ThemeProvider>
    </StyledEngineProvider>
  );
}

export default App;

Any help would be appreciated as my googling hasn't helped so far.

Emanate answered 14/3, 2022 at 15:16 Comment(3)
I am having this issue too. Did you figured it out?Bible
I gave up and switched to using emotion instead of styled components which solved the issue. Has helped with some other setup issues we were having also.Emanate
@JulioW. did you find a solution? I'm also struggling with thisGuarantor
K
6

You should try to wrap the CompletionFlag component in your unit test into ThemeProvider and provide the theme to it

import { render } from "../../../test-utils/testing-library-utils";
import CompletionFlag from "../CompletionFlag";
import { ThemeProvider } from "@mui/material/styles";
import { theme } from "../global-styles/global";

test("check text in completion flag", () => {
  render(
    <ThemeProvider theme={theme}>
      <CompletionFlag />
    </ThemeProvider>
    );
});
Koumis answered 30/3, 2022 at 18:13 Comment(1)
Thanks Alex. I think I have already done this in the custom render above though? const AllTheProviders = ({ children }) => { return <ThemeProvider theme={theme}>{children}</ThemeProvider>; };Emanate
H
0

I had exactly this problem today and solved it. So the problem is that the import is wrong. Import must be

import { render } from "../../../test-utils";

and not

import { render } from "../../../test-utils/testing-library-utils";

Last one again takes the default function from RTL. And our custom "proxy" render doesn't work. In my case mistake was caused because of the auto imported ... Also I would advise you to make imports absolute paths (not relative ones)

Hixon answered 30/12, 2023 at 17:4 Comment(0)
E
-1

I think the colors property is missing from the themes object. Have you checked the themes object if colors is there. To just get rid of this error you can do a conditional check like this.

background-color: ${(props) => props.theme.colors?.primary};
Elliott answered 14/3, 2022 at 16:8 Comment(1)
Thanks for the comment. There is a console log in the custom render which appears to show that colors is coming through correctly as part of the theme. But of course I suppose this doesn't mean it is getting to the test component. Any idea how I might be able to console theme within the test component itself?Emanate

© 2022 - 2024 — McMap. All rights reserved.