For some reasons, when I call unmount on a react wrapper node, React throws an error saying I don't have a DOM.
jsdom is initialized, before importing React and enzyme, but I still get this error on unmount.
I even checked if the dom was really initialized properly, with the same check React does, and it's evaluated as true so everything looks ok.
!!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
)
Any idea what could be the problem?
Can you share the code for the test that is throwing this error? Thanks!
I tried to find the root cause of this and I think I found it.
ES6 imports were used in the setup file for jsdom, and inside it React was imported as well and it seems that it was causing the issue.
Not sure why es6 imports were used in this file instead of commonjs :) I'm surprised it even worked before with es6 imports. The file is probably also run with the babel compilers, but then it seems there's an issue with React.
Anyway, problem solved when modifying in simple commonjs require.
Thanks for the update @tleunen!
@tleunen Can you elaborate on this? I don't understand what was the fix.
Anyway, having the same issue basically, calling unount on componet throws this invariant.
it('doesnt work', () => {
const dispatchSpy = spy();
const globalDispatchSpy = spy(mockStore, 'dispatch');
const WrappedView = dfView(syncMockFetch, MOCK_SCHEMA, MockView);
const cmp = mount(
<WrappedView
model={{}}
store={mockStore}
dispatch={dispatchSpy}
/>
);
cmp.unmount();
});
Of course neither document nor window is empty (using jsdom).
I had import X from 'Y' in the jsdom setup file and after replacing it with const X = require('Y'), everything worked
that's weird
1) I don't see how these two are different
2) Doesn't work for me anyway...
@tomkis1 can you share a small example reproducing your issue (including the content of the mounted component)? I'm in Paris for ReactEurope this week but I can look at this when I get back if no one else does.
import React from 'react';
import jsdom from 'jsdom';
import { mount } from 'enzyme';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.document = doc;
global.window = doc.defaultView;
describe('foo', () => {
it('works', () => {
mount(<div>Foo Bar</div>);
});
it('doesnt work', () => {
const mounted = mount(<div>Foo Bar</div>);
mounted.unmount();
});
});
stacktrace:
1) foo doesnt work:
Invariant Violation: dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use ReactDOMServer.renderToString() for server rendering.
at invariant (node_modules/fbjs/lib/invariant.js:38:15)
at Object.Danger.dangerouslyReplaceNodeWithMarkup (node_modules/react/lib/Danger.js:130:79)
at Object.wrapper [as replaceNodeWithMarkup] (node_modules/react/lib/ReactPerf.js:66:21)
at [object Object].ReactCompositeComponentMixin._replaceNodeWithMarkup (node_modules/react/lib/ReactCompositeComponent.js:679:31)
at [object Object].ReactCompositeComponentMixin._updateRenderedComponent (node_modules/react/lib/ReactCompositeComponent.js:669:12)
at [object Object].ReactCompositeComponentMixin._performComponentUpdate (node_modules/react/lib/ReactCompositeComponent.js:643:10)
at [object Object].ReactCompositeComponentMixin.updateComponent (node_modules/react/lib/ReactCompositeComponent.js:572:12)
at [object Object].wrapper [as updateComponent] (node_modules/react/lib/ReactPerf.js:66:21)
at [object Object].ReactCompositeComponentMixin.performUpdateIfNecessary (node_modules/react/lib/ReactCompositeComponent.js:511:12)
at Object.ReactReconciler.performUpdateIfNecessary (node_modules/react/lib/ReactReconciler.js:122:22)
at runBatchedUpdates (node_modules/react/lib/ReactUpdates.js:143:21)
at ReactReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
at ReactUpdatesFlushTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
at ReactUpdatesFlushTransaction._assign.perform (node_modules/react/lib/ReactUpdates.js:89:38)
at Object.flushBatchedUpdates (node_modules/react/lib/ReactUpdates.js:165:19)
at Object.wrapper [as flushBatchedUpdates] (node_modules/react/lib/ReactPerf.js:66:21)
at ReactDefaultBatchingStrategyTransaction.Mixin.closeAll (node_modules/react/lib/Transaction.js:202:25)
at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:149:16)
at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
at Object.enqueueUpdate (node_modules/react/lib/ReactUpdates.js:194:22)
at enqueueUpdate (node_modules/react/lib/ReactUpdateQueue.js:22:16)
at Object.ReactUpdateQueue.enqueueSetState (node_modules/react/lib/ReactUpdateQueue.js:201:5)
at [object Object].ReactComponent.setState (node_modules/react/lib/ReactComponent.js:67:16)
at ReactWrapper.<anonymous> (node_modules/enzyme/build/ReactWrapper.js:215:28)
at ReactWrapper.single (node_modules/enzyme/build/ReactWrapper.js:1297:19)
at ReactWrapper.unmount (node_modules/enzyme/build/ReactWrapper.js:214:14)
at Context.<anonymous> (foo.spec.js:11:1)
Somebody found a solution for this problem?
Yes, @matthewgertner found a workaround. Can you please elaborate?
I am having the same problem you had @tomkis1 , when I try to unmount the component I receive the same error message about the Invariant Violation
Sorry about the delay in posting a solution, here goes:
react-dom needs navigator to be a global (enzyme imports react-dom)react and enzyme imports need to happen after the globals are attached (so they need to be require calls, because imports happen before anything else irrespective of where they appear in the fileimport jsdom from 'jsdom';
const doc = jsdom.jsdom('');
global.document = doc;
global.window = doc.defaultView;
global.navigator = {
userAgent: 'node.js',
}
const React = require('react')
const mount = require('enzyme').mount
describe('foo', () => {
it('works', () => {
mount(<div>Foo Bar</div>);
});
it('doesnt work', () => {
const mounted = mount(<div>Foo Bar</div>);
mounted.unmount();
});
});
@nfcampos The import happens even before the configs I put on setup.js when I run the tests?
it's something like this:
import React from 'react'
console.log('hey')
import jsdom from 'jsdom'
console.log('boo')
react is importedjsdom is importedhey is loggedboo is loggedas for setup.js, are you talking about running mocha --require setup.js test.js? if yes, then anything in setup.js will run before anything in test.js (which is why we recommend jsdom setup happens in a separate file, to avoid these situations
That's my scenario, I put the jsdom config on setup.js and just import the mount and React on the test files. So I don't know why still happens.
Running into the same issue with TypeScript transpiled files, that make it hard to require/order the imports. Is there any other known workaround? Really surprised this only happens when unmounting, since everything else works fine.
I figured out what my issue was:
Here's my setup-test-env.js file now:
/**
* This is used to set up the environment that's needed for most
* of the unit tests for the project which includes polyfilling,
* chai setup, and initializing the DOM with jsdom
*/
import 'babel-polyfill'
import chai from 'chai'
import sinonChai from 'sinon-chai'
import {jsdom} from 'jsdom'
chai.use(sinonChai)
global.document = jsdom('<body></body>')
global.window = document.defaultView
global.navigator = window.navigator
global.expect = chai.expect
// this has to happen after the globals are set up because `chai-enzyme`
// will require `enzyme`, which requires `react`, which ultimately
// requires `fbjs/lib/ExecutionEnvironment` which (at require time) will
// attempt to determine the current environment (this is where it checks
// for whether the globals are present). Hence, the globals need to be
// initialized before requiring `chai-enzyme`.
chai.use(require('chai-enzyme')())
I don't think this issue should be closed.
I agree with @j-funk, something appears to be wrong here. If nothing else, this seems like it ought to be addressed within the documentation?
In addition to @kentcdodds fixes, I managed to fix this issue by changing the order of my test-setup.js to the following:
/**
* This is used to set up the environment that's needed for most
* of the unit tests for the project which includes polyfilling,
* chai setup, and initializing the DOM with jsdom
*/
import 'babel-polyfill'
import chai from 'chai'
import sinonChai from 'sinon-chai'
import {jsdom} from 'jsdom'
chai.use(sinonChai)
global.document = jsdom('<body></body>')
global.window = document.defaultView
global.navigator = window.navigator
global.expect = chai.expect
// this has to go after the window, document and navigator are set up
global.React = require('React');
global.TestUtils = require('react-addons-test-utils');
// this has to happen after the globals are set up because `chai-enzyme`
// will require `enzyme`, which requires `react`, which ultimately
// requires `fbjs/lib/ExecutionEnvironment` which (at require time) will
// attempt to determine the current environment (this is where it checks
// for whether the globals are present). Hence, the globals need to be
// initialized before requiring `chai-enzyme`.
chai.use(require('chai-enzyme')())
thanks @jackbrown . the last point was essential for it to work for me
To possibly help future debuggers.
The equivalent of the line below should also be placed after jsdom.
const Adapter = require('enzyme-adapter-react-15');
Most helpful comment
stacktrace: