Jest-styled-components: Styles not found on a component (toHaveStyleRule)

Created on 16 Aug 2018  路  32Comments  路  Source: styled-components/jest-styled-components

With the latest enzyme (3.4.1) and jest-styled-components (6.0.0), the toHaveStyleRule assertion is not finding the styles on a component.

Symptoms:

    No style rules found on passed Component

      28 |         .dive()
      29 |         .dive()
    > 30 |     ).toHaveStyleRule('text-align', 'center')
      31 |   })
      32 |

Also visible in snapshots. Before, the snapshot included the style classes definition, e.g.:

.c0 {
  padding-left: 0;
 ...
  display: table-cell;
}

With the latest enzyme, in the snapshot, the className is still there on a component (twice?), but the class definition is missing:

<MyComponent
  centre={false}
  className="c0"
  className="... obnHq"
no repro

Most helpful comment

I think I found the pattern that breaks after updating to version 6. It occurs when the styled component is (shallow) rendered by another component (which is the one being tested). Here's an example:

MyComponent.js

import React from 'react';
import styled from 'styled-components';

const Card = styled.div`
  opacity: 1;
`;

const MyComponent = () => (
  <Card></Card>
);

export default MyComponent;

MyComponent.test.js

import React from 'react';
import { shallow } from 'enzyme';

import MyComponent from '../MyComponent';

describe('<MyComponent />', () => {
  it('renders with style rule', () => {
    const wrapper = shallow(<MyComponent />);
    console.log(wrapper.debug());
    expect(wrapper.find('MyComponent__Card')).toHaveStyleRule('opacity', '1');
  });
});

Output

  No style rules found on passed Component

       8 |     const wrapper = shallow(<MyComponent />);
       9 |     console.log(wrapper.debug());
    > 10 |     expect(wrapper.find('MyComponent__Card')).toHaveStyleRule('opacity', '1');
         |                                               ^
      11 |   });
      12 | });
      13 | 

All 32 comments

Hello @msbober, thanks for opening this issue.
The tests are currently passing with enzyme 3.1.4, would you mind providing more information (e.g. the component and the test implementation)?

Fwiw, I'm experiencing the same issue, but cannot provide much debugging information at this time.

[email protected]
[email protected]

Same here, snapshot's okay, but no styles found using toHaveStyleRule, happening since v6.

[email protected]
[email protected]

Thank you very much, @wesrice @oreqizer.

Unfortunately, without a non-working example and the tests that are passing is super hard for me to investigate this issue.
Would you be able to provide some code?

I'm running into the issue as well and have been trying to setup a codesandbox with a repro. Can't get the sandbox running with jest + enzyme though. Do you have a codesandbox that I could use as a starter somewhere?

Hey @thomas88, thanks for your comment.

Codesandbox helps but it's not require, if you could paste the code (of the component and the failing test) here, I will be happy to run it locally.

At some point the problem just stopped happening for us (enzyme聽(3.4.1) and聽jest-styled-components聽(4.9.0)) without any apparent reason.

Currently we think that in our case it was some weird yarn caching problem. We'll keep an eye for it.

I'd close the issue but it looks like there are others observing the same behaviour.

I think I found the pattern that breaks after updating to version 6. It occurs when the styled component is (shallow) rendered by another component (which is the one being tested). Here's an example:

MyComponent.js

import React from 'react';
import styled from 'styled-components';

const Card = styled.div`
  opacity: 1;
`;

const MyComponent = () => (
  <Card></Card>
);

export default MyComponent;

MyComponent.test.js

import React from 'react';
import { shallow } from 'enzyme';

import MyComponent from '../MyComponent';

describe('<MyComponent />', () => {
  it('renders with style rule', () => {
    const wrapper = shallow(<MyComponent />);
    console.log(wrapper.debug());
    expect(wrapper.find('MyComponent__Card')).toHaveStyleRule('opacity', '1');
  });
});

Output

  No style rules found on passed Component

       8 |     const wrapper = shallow(<MyComponent />);
       9 |     console.log(wrapper.debug());
    > 10 |     expect(wrapper.find('MyComponent__Card')).toHaveStyleRule('opacity', '1');
         |                                               ^
      11 |   });
      12 | });
      13 | 

I am experiencing this issue with the same scenario that @thomas88 is describing.

This is awesome, thanks @thomas88 @wesrice.
I found the regression, and fixed it in v6.1.1.

@thomas88 you should run the expectation on wrapper, as opposed to wrapper.find('MyComponent__Card'), because the latter doesn't exist:

