Jest-styled-components: v7.0.0 No Styles found on passed Component

Created on 15 Jan 2020  ·  23Comments  ·  Source: styled-components/jest-styled-components

I've just updated styled components to v5 and jest-styled-components to v7 and now any test that calls on toHaveStyleRule fails with the error No style rules found on passed Component.

Example test

  test("it renders correctly when disabled", () => {
    const { container } = render(<ControlButton disabled />);
    const button = container.querySelector("button");
    expect(button).toHaveStyleRule("opacity", "0.5");
  });

Tested component

const ControlButton = styled.button`
  background-color: white;
  border: none;
  font-size: 0;
  height: 20px;
  line-height: 0;
  margin: 0;
  padding: 0;
  user-select: none;
  width: 20px;

  ${props =>
    props.disabled &&
    css`
      opacity: 0.5;
      pointer-events: none;
    `}
`

This test passed using styled components v4.3.2 and jest-styled-components v6.3.3.

This is being tested with @testing-library/react v9.1.4

Most helpful comment

I got it working for me with the new namespace option of babel-plugin-styled-components, which enables us to pass the sc prefix by hand now. This is my babel config now:

module.exports = {
  // ...
  env: {
    test: {
      plugins: [
        [
          'babel-plugin-styled-components',
          { ssr: false, displayName: false, namespace: 'sc' },
        ],
      ],
    },
  },
};

All 23 comments

I'm not very familiar with the inner workings of SC or this package, but I think I may have tracked the issue down to here: https://github.com/styled-components/jest-styled-components/commit/8c2ea4a0a8789e11707e7f18e76b811e0d70c4c0#diff-4eed74593d3d8efde6a0959c9c35119bR71

In my case the static classes have names of the format "Component-sc-hashstuff", but the filter expects them to start with 'sc-', so none are found. Then due to the way the some() calls are nested, hasClassNames returns false when I don't think it should.

My issue with this boiled down to 'babel-plugin-styled-components' being used. That plugin modifies the class names to include the component, removing that fixed the issue. See https://github.com/styled-components/jest-styled-components/issues/290

We need the babel-plugin-styled-components plugin for server side rendering to have consistent hashing of classnames. Unless this is no longer needed in v5?

I'm having the same problem with enzyme.

I'm seeing the same issue even with displayName set to false during tests:

[
  `babel-plugin-styled-components`,
  { ssr: false, displayName: false },
]

Test:

describe(`example`, () => {
  const Example = styled.div`
    color: red;
  `

  it(`has style rule`, () => {
    const tree = renderer.create(<Example />).toJSON()
    expect(tree).toHaveStyleRule(`color`, `red`)
  })
})

Tree:

type: 'div',
props: { className: 'sc-AykKC kWTpbh' },
children: null

Message:

No Styles found on passed Component

  • react: 16.12.0
  • react-test-renderer: 16.12.0
  • styled-components: 5.0.0
  • jest-styled-components: 7.0.0

I'm having this same issue styled 4.4.1, and 6.3.4 of this lib, not sure. For now I'm rolling with a local patch-package that seems to fix things. Hoping the update to 7 here and styled 5 fixes.

I'll following this repo and lmk if I can help in any way. :D

We are having the same issue with styled-components 5.0.0 and jest-styled-components 7. To fix it we are currently using:

const plugins = [
  ['babel-plugin-styled-components', { ssr: !isTest, displayName: !isTest }],
];

This is a duplicate of #290

Having a similar issue when using:

    "jest": "^25.1.0",
    "jest-styled-components": "^7.0.0",
    "@testing-library/jest-dom": "^4.0.0",
    "@testing-library/react": "^9.1.1",
    "babel-plugin-styled-components": "^1.10.7",
    "styled-components": "^5.0.1",

And configuring babelrc:

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "styled-components",
      {
        "ssr": true,
        "preprocess": false,
        "displayName": true
      }
    ],
  ]
}

Test is:

    it('should be visible', () => {
      const { getByTestId } = render(
        ...
      );

      const cta = getByTestId('cta-id');
      fireEvent.click(cta);

      expect(getByTestId('content')).toMatchSnapshot();
      expect(getByTestId('content')).toHaveStyleRule('visibility', 'visible');
    });

The snapshot clearly shows that styles are there:

.c0 {
  -webkit-transform: translateY(0px);
  -ms-transform: translateY(0px);
  transform: translateY(0px);
  -webkit-transition: all 200ms cubic-bezier(0.175,0.885,0.32,1.275);
  transition: all 200ms cubic-bezier(0.175,0.885,0.32,1.275);
}

