Hi everyone,
I've been trying all day to get Jest working with my Styled Components. I opened the following Stack Overflow question to get some help with it.
All of the explanation (and more) about my problems you can find on that page. Does any one here has any idea on how to solve this problem?
All help will be greatly appreciated!
Personally I provide default props to any styled component using the theme, e.g:
const Wrapper = styled.div`
color: ${props => props.theme.color.primary};
`;
Wrapper.defaultProps = {
theme: {
color: {
primary: "hotpink",
},
},
};
You can streamline this by making an object of the basic default props for your theme, and importing it, e.g:
const defaultThemeProps = {
theme: {
color: {
primary: 'hotpink',
secondary: 'coral',
},
font: {
regular: 'arial',
bold: 'helvetica',
},
},
};
Then in your component:
import defaultThemeProps from './defaultThemeProps';
...
Wrapper.defaultProps = defaultThemeProps;
This solution fits my use-case because I need to be able to render the components independent of a theme sometimes. Another benefit of this is that shallow can still be used in tests, which is nice.
@JonShort I was thinking about going this route too. Would be a little overkill since my application will only have two themes. However, it will fix the issues with tests.
If anyone has any other solutions please share them, very interested in the different ways on how to solve this problem!
@JonShort I tried your solution and it works.
However, I'm not a big fan of this solution. It means that I've to do this:
const ProductInformationWrapper = styled.div`
display: flex;
width: 25%;
padding: 0 0 0 ${PXToVW(64)};
flex: 0 0 auto;
flex-direction: column;
flex-wrap: nowrap;
background-color: ${props => props.theme.ProductDetail.ProductInformationWrapper.BackgroundColor};
`;
ProductInformationWrapper.defaultProps = {
theme: {
ProductDetail: {
ProductInformationWrapper: {
BackgroundColor: '#fff'
}
}
}
};
For EVERY SINGLE Styled Component in my application. That will just make the code huge, not maintainable and cluttered.
I really hope someone else has another (better) solution since this setup will be very hard to implement on big projects.
@JonShort I tried your solution but it doesn't work. Check out my reference above.
Your best bet would be to create a renderShallowWithTheme method, see the readme for more information: https://github.com/styled-components/jest-styled-components#theming
@mxstbr thanks for your response. If you read my Stack Overflow Question you will see that using shallow is not an option for me.
I need to use mount otherwise GSAP won't work and all my tests will fail.
Then just write a quick mountWithTheme method instead? :wink:
const mountWithTheme = (tree, theme) => {
const context = shallow(<ThemeProvider theme={theme} />)
.instance()
.getChildContext()
return mount(tree, { context })
}
const wrapper = mountWithTheme(<Button />, theme)
@mxstbr thanks again!
I tried your solution, this my test code now:
import React from 'react';
import Enzyme, { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'jest-styled-components';
import { ThemeProvider } from 'styled-components';
import LooksBrowser from './../src/app/components/LooksBrowser/LooksBrowser';
import LooksBrowserThemes from './../src/app/components/LooksBrowser/LooksBrowser.themes';
Enzyme.configure({ adapter: new Adapter() });
const mountWithTheme = (tree, theme) => {
const context = shallow(<ThemeProvider theme={theme} />)
.instance()
.getChildContext();
return mount(tree, { context });
};
test('Renders without crashing', () => {
mountWithTheme(<LooksBrowser currentSlide={{ imageURL: 'http://localhost:3001/img/D1_VW_SPW.jpg', index: 1, subtitle: 'Where amazing happens', title: 'The United States of America', textColor: '#fff' }} nextSlide={{ imageURL: '', index: 0, subtitle: '', title: '', textColor: '' }} />, LooksBrowserThemes.ThemeTH);
});
Test output:
TypeError: Cannot read property 'FontFamily' of undefined
49 | color: ${props => props.color};
> 50 | font-family: ${props => props.theme.LooksBrowser.SlideTitle.FontFamily};
54 | font-size: ${PXToVW(52)};
So I add a console.log inside of my mountWithTheme function:
const mountWithTheme = (tree, theme) => {
console.log(theme);
const context = shallow(<ThemeProvider theme={theme} />)
.instance()
.getChildContext();
return mount(tree, { context });
};
Output of this log:
console.log __tests__/LooksBrowser.react-test.js:13
{ LooksBrowser: {
SlideTitle: { FontFamily: 'Futura-Light, sans-serif' },
SlideSubtitle: { FontFamily: 'Futura-Demi, sans-serif' } }
}
And I also added this console.log to my SlideTitle Styled Component:
const SlideTitle = styled.p`
flex: 0 0 auto;
text-transform: uppercase;
line-height: 1;
color: ${props => props.color};
font-family: ${(props) => {
console.log(props.theme.LooksBrowser.SlideTitle.FontFamily);
return props.theme.LooksBrowser.SlideTitle.FontFamily;
}};
font-size: ${PXToVW(52)};
`;
Output of this log:
console.log src/app/components/LooksBrowser/LooksBrowser.js:51
Futura-Light, sans-serif
Then I scroll down and see this:
TypeError: Cannot read property 'FontFamily' of undefined
50 | font-family: ${props => {
> 51 | console.log(props.theme.LooksBrowser.SlideTitle.FontFamily);
52 | return props.theme.LooksBrowser.SlideTitle.FontFamily;
53 | }};
What?!
That looks like you have another test somewhere else which doesn't use the mountWithTheme util yet
@mxstbr I've figured it out with help from a colleague!
I had another Styled Component which extended the SlideTitle:
const SlideSubtitle = SlideTitle.extend`
font-family: ${props => props.theme.LooksBrowser.SlideSubtitle.FontFamily};
`;
When I removed this component my test passed. So I now refactored it to this:
const SlideTitlesSharedStyling = styled.p`
flex: 0 0 auto;
text-transform: uppercase;
line-height: 1;
color: ${props => props.color};
font-size: ${PXToVW(52)};
`;
const SlideTitle = SlideTitlesSharedStyling.extend`
font-family: ${props => props.theme.LooksBrowser.SlideTitle.FontFamily};
`;
const SlideSubtitle = SlideTitlesSharedStyling.extend`
font-family: ${props => props.theme.LooksBrowser.SlideSubtitle.FontFamily};
`;
And it works!
:tada: Awesome, glad to hear that! I'm going to assume this can be closed :blush:
getChildContext is the old deprecated react context. This solution does not work anymore nowadays.
I can confirm the current documented way given by @mxstbr doesn't work anymore because of the old Context API.
The easiest way I found to get this around was to use the wrappingComponent option.
import React from 'react'
import { ThemeProvider } from 'styled-components'
import { mount, shallow } from 'enzyme'
const themeMock = { /* your theme here */ }
const ThemeProviderWrapper = ({ children }) => (
<ThemeProvider theme={themeMock}>
{ children }
</ThemeProvider>
)
export const shallowWithTheme = tree => shallow(tree, {
wrappingComponent: ThemeProviderWrapper
})
export const mountWithTheme = tree => mount(tree, {
wrappingComponent: ThemeProviderWrapper
})
Are there any updates on best practices for jest/enzyme testing with styled-components? Specifically, is the last comment still the best solution to access a theme from Jest?
Most helpful comment
I can confirm the current documented way given by @mxstbr doesn't work anymore because of the old Context API.
The easiest way I found to get this around was to use the wrappingComponent option.