Jest-styled-components: Can't find element in Shallow render

Created on 30 Nov 2017  路  2Comments  路  Source: styled-components/jest-styled-components

Follow up on this (https://twitter.com/MicheleBertoli/status/936196097218883584) twitter discussion. I've got an issue with styled components not rendering my test, or not able to find elements.

I've got the following test:

import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import ShallowRenderer from 'react-test-renderer/shallow';
import renderer from 'react-test-renderer';
import 'jest-styled-components';

import { SelectProjects } from '../src/app/components/modules/SelectProjects';

describe('<SelectProjects /> - unit test', () => {
    it('should render dumb component with projects', () => {
        const projects = {
            loading: false,
            data: [
                { id: 1, name: 'Pack' },
                { id: 2, name: 'Portal' },
                { id: 3, name: 'Zorgboodschap' },
            ],
        };

        const props = {
            projects,
            history: {},
            getProjects: jest.fn(),
            projectsConfirmSelected: jest.fn(),
        };

        // Examples of https://gist.github.com/MicheleBertoli/4210aecb837b4048853c1481a6e1177f
        // First try (second example)
        const shallowRenderer = new ShallowRenderer();
        shallowRenderer.render(<SelectProjects {...props} />);
        const tree = shallowRenderer.getRenderOutput();

        const rendererInput = shallowRenderer.find('input');
        console.log('input', rendererInput);
        const secondInput = tree.find('Input');
        console.log('Input', secondInput);
        // @ERROR TypeError: shallowRenderer.find is not a function

        const treeInput = tree.find('input');
        console.log('input', treeInput);
        const treeInputEl = tree.find('Input');
        console.log('Input', treeInputEl);
        // @ERROR TypeError: tree.find is not a function

        // Second try (first example)
        const wrapper = shallow(<SelectProjects {...props} />).dive();
        expect(toJson(wrapper)).toMatchSnapshot();

        const input = wrapper.find('input');
        console.log('input', input);
        const inputStyled = wrapper.find('Input');
        console.log('Input', inputStyled);

        /* @OUTPUT
        $ console.log __test__/SelectProjects.spec.js:96
        $ input ShallowWrapper { length: 0 }

        $ console.log __test__/SelectProjects.spec.js:98
        $ Input ShallowWrapper { length: 0 } */

        // Another try (not from examples)
        const anotherTree = renderer.create(<SelectProjects {...props} />).toJSON();

        const anotherTreeInput = anotherTree.find('input');
        console.log('input', anotherTreeInput);
        // @ERROR  TypeError: tree.find is not a function
    });
});

This doens't work as you can see above in my comments. I think this has to do with not good rendered styled components. My snapshot is as following:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<SelectProjects /> - unit test should render dumb component with projects 1`] = `
.c0 {
  max-width: 100vw;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  -ms-flex-pack: center;
  justify-content: center;
}

<section
  className="c0"
>
  <styled.form
    onSubmit={[Function]}
  >
    <Title>
      Selecteer de gewenste projecten
    </Title>
    <styled.input
      name="search"
      onChange={[Function]}
      placeholder="Zoek naar een specifiek project.."
      type="text"
    />
    <styled.div
      active={false}
      key="1"
      onClick={[Function]}
    >
      <styled.span
        active={false}
      />
      Pack
    </styled.div>
    <styled.div
      active={false}
      key="2"
      onClick={[Function]}
    >
      <styled.span
        active={false}
      />
      Portal
    </styled.div>
    <styled.div
      active={false}
      key="3"
      onClick={[Function]}
    >
      <styled.span
        active={false}
      />
      Zorgboodschap
    </styled.div>
    <styled.button
      type="submit"
    >
      Bevestig 
       projecten
    </styled.button>
  </styled.form>
</section>
`;

I do have the following related packages installed:

"react": "^16.2.0",
"styled-components": "^2.2.3",
"babel-jest": "^21.2.0",
"babel-plugin-styled-components": "^1.3.0",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"jest": "^21.2.1",
"jest-styled-components": "^4.9.0",
"react-test-renderer": "^16.2.0",

My Jest config is as below:

"jest": {
    "verbose": true,
    "transform": {
      ".*": "./__test__/__mock__/jestPreprocessor.js"
    },
    "moduleDirectories": [
      "node_modules",
      "src/app",
      "src/app/components"
    ],
    "moduleNameMapper": {
      "^-!svg-react-loader.*$": "./__test__/__mock__/fileMock.js"
    },
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "setupFiles": [
      "./__test__/__settings__/shim.js",
      "./__test__/__settings__/setup.js"
    ]
  },

setup.js

import Enzyme, { shallow, render, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });

// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;

shim.js

global.requestAnimationFrame = (callback) => {
    setTimeout(callback, 0);
};

jestPreprocessor.js

const babelJest = require('babel-jest');
const includeStylesSvg = new RegExp(/require\(\s*\'.*\.(css|svg)\'\);/gm);
const storeStylesSvg = new RegExp(/= require\(\s*\'.*\.(css|svg)\'\);/gm);

module.exports = {
    process: (src, filename) => {
        return babelJest
            .process(src, filename)
            .replace(storeStylesSvg, '= \'\';')
            .replace(includeStylesSvg, '');
        }
};

Hope this will help.
Thanks in advance for trying to solve my issue.

Most helpful comment

Oh, now I see the "problem" @Ronnyrr.
I didn't get that having styled.* was causing you an issue in finding components, sorry.
Thank you very much for opening the issue.

Using displayName seems a correct solution, but what I usually do is just referring to the component itself (see last expectation in the following example):

test('shallow', () => {
  const Input = styled.input``
  const Container = styled.div``
  const wrapper = shallow(
    <Container>
      <Input />
    </Container>
  )

  expect(wrapper).toMatchSnapshot()
  expect(wrapper.find('input')).toHaveLength(0)
  expect(wrapper.find(Input)).toHaveLength(1)
})

I hope this helps!

All 2 comments

Seems to be "fixed" using Input.displayName = 'Input'; as mentoined in the last comment from https://github.com/styled-components/styled-components/issues/896

Is this a proper fix and expected behavior?

Oh, now I see the "problem" @Ronnyrr.
I didn't get that having styled.* was causing you an issue in finding components, sorry.
Thank you very much for opening the issue.

Using displayName seems a correct solution, but what I usually do is just referring to the component itself (see last expectation in the following example):

test('shallow', () => {
  const Input = styled.input``
  const Container = styled.div``
  const wrapper = shallow(
    <Container>
      <Input />
    </Container>
  )

  expect(wrapper).toMatchSnapshot()
  expect(wrapper.find('input')).toHaveLength(0)
  expect(wrapper.find(Input)).toHaveLength(1)
})

I hope this helps!

Was this page helpful?
0 / 5 - 0 ratings