I havn't been able to find any documentation on using toHaveStyleRule for checking properties on nested classes.
Let's say I have a stylesheet for a styled component that looks like this:
export default styled.div`
position: relative;
line-height: 24px;
width: ${props => props.width};
height: 72px;
label {
color: ${props => {
if (!props.hasValue && !props.meta.active) {
return props.theme.lightGrey
}
if (props.meta.touched && props.meta.error) {
return props.theme.errorColor
}
if (props.meta.touched && props.meta.warning) {
return props.theme.warningColor
}
return props.theme.brandColor
}};
}
.placeholder {
color: ${props => props.theme.lightGrey};
}
`
How would I check for a style rule on label or .placeholder?
Interesting question, thanks @duro.
At the moment it's not possible, but it would be a very interesting addition.
cc @yanawaro
@duro This could be possible with a little more modification of using modifiers.
(Awaiting merge on https://github.com/styled-components/jest-styled-components/pull/60 馃槀 )
@MicheleBertoli I experimented and could get this test passed based on PR https://github.com/styled-components/jest-styled-components/pull/60 code
test('nested', () => {
const Wrapper = styled.div`
margin: auto;
label {
color: black;
${props => props.green && 'color: green;'}
}
.flex {
display: flex;
}
`
toHaveStyleRule(<Wrapper />, 'color', 'black', { modifier: ' label' })
toHaveStyleRule(<Wrapper green />, 'color', 'green', { modifier: ' label' })
toHaveStyleRule(<Wrapper green />, 'display', 'flex', { modifier: ' .flex' })
})
When using nested selectors, the rules classnames has a gap like
[ '.lhCfhI label' ] [ '.hLYBzJ .flex' ] so the modifier value follows that gap.
If not adding too much complexity, I think the selectors that finds the classnames to match in hasClassNames could simply remove the gaps for matching?
Also, if this feature is added, should this option be called nested innerSelector 馃槀 ? or use the same modifier so the code doesn't have more branches (I'm not sure if make sense to users) ?
Also this might get closer to testing styles when referring to other components, I've been experimenting something like
test('contexted', () => {
const Item = styled.li`padding: 10px 20px;`
const List = styled.ul`
margin: auto;
${Item} {padding: 8px 16px;}
`
const Component = mount(
<List>
<Item />
<Item />
</List>
)
toHaveStyleRule(Component.find(Item), 'padding', '8px 16px')
})
It's just need to modify the test file to handle more complex mounts.
You are awesome @yanawaro, thanks!
As you know I love our minimalistic approach, but it seems wrong to me to ask developers to include the space in the "modifier". I'd rather implement little bit complex solution which can find the matches applying an optional space.
A nested or child option would work but I also love your last example where you pass the child to expect and then search for the style within the parent.
Any update on this @yanawaro?
I know you are super busy but it'd be great if you could push some code, so I might help you complete the feature.
Thank you very much in advance.
const ParagraphStyled = styled(Text)`
color: #fff;
`;
const wrapper = shallow(<ParagraphStyled />);
expect(wrapper).toHaveStyleRule('color', '#fff');
When i try to test
const ParagraphStyled = styled.peverything will be ok. Is it possible to test const ParagraphStyled = styled(Text) ?
Hello @alex-shatalov, you test seems to pass.
Which version of the package are you using?
Thank you very much!
"jest-enzyme": "^4.0.1",
"enzyme": "^3.2.0",
"jest-styled-components": "^4.9.0"
@MicheleBertoli , my bad, i look again to my ParagraphStyled component and it is triple nested.
const ParagraphStyled = styled(Paragraph)
const Paragraph = styled(Text)
const Text= styled.p
If it runs like double you are right tests pass. But on triple same error.
I see @alex-shatalov, thanks for providing more information.
So the confusion comes from the way shallow rendering works with Styled Components.
If you run console.log(wrapper.debug()), you will see the following component:
<Styled(styled.p) className="sc-htpNat fNIFfJ" innerRef={[undefined]} />
which is not the one you are looking for.
What you should do instead is using the Enzyme's dive function, to go one level deeper.
So, If you change your test to (please note wrapper.dive() in the expectation):
test('dive', () => {
const Text = styled.p``
const Paragraph = styled(Text)``
const ParagraphStyled = styled(Paragraph)`
color: #fff;
`
const wrapper = shallow(<ParagraphStyled />)
expect(wrapper.dive()).toHaveStyleRule('color', '#fff')
})
It finally works.
I hope this helps.
@MicheleBertoli thanks a lot, you helps me!
But i have another strange situation
My components is being exporting from files. And all of them have style color.
So i try to change your working example of test('dive', () just like this.
test('dive', () => {
const Text = styled.p`
color: green;
`
const Paragraph = styled(Text)`
color: blue;
`
const ParagraphStyled = styled(Paragraph)`
color: #fff;
`
const wrapper = shallow(<ParagraphStyled />)
expect(wrapper.dive()).toHaveStyleRule('color', '#fff')
})
And yes it works. Test Suites: 1 passed, 1 total;
But when i take out component Text to another file and import it.
Text file looks like this
export const TextAnother = styled.p`
color: blue;
`;
and test suite is now like this
import { TextAnother } from '../../atoms/Text';
test('diveText', () => {
const Paragraph = styled(TextAnother)`
color: blue;
`
const ParagraphStyled = styled(Paragraph)`
color: #fff;
`
const wrapper = shallow(<ParagraphStyled />)
expect(wrapper.dive()).toHaveStyleRule('color', '#fff')
})
Tests fails.
Expected color to match:
"#fff"
Received:
"blue"
The property color is from component Text but not from ParagraphStyled
So i look in to file toHaveStyleRule and found that test will pass only if i change isTagWithClassName and remove node type checking
const isTagWithClassName = node => node.prop('className');
and remove dive() in test
Hello @alex-shatalov, I'm glad to hear the first solution was helpful.
I tried your code but the second test (which imports TextAnother) works for me.
Could you please check and provide more information?
Thank you very much!
Hi @MicheleBertoli can we reopen this? I use a lot of @yanawaro's 2nd example where I style nested subcomponents. Will be happy to try to get a PR out for it soon unless @yanawaro already has something
Yep please @Joelkang - I would love to get this done.
I'm looking forward to receiving your PR.
Thank you very much!
@MicheleBertoli PR rendered; we should probably discuss how to handle nested styled components, but I think nested element/class/attribute selectors should be handled properly
This is awesome, @Joelkang.
Thank you very much!
Most helpful comment
@duro This could be possible with a little more modification of using modifiers.
(Awaiting merge on https://github.com/styled-components/jest-styled-components/pull/60 馃槀 )
@MicheleBertoli I experimented and could get this test passed based on PR https://github.com/styled-components/jest-styled-components/pull/60 code
When using nested selectors, the rules classnames has a gap like
[ '.lhCfhI label' ][ '.hLYBzJ .flex' ]so the modifier value follows that gap.If not adding too much complexity, I think the
selectorsthat finds the classnames to match inhasClassNamescould simply remove the gaps for matching?Also, if this feature is added, should this option be called
nestedinnerSelector馃槀 ? or use the samemodifierso the code doesn't have more branches (I'm not sure if make sense to users) ?Also this might get closer to testing styles when referring to other components, I've been experimenting something like
It's just need to modify the test file to handle more complex mounts.