Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing with custom theme palette #8000

Closed
darkhightech opened this issue Sep 1, 2017 · 15 comments
Closed

Testing with custom theme palette #8000

darkhightech opened this issue Sep 1, 2017 · 15 comments
Labels
support: question Community support but can be turned into an improvement test

Comments

@darkhightech
Copy link

darkhightech commented Sep 1, 2017

Problem description

If I create a custom theme and test a component that is leveraging a custom object from that theme, tests fail because that custom field is undefined, is there a documented way to test components with custom themes?

Steps to reproduce

Create a custom theme:

customTheme = {
  "direction": "ltr",
  "palette": {
     "customObject": {
         "customField1": "#FF0000"
     },
......
}

Shallow render custom component with a custom theme.

shallow(
<MuiThemeProvider theme={customTheme} >
<ComponentThatUsesCustomField1 />
</MuiThemeProvider
).dive()

Will throw an undefined error at customObject.

Versions

  • Material-UI: alpha v7
  • React: 15.6.1
  • Browser: latest
@oliviertassinari
Copy link
Member

oliviertassinari commented Sep 1, 2017

First thing, the createMuiTheme is doing the job correctly.

capture d ecran 2017-09-01 a 21 52 21

Second thing, the shallow function of enzyme doesn't propagate the context create by a component. You need to use the mount API.

@oliviertassinari oliviertassinari added support: question Community support but can be turned into an improvement test labels Sep 1, 2017
@mynameistechno
Copy link

Second thing, the shallow function of enzyme doesn't propagate the context create by a component. You need to use the mount API.

I tried using mount but enzyme still complains if I have custom variables in the theme. What is the best way to do this? Is there a cleaner way to do the below?

    const JobWithTheme = props => (
      <MuiThemeProvider theme={theme}>
        <Job {...props} />
      </MuiThemeProvider>
    );
    const wrapper = shallow(<JobWithTheme match={{params: {id: '1'}}} />);

Even then, I am running into this enzyme issue reported here where it's hard to setState of the nested component (the example uses MuiThemeProvider): enzymejs/enzyme#1289

Any pointers would be greatly appreciated.

@stocky37
Copy link

I worked around this by manually adding the custom theme in to the context like so:

import {CHANNEL} from '@material-ui/core/styles/themeListener';
import createBroadcast from 'brcast';

const broadcast = createBroadcast();
broadcast.setState(customTheme);
const wrapper = shallow(<ComponentThatUsesCustomField1 />, {
  context: {
    [CHANNEL]: broadcast,
  },
});

You can use the above logic to wrap createShallow or make it cleaner in any number of ways.

@DanielGibbsNZ
Copy link

@stocky37 Thank you so much! I've spent all evening trying to provide a custom theme via a context and this worked fine. The only thing I needed to do was to add childContextTypes:

const wrapper = shallow(<ComponentThatUsesCustomField1 />, {
  context: {
    [CHANNEL]: broadcast
  },
  childContextTypes: {
    [CHANNEL]: PropTypes.object
  }
});

@eps1lon
Copy link
Member

eps1lon commented Nov 8, 2018

You should not rely on implementation details about the theme context. Once we switch to the stable context API this will be obsolete. Use MuiThemeProvider instead.

@stocky37
Copy link

stocky37 commented Nov 8, 2018

But then you can’t use shallow in your unit tests, right?

@eps1lon
Copy link
Member

eps1lon commented Nov 8, 2018

I don't think so. Enzyme is currently lacking test methods for the new context API anyway and some people would argue that you should never test with shallow rendering anyway which is at least true for this particular test case in my opinion

@DanielGibbsNZ
Copy link

@eps1lon The reason that I wasn't using MuiThemeProvider in the tests is that a lot of the existing tests manipulate (set state, set props, check props) on the top-level component, so wrapping components in MuiThemeProvider breaks all of these tests.

@eps1lon
Copy link
Member

eps1lon commented Nov 9, 2018

We are currently having similar issues when switching from the legacy context api to the component based API and the issue is that our tests were to brittle to begin with. You can work around setProps and setState with helper methods although setState should only be used on rare occasions. Anything else should not rely on the actual component tree but the rendered dom tree.

@JavierLight
Copy link

Hello,

I'm experiencing a similar issue. I've wrapped a TextField in a MuiThemeProvider, so when using jest tests fail:

createMuiTheme -> createPalette -> augmentColor ()

It seems that it's unable to get the palette in the tests, even though it's working perfectly in the rest of scenarios (no browser console logs complaining when NODE_ENV='development', so our palette is well defined).

Is there any workaround to include this context for our tests (with shallow)? Maybe the augmentColor should not be raising an error if NODE_ENV='test' ?

Thank you very much for your help.

@eps1lon
Copy link
Member

eps1lon commented Nov 16, 2018

@JavierLight This is a fundamental issue with enzyme and shallow rendering. You could use either use shallow with dive or mount. Some additional ressources: https://blog.kentcdodds.com/why-i-never-use-shallow-rendering-c08851a68bb7

@smoleniuch
Copy link

@eps1lon

You should not rely on implementation details about the theme context. Once we switch to the stable
context API this will be obsolete. Use MuiThemeProvider instead.

I am trying to test my component which is wrapped by theme provider.The problem is that i can not use setProps method in the instance of wrapped component.
Here is my test case:

    it('Renders valid no data text', () => {

        let wrapper = mount(
         <ThemeProvider>
           <ResponsiveTable
            noDataText="no data"
            loadingError={false}
            isLoading={false}  data={[]}/>
         </ThemeProvider>
       )
        expect(wrapper.contains(<div>no data</div>)).toBeTruthy()
        wrapper.find('ResponsiveTable').instance().setProps({isLoading:'true'}) // this doesnt work due to lack of setProps method
        expect(wrapper.contains(<div>no data</div>)).toBeFalsy()

     })

I have some logic in the componentDidUpdate which i would like tot test, but i can't when i wrap my component in ThemeProvider.

I'am in dead end.What say you?

Cheers!

@michaelpward
Copy link

@oliviertassinari, @eps1lon, may I point out...

It's a very bad experience to spend hours on a Sunday trying to find out what I've done wrong, only to learn that failed tests are the expected result when following the MUI5 documentation for adding custom colors in a theme.

Frustrating.

@evasimonyi
Copy link

evasimonyi commented Jun 8, 2023

I had the same issue (custom theme fields being undefined, even when I used module augmentation as suggested in the MUI documentation: https://mui.com/material-ui/customization/palette/#typescript )
I am using @mui/material v5 and @testing-library/react, not enzyme, so not sure if this is helping or not, but I created a mock theme, using my custom palette, and module augmentation in the mockfile too, like this:

import { createTheme } from '@mui/material/styles';

declare module '@mui/material/styles' {
   interface Theme {
    "direction": string,
   }

  interface ThemeOptions {
    "direction": string,
  }

  interface Palette {
    "customObject": {
       "customField1": string,
    },
  }

  interface PaletteOptions {
    "customObject": {
      "customField1": string
    },
  }

export const mockTheme = createTheme({
  direction: "ltr",
  palette: {
     "customObject": {
         "customField1": "#FF0000"
     },
});

Then I used a custom render method to avoid wrapping every single component in the test. but you can just wrap them in the Theme provider, passing down the mocktheme value.

Custom renderer in a test-utils.ts file:

import React, { ReactElement } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { ThemeProvider } from '@mui/material/styles';
import { mockTheme } from './__mocks__/mockTheme';

const CustomThemeProvider = ({ children }: { children: React.ReactNode }) => (
  <ThemeProvider theme={mockTheme}>
    {children}
  </ThemeProvider>
);

const customRender = (
  ui: ReactElement,
  options?: Omit<RenderOptions, 'wrapper'>,
) => render(ui, { wrapper: CustomThemeProvider, ...options });

export * from '@testing-library/react';
export { customRender as render };

and using this custom render in my components:

import { screen } from '@testing-library/react';
import { render } from '../../../test-utils';

it('should render Run Information if user clicks on tab', async () => {
   const mockprops = {...};
    const { container } = render(<ComponentThatUsesCustomField1 {...props} />);
    expect(container.firstChild).toMatchSnapshot();
  });

Hope this helps! :)

@hattapoglu
Copy link

I have the same issue with a custom color in the button component. I worked around it by mocking the Button import:

vi.mock("@mui/material", async () => {
  const actual = (await vi.importActual("@mui/material")) as object;
  return {
    ...actual,
    Button: () => {
      return <button></button>;
    },
  };
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
support: question Community support but can be turned into an improvement test
Projects
None yet
Development

No branches or pull requests