.c1 {
  background-color: #FFFFFF;
  border-radius: 0 0 4px 4px;
  box-shadow: 0 3px 7px 0 rgba(126,87,194,0.2);
  top: calc(100% + 0.5rem);
  position: absolute;
  padding: 0.5rem;
  min-width: 300px;
  right: 0;
  visibility: visible;
}

<div
  class="c0 c1"
  data-testid="content"
  y="0"
>
  Content Test
</div>
`;

But assertion says:
No style rules found on passed Component

This is still broken for me, even with:

const plugins = [
  ['babel-plugin-styled-components', { ssr: false, displayName: false }],
];

This is also preventing us from upgrading to v7.

I saw that 7.0.2 was released, I'm still having trouble getting it to work with Styled components v5.1.0 and react-testing-library. I think it does have something to do with the class name used and the regex code in jest-styled-components to parse it. It looks like the regex is looking for "sc-" but my component is outputting a different pattern. TextField__StyledLabel-ramey7-1

Here's the test:

const rendered = render(<TextField />)
...
const label = rendered.getByTestId('field-label')
expect(label).toMatchSnapshot()
expect(label).toHaveStyleRule('width', '(100% / 0.75)')

here is a debug() output of the component:

  console.log node_modules/@testing-library/react/dist/pure.js:94
    <span
      class="TextField__StyledLabel-ramey7-1 jQMSNG"
      data-test-id="field-label"
    />

When I output the snapshot I see the style rule does exist.

exports[`TextField removes active state on blur 1`] = `
.c0 {
  padding: 0.5rem 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
  position: absolute;
  top: 1rem;
  -webkit-transform-origin: 0 0;
  -ms-transform-origin: 0 0;
  transform-origin: 0 0;
  -webkit-transition: color 0.4s cubic-bezier(0.25,0.8,0.25,1),-webkit-transform 0.4s cubic-bezier(0.25,0.8,0.25,1);
  -webkit-transition: color 0.4s cubic-bezier(0.25,0.8,0.25,1),transform 0.4s cubic-bezier(0.25,0.8,0.25,1);
  transition: color 0.4s cubic-bezier(0.25,0.8,0.25,1),transform 0.4s cubic-bezier(0.25,0.8,0.25,1);
  width: 100%;
  -webkit-transform: translateY(-50%) scale(0.75);
  -ms-transform: translateY(-50%) scale(0.75);
  transform: translateY(-50%) scale(0.75);    
  width: (100% / 0.75);  <-- style testing for
}

Test output shows:

● TextField › removes active state on blur

    No style rules found on passed Component

      159 |     // input has focus
    > 161 |     expect(label).toHaveStyleRule('width', '(100% / 0.75)')
          |                   ^

Dependencies:

    "@testing-library/jest-dom": "5.5.0",
    "@testing-library/react": "10.0.2",
    "babel-plugin-styled-components": "1.10.7",
    "jest-styled-components": "7.0.2",
    "styled-components": "5.1.0",

@fullstackzach having the same issue here

Adding to this: I'm seeing the same issue on the toHaveStyleRule() assertion. A little debugging appears to show some components loading the stylesheet via the getHTML() function via this function in utils.js (https://github.com/styled-components/jest-styled-components/blob/master/src/utils.js#L17)
image

and other components are not
image

masterSheet is a module imported via __PRIVATE__ (https://github.com/styled-components/jest-styled-components/blob/master/src/utils.js#L2) when styled-components is imported. I can't wrap my head around how these modules are constructed in styled-components, so this is where my trail goes cold. Should I reach out to the styled components team about this?

Probably similar?

Bug.test.tsx

import styled from "styled-components";
import React from "react";
import {mount} from "enzyme";

import 'jest-styled-components';

const Foo = () => (
  <div>Bar</div>
);

const StyledFoo = styled(Foo)`
  color: mediumspringgreen;
`;

describe('StyledFoo', () => {
  it('has a mediumspringgreen color', () => {
    const styledFoo = mount(<StyledFoo />);
    expect(styledFoo).toHaveStyleRule('color', 'mediumspringgreen');
  });
});

Snapshot:

exports[`StyledFoo has a mediumspringgreen color 1`] = `
.c0 {
  color: mediumspringgreen;
}

<Styled(Foo)>
  <Foo
    className="c0"    <<<<  it's there
  >
    <div>
      Bar
    </div>
  </Foo>
</Styled(Foo)>
`;

But I get No style rules found on passed Component.

However, if I introduce className, it works:

const Foo = ({className}: {className?: string}) => (
  <div className={className}>Bar</div>
);

Snapshot:

exports[`StyledFoo has a mediumspringgreen color 1`] = `
.c0 {
  color: mediumspringgreen;
}

<Styled(Foo)>
  <Foo
    className="c0"
  >
    <div
      className="c0"
    >
      Bar
    </div>
  </Foo>
</Styled(Foo)>
`;

Is this expected?

So... I had similar issue and I have already pointed out the problem in this comment
https://github.com/styled-components/jest-styled-components/issues/297#issuecomment-639362675

In short the algorithm is built so that you HAVE TO have the sc- prefix when testing (ssr: true), but in my case (for some strange unknown reason) not all my components class names get the prefix, even with ssr: true, so I am stuck with upgrading until this is fixed.

I have already proposed to fix it, but as I am not sure how the procedure work I won't start any correction before I get a green light to do so.

I got it working for me with the new namespace option of babel-plugin-styled-components, which enables us to pass the sc prefix by hand now. This is my babel config now:

module.exports = {
  // ...
  env: {
    test: {
      plugins: [
        [
          'babel-plugin-styled-components',
          { ssr: false, displayName: false, namespace: 'sc' },
        ],
      ],
    },
  },
};

Simply adding the package react-test-renderer as a dependency fixed this issue for me:

yarn add react-test-renderer --dev
npm install react-test-renderer --save-dev

# for ts projects
yarn add @types/react-test-renderer --dev
npm install @types/react-test-renderer --save-dev

Also, here's my babel config:

{
  "presets": ["next/babel"],
  "plugins": [["styled-components", { "ssr": true }]]
}

Simply adding the package react-test-renderer as a dependency fixed this issue for me:

@oguzgelal , react-test-renderer installed, but problem still exists.

jest-styled-components checking is hardcoded.

const isStyledClass = className =>
  /^(\w+(-|_))?sc-/.test(className);

styles should masker with "sc-".

Snapshot from another my project with working:

<div
  class="indextest__DivStyled-sc-1wt13as-0 hViIRb"
>
  Text 
</div>

"sc-" in classname exists.

But on broken project not exists:

<div
  class="indextest__DivStyled-p00oe-0 jAwXaO"
>
  Text 
</div>

To fix i have to add namespace to babel config.

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "styled-components",
      {
        // In some projects missed namespace for styled-components
        // По какой-то причине в styled пустой неймспейс и в тестах не проходит маска
        // /^(\w+(-|_))?sc-/.test(className)
        "namespace": "sc-",
        "ssr": true
      }
    ]
  ]
}

i got the same problem. i saw that there where multiple versions of styled components included (v5.1 and v5.2).

removing v5.2 did the trick for me

I got it working for me with the new namespace option of babel-plugin-styled-components, which enables us to pass the sc prefix by hand now. This is my babel config now:

module.exports = {
  // ...
  env: {
    test: {
      plugins: [
        [
          'babel-plugin-styled-components',
          { ssr: false, displayName: false, namespace: 'sc' },
        ],
      ],
    },
  },
};

This worked for me! Using nx.dev

Using Jest testEnvironment "node" seemed to be one of the issues for me. When I change it back to the default "jsdom", the issue went away.

Maybe another slight piece to _someone's_ puzzle. I've had to solve this a half dozen times and this is yet another scenario for me.

Both specificity and the modifier option are very key. If you have your babel-plugin-styled-components configured correctly, you can still easily encounter this issue.

:x: Broken No style rules found on passed Component:

import styled from 'styled-components';
import { mount } from 'enzyme';

const Checkbox = styled.input`
  input[type='checkbox']& {
    height: 20px;
  }
`;

test('fails', () => {
  expect(mount(<Checkbox />)).toHaveStyleRule('height', '20px');
});

test('fails w/ modifier', () => {
  expect(mount(<Checkbox />)).toHaveStyleRule('height', '20px', {
    modifier: "input[type='checkbox']",   // no combination of this worked here like it does in other tests…
  });
});

🎉 I got it passing by changing the component itself, adding &, to the specificity. This would be breaking in other places, but in here it works 🤞…

import styled from 'styled-components';
import { mount } from 'enzyme';

const Checkbox = styled.input`
  &,
  input[type='checkbox']& {
    height: 20px;
  }
`;

test('passes', () => {
  expect(mount(<Checkbox />)).toHaveStyleRule('height', '20px');
});

I hope no one's really using this specific pattern scenario, but it might highlight specificity issues to people debugging.

Was this page helpful?
0 / 5 - 0 ratings