describe('<MyComponent />', () => {
  it('renders with style rule', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper).toHaveStyleRule('opacity', '1');
  });
});

How awesome is that. Thanks for the fast fix @MicheleBertoli! All tests are working again.

@MicheleBertoli

I wasn't sure if I should open another issue or comment here. I'm experiencing the same issue as described previously - No style rules found on passed Component.

Button.styles.js

Here is the styled-component I would like to test.

import styled from 'styled-components'
import { hover } from '../../../styles/_utility/'

export default styled.button`
  cursor: pointer;
  outline: none;
  border: none;
  border-radius: 3px;
  font-size: 14px;
  margin: 0 10px;
  padding: 8px 18px;
  color: ${props => {
    if (props.variant === 'main' && props.color !== 'white') {
      return props.theme.white.main
    } else if (props.color !== 'white') {
      return props.theme[props.color].main
    } else {
      return props.theme.dark.main
    }
  }};
  background-color: ${props => props.theme[props.color][props.variant]};
  ${props => props.hover ? hover(props.selected) : ''}
`

Button.test.js

Here is the test I've written:

/* eslint-env jest */
import React from 'react'
import { shallow } from 'enzyme'
import toJson from 'enzyme-to-json'
import ButtonContainer from './Button.styles'
import theme from '../../../styles/theme'
import 'jest-styled-components'

describe('Button styled-components', () => {
  it('should render a ButtonContainer', () => {
    const wrapper = shallow(<ButtonContainer color='primary' variant='lighter' theme={theme} />)
    expect(wrapper).toHaveStyleRule('color', '#FFD5B7')
  })
})

I've tried it with the wrapper and also the wrapper as JSON with

const tree = toJson(wrapper)
expect(tree).toHaveStyleRule('border', 'none')

Versions

"react": "latest"
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.4",
"jest-styled-components": "^6.2.0"

If I can get this to work, I will happily make a video on egghead to share with the community 鉂わ笍

Thank you in advance for your time and help!

Hello @jsjoeio, thanks for your comment.
I tried to run your code and it worked.

screen shot 2018-09-22 at 8 06 33 am

Would you be able to provide a non-working repo or sandbox?
Cheers!

@MicheleBertoli Strange...

Here's a non-working repo: https://github.com/jsjoeio/styles-not-found/tree/master

Thanks for setting up a repo, @jsjoeio.
So the problem is that you are using Styled Components v4.0.0-beta.4-hmr, which is not supported yet by this library.

Ahhh - that makes sense now. My apologies @MicheleBertoli - I should have checked if v4 was supported before commenting on this issue. Thanks for helping figure out the problem! Really appreciate your help!

In case anyone comes here because they have the same issue, I hit this from having two versions of styled-components in my node_modules (Lerna multimodule with a yarn workspace - there was a hoisted version and a version local to one module).

Thank you @lexanth! I was having the same issue.

Having this issue as well, this is my component:

interface StyledProps {
  fluid: boolean;
}

const Button = styled(TouchableOpacity)<StyledProps>`
  align-items: center;
  justify-content: center;
  ${({ fluid }: StyledProps) => fluid && { width: '100%' }};
`;

const ButtonText = (styled as any).Text({
  color: #fff,
  fontSize: 16,
});

const GradientButton = ({ testID, onPress, title, fluid = false }: GradientButtonProps) => {
  return (
    <Button fluid={fluid} {...testLabel(testID)} onPress={onPress}>
      <ButtonText>{title}</ButtonText>
    </Button>
  );
};

export default GradientButton;

And here is the test:

let wrapper: ShallowWrapper;
const component = <GradientButton testID={TEST_ID} title={TITLE} onPress={onPressSpy} />;
beforeEach(() => {
  wrapper = shallow(component);
});

describe('<GradientButton />', () => {
  it('renders fluid', () => {
    expect(wrapper).toHaveStyleRule('width', '100%');
  });
});

Which gives me: No style rules found on passed Component

package.json:

    "react": "16.5.1",
    "styled-components": "^3.4.10",
    "react-native": "0.57.1",
    "jest-styled-components": "^6.2.2",

Thanks for your comment, @jmacioszek.

The test says "No style rules found on passed Component" because you are shallow rendering and the children components don't get resolved. If you use mount, the tests will work.
However, they would still fail because you need to see fluid={true}.

I hope this helps.

For anyone else running into this, my issue was styled-components not being hoisted in my monorepo.

I had dev and peer dependencies misconfigured in a couple packages, so there were two versions being referenced depending what package I was importing from. Check your node_modules folders in each package.

You should only see one copy of styled components hoisted to the root node_modules of of your monorepo.

Thanks for your comment, @jmacioszek.

The test says "No style rules found on passed Component" because you are shallow rendering and the children components don't get resolved. If you use mount, the tests will work.
However, they would still fail because you need to see fluid={true}.

I hope this helps.

I changed rendering method shallow to mount and it worked. Thanks.

I'm running into the same issue. Not exactly sure what I'm doing wrong here, but here are my files:

import React from 'react';
import styled from 'styled-components';

const Home = styled.div`
    .desktop {
        color: red;
        display: block;
    }
`;

const HomeView = function() {
    return (
        <Home className="home">
            <div className="desktop">The desktop home page!</div>
        </Home>
    );
};

export default HomeView;

Spec File:

import React from 'react';
import { mount } from 'enzyme';
import { HomeView } from './';
import 'jest-styled-components';

describe('Examples', () => {
    it('should check the styles', () => {
        const wrapper = mount(<HomeView />);
        const element = wrapper.find('.desktop');

        expect(element).toHaveStyleRule('color', 'red');
    });
});

In the end I'm getting the error No style rules found on passed Component. Any help would be appreciated.

"enzyme": "3.8.0",
"jest": "24.5.0",
"jest-styled-components": "6.3.1",
"react": "16.7.0",
"react-dom": "16.7.0",
"styled-components": "4.1.3"

@goodbomb

const Home = styled.div .desktop { color: red; display: block; } ;

There's no styling for Home component. Error is normal. I think, if you have another styled component, just write like this and write a test for that component:

const Home =  styled.div`
    Home Sytling...
 `;

const Desktop = styled.div`
       color: red;
       display: block;
`;


<Home className="home">
    <Desktop>The desktop home page!</div>
 </Home>

@Soul244 how does one access the styles for nested elements though?

@goodbomb i updated my answer.

Damn, so you can't use the cascading styles aspect of Styled-Components? That's one of the big benefits I see for using it. You only need to style the top-level component, and then use normal cascading CSS selectors for the child elements. Having to create new components for every styled element adds a lot of verbosity to the styles and the file itself.

If you need a general component which created with styled component, just write that in a file and export it. I am not sure, what you need exactly?

A hover example:

const Home =  styled.div`
    Home Sytling...
 `;

const Desktop = styled.div`
 ${Home}:hover &(
       color: red;
       display: block;
)
`;
<Home className="home">
    <Desktop>The desktop home page!</div>
 </Home>

I am sure, Styled Components is ten times more better than css. You can use all css selectors and other things in Styled Components. Docs: https://www.styled-components.com/docs

this is my mail address: [email protected]. I can help you, if you have a problem about styled-components.

We're using the Cascade approach to styling. So instead of creating a new component for each element we want to style (like you're doing in your examples), we're using cascade selectors (like you do in normal CSS files).

Instead of this:

const Home =  styled.div`
    ...Home styles...
`;

const Desktop = styled.div`
    ...Desktop styles...
`;

<Home className="home">
    <Desktop>The desktop home page!</div>
 </Home>

We're doing this:

const Home =  styled.div`
    ...Home styles...

    .desktop {
        ...desktop styles...
    }
`;

<Home className="home">
    <div className="desktop">The desktop home page!</div>
 </Home>

Our approach makes for less verbosity and is much closer to normal CSS. I take it this library doesn't support that approach, @Soul244 ?

No, you can use your approach. I tried and couldn't find a problem. Could you write result of this code?

const element = wrapper.find('.desktop');
console.log(element)

Probably, toHaveStyleRule function is just working for styled components. You need to test it with another function. Maybe this: "toHaveProperty".

I did this:

const element = wrapper.find('.desktop');
console.log(element)

Output only gives me this: ReactWrapper {}

Changing to console.log(element.debug()); gives this:

console.log src/app/home/HomeView.spec.js:14
    <div className="desktop">
      The desktop home page!
    </div>

I can't help you about this problem, sorry.

But i found what is your problem with your approach. Styled Components creates a unique class name for every component to prevent css problems with other html elements. But when you create a component like you do, it's creating a normal css class name. For example:

const Home =  styled.div`
    ...Home styles...

    .desktop {
        ...desktop styles...
    }
`;

<Home className="home">
    <div className="desktop">The desktop home page!</div>
 </Home>

in this code, Home has a unique class name. You can see the name on inspect. But child component class name is not unique. That is the problem. So you need to create new styled component for every element.

Was this page helpful?
0 / 5 - 0 ratings