styled-components: 3.2x Breaking Jest/Enzyme tests of components that mount elements using unstable_RenderSubtreeIntoContainer

Jest/enzyme tests that were passing until 3.2x release are now failing once 3.2x is installed. Reverting to 3.1.6 solves the issue.

Non-passing Environment

System:

  • OS: macOS High Sierra 10.13.3
  • CPU: x64 Intel® Core™ i7-4770HQ CPU @ 2.20GHz
  • Free Memory: 1.41 GB
  • Total Memory: 16.00 GB
  • Shell: /bin/zsh - undefined

Binaries:

  • Node: 7.10.0
  • Yarn: 0.23.3
  • npm: 4.2.0
  • Watchman: 4.7.0

npmPackages:

babel-jest:

  • wanted: ^20.0.3
  • installed: 20.0.3

babel-plugin-styled-components:

  • wanted: ^1.3.0
  • installed: 1.5.1

enzyme:

  • wanted: ^2.7.1
  • installed: 2.9.1

jest:

  • wanted: ^20.0.4
  • installed: 20.0.4

styled-components:

  • wanted: ^3.0.0
  • installed: 3.2.2

Passing Environment

System:

  • OS: macOS High Sierra 10.13.3
  • CPU: x64 Intel® Core™ i7-4770HQ CPU @ 2.20GHz
  • Free Memory: 1.47 GB
  • Total Memory: 16.00 GB
  • Shell: /bin/zsh - undefined

Binaries:

  • Node: 7.10.0
  • Yarn: 0.23.3
  • npm: 4.2.0
  • Watchman: 4.7.0

npmPackages:

babel-jest:

  • wanted: ^20.0.3
  • installed: 20.0.3

babel-plugin-styled-components:

  • wanted: ^1.3.0
  • installed: 1.5.1

enzyme:

  • wanted: ^2.7.1
  • installed: 2.9.1

jest:

  • wanted: ^20.0.4
  • installed: 20.0.4

styled-components:

  • wanted: 3.1.6
  • installed: 3.1.6

Reproduction

I am unable to share the code in which I found this behavior, but can create a repo to repro if needed.

Steps to reproduce

Using React 15x, create a component that has a child that is rendered via unstable_renderSubtreeIntoContainer, write test using enzyme’s mount. Tests pass with version 3.1.6 but fail under version(s) 3.2x.

Expected Behavior

Tests don’t break due to warning from styled-components that DOM node mounted with unstable_renderSubtreeIntoContainer is unmounted. Tests pass with 3.1.6 but do not with

Actual Behavior

Tests break with this sample output (where Feedback.js.jsx is a child of the mounted component that uses unstable_renderSubtreeIntoContainer) when moving from 3.1x to 3.2x:

 FAIL  app/__tests__/dashboard_test.js
  ● Test suite failed to run

    Trying to insert a new style tag, but the given Node is unmounted!
    - Are you using a custom target that isn't mounted?
    - Does your document not have a valid head element?
    - Have you accidentally removed a style tag manually?
      
      at makeStyleTag (node_modules/styled-components/dist/styled-components.cjs.js:510:13)
      at makeTag (node_modules/styled-components/dist/styled-components.cjs.js:818:14)
      at StyleSheet.makeTag$$1 [as makeTag] (node_modules/styled-components/dist/styled-components.cjs.js:1045:12)
      at StyleSheet.getTagForId (node_modules/styled-components/dist/styled-components.cjs.js:1063:18)
      at StyleSheet.deferredInject (node_modules/styled-components/dist/styled-components.cjs.js:1103:10)
      at new ComponentStyle (node_modules/styled-components/dist/styled-components.cjs.js:1963:27)
      at createStyledComponent (node_modules/styled-components/dist/styled-components.cjs.js:1808:26)
      at templateFunction (node_modules/styled-components/dist/styled-components.cjs.js:2076:14)
      at Object.<anonymous> (app/components/feedback.js.jsx:15:116)
      at Object.<anonymous> (app/components/dashboard.js.jsx:12:19)
      at Object.<anonymous> (app/__tests__/dashboard_test.js:9:20)
      at handle (node_modules/worker-farm/lib/child/index.js:44:8)
      at process.<anonymous> (node_modules/worker-farm/lib/child/index.js:51:3)
      at emitTwo (events.js:106:13)
      at process.emit (events.js:194:7)
      at process.nextTick (internal/child_process.js:766:12)
      at _combinedTickCallback (internal/process/next_tick.js:73:7)
      at process._tickCallback (internal/process/next_tick.js:104:9)

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 23 (6 by maintainers)

Most upvoted comments

Here is how I solved it: I created a mock file for styled-components __mocks__/styled-components.js


const StyledComponent = require("styled-components")

const fixNodeUnmountedErrorInTestForStoryShots = styledComponents => {
  const secretInternals = styledComponents.__DO_NOT_USE_OR_YOU_WILL_BE_HAUNTED_BY_SPOOKY_GHOSTS //eslint-disable-line
  const prevMakeTag = secretInternals.StyleSheet.prototype.makeTag
  secretInternals.StyleSheet.prototype.makeTag = function makeTagMock(tag) {
    const newTag = tag || {}
    newTag.styleTag = newTag.styleTag || document.createElement("style")
    const { head } = document
    if (!newTag.styleTag.parentNode) {
      head.appendChild(newTag.styleTag)
    }
    const prevMakeTagBinded = prevMakeTag.bind(this)
    return prevMakeTagBinded(newTag)
  }
}

fixNodeUnmountedErrorInTestForStoryShots(StyledComponent)

module.exports = StyledComponent

Styled-Components: 3.3.2 Jest: 23.6.0

We run into the same issue with jest.mock calls in test module causing Trying to insert a new style tag, but the given Node is unmounted! error.

Resolved the issue by changing our mocks from this

jest.mock('../somePath/SomeDependency.js');

to this:

jest.mock('../somePath/SomeDependency.js', () => ({
  methodToMock: jest.fn();
}));

Chiming in…we have a fairly large collection of styled components in our application (~1000 off of a quick search) tested with jest 23 using v3.1.4. Upgrading to v3.3.2 triggered the above error in a few of our test files:

    Trying to insert a new style tag, but the given Node is unmounted!
    - Are you using a custom target that isn't mounted?
    - Does your document not have a valid head element?
    - Have you accidentally removed a style tag manually?

For us, the error actually seems to get thrown before any test cases are run. We were able to get past the error by un-mocking certain modules, but the stack trace indicates that modules containing styled component declarations are being imported/evaluated during test setup, before the DOM environment is initialized (possibly as a result of how jest handles mocking?). (The styled-components specific stack trace looks very similar to the OP).

@kitten Your workaround did the trick for us, but hoping you might be able to shed some light on why this is happening. Noting the StyleSheet rewrite from 3.1.x to >3.2, did the point at which styles get injected into the document change?

Here’s the snippet added to our test initialization, pulled from your comments. Based on my understanding we’re now using the server stylesheet instead of the DOM based one.

const secretInternals = require('styled-components')
  .__DO_NOT_USE_OR_YOU_WILL_BE_HAUNTED_BY_SPOOKY_GHOSTS;
const StyleSheet = secretInternals.StyleSheet;
StyleSheet.reset(true);

beforeAll(() => {
  StyleSheet.reset(true);
});

Happy to connect and provide more info if it would be helpful!

If anyone else runs into these problems… another solution that seem to work is to force styled-components to use server side rendering in your tests.

It can be done by adding this to one of the files you have specified in your jest.config.js as setupFilesAfterEnv.

import { __DO_NOT_USE_OR_YOU_WILL_BE_HAUNTED_BY_SPOOKY_GHOSTS } from 'styled-components';

__DO_NOT_USE_OR_YOU_WILL_BE_HAUNTED_BY_SPOOKY_GHOSTS.StyleSheet.reset(true);

The true is assigned to forceServer variable. This must run before the imports in tests (it’s not enough to put it to test itself).

I combined the answers, I put the stylesheet reset of the comment above in __mocks__/styled-components.js and return the original.

I can reliably reproduce this. jest@20.0.4 react@16.4.2 styled-components@3.3.3. Upgrading to jest@23.4.2 gives a different (unrelated) error.

This is what I did to reproduce it. If you are trying to reproduce this, please use the files exactly as below. There are many things that I would expect to have no effect that actually do.

test.js

const styled = require('styled-components').default;
jest.mock('./test2');
styled.div({});
require('./test2');

for (var i = 0; i < 40; i++) {
  styled.div({});
}

test('hello', () => {});

test2.js

const styled = require('styled-components').default;

styled.div({});
styled.div({});

Running jest test.js produces the following

 FAIL  ./test.js
  ● Test suite failed to run

    Trying to insert a new style tag, but the given Node is unmounted!
    - Are you using a custom target that isn't mounted?
    - Does your document not have a valid head element?
    - Have you accidentally removed a style tag manually?

      at makeStyleTag (node_modules/styled-components/dist/styled-components.cjs.js:533:13)
      at makeTag (node_modules/styled-components/dist/styled-components.cjs.js:847:14)
      at StyleSheet.makeTag$$1 [as makeTag] (node_modules/styled-components/dist/styled-components.cjs.js:1075:12)
      at StyleSheet.getTagForId (node_modules/styled-components/dist/styled-components.cjs.js:1093:18)
      at StyleSheet.deferredInject (node_modules/styled-components/dist/styled-components.cjs.js:1133:10)
      at new ComponentStyle (node_modules/styled-components/dist/styled-components.cjs.js:2030:27)
      at createStyledComponent (node_modules/styled-components/dist/styled-components.cjs.js:1861:26)
      at Function.templateFunction [as div] (node_modules/styled-components/dist/styled-components.cjs.js:2142:14)
      at Object.<anonymous> (test.js:7:10)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.688s
Ran all test suites matching "test.js".

I am going to keep looking into how to fix this, but perhaps this may help others track it down faster than I.

hm… render props shouldn’t be a problem, we hardly use them. It’s a problem how many style rules are allowed in development mode. Enzyme somehow cleans DOM if it gets too big and this removes some previous styles and breaks SC. I don’t know what is the best option here… allowing to bump this limit or somehow force enzyme not to clean DOM every time.

we were able to bypass this problem by setting NODE_ENV=production in npm script. if this solves the problem, can we opt-in this limit for development?

Actually, I have a hypothesis. It might be that a jsdom environment in enzyme and jest resets the head element, without styled-components being reset.

In 3.2 we’re now attempting to keep all elements together. So we’re appending to the last style tag’s parent and are inserting the new style tag after the last.

So knowing that, maybe a reset will do it. Can you try the following for me please?

beforeAll(() => {
  const secretInternals = require('styled-components')
    .__DO_NOT_USE_OR_YOU_WILL_BE_HAUNTED_BY_SPOOKY_GHOSTS;
  const StyleSheet = secretInternals.StyleSheet;
  StyleSheet.reset(typeof document === 'undefined');
})

(Also don’t worry, you’ll only be haunted by spooky and funny ghosts 👻)

Looking at the output more closely, the “Several Instance” warning mentioned in this issue: https://github.com/styled-components/styled-components/issues/1592 is also being thrown for these tests (the warning terminal output got overwritten initially). So this issue may be related to Jsdom/jest setting multiple instances of styled-components.