Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling setState in jsdom-based tests causing "Cannot render markup in a worker thread" error

Tags:

I'm testing my React components under jsdom using my own tiny "virtual browser" utility. Works just fine, until I'm trying to setState. For example, when testing a children ages input control:

describe('rendering according to the draft value', function () {     var component;      beforeEach(function () {         component = TestUtils.renderIntoDocument(             React.createElement(ChildrenInput, {value: []})         );          component.setState({draft: [{age: null}, {age: null}]}, done);     });      it('overrides the value property for the count', function () {         assert.strictEqual(component.refs.count.props.value, 2);     });      it('overrides the value property for the ages', function () {         assert.strictEqual(component.refs.age1.props.value, null);     }); }); 

…on the setState line I'm getting:

Uncaught Error: Invariant Violation: dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure window and document are available globally before requiring React when unit testing or use React.renderToString for server rendering.

I know that window and document globals are indeed set by the jsdom-based TestBrowser, like that:

global.document = jsdom.jsdom('<html><body></body></html>', jsdom.level(1, 'core')); global.window = global.document.parentWindow; 

I even tried to wrap setState into a setTimeout(..., 0). It doesn't help. How can I make testing the state changes work?

like image 305
Ivan Krechetov Avatar asked Nov 11 '14 14:11

Ivan Krechetov


2 Answers

At load time React determines if it can use DOM, and stores it as a boolean.

var canUseDOM = !!(   typeof window !== 'undefined' &&   window.document &&   window.document.createElement ); ExecutionEnvironment.canUseDOM = canUseDOM; 

This means that if it's loaded before those conditions will be true, it assumes it can't use DOM.

You could monkey patch it in your beforeEach.

require('fbjs/lib/ExecutionEnvironment').canUseDOM = true 

Or you could fake it at first:

global.window = {}; global.window.document = {createElement: function(){}}; 

Or ensure that you don't load React before you set up JSDOM, which is the only way I'm positive about, but it's also the most difficult and bug prone.

Or you could report this as an issue, or inspect jest's source and see how it resolves it.

like image 200
Brigand Avatar answered Oct 15 '22 21:10

Brigand


If you use Mocha and Babel, you will need to setup jsdom before the compilers evaluate the React code.

// setup-jsdom.js  var jsdom = require("jsdom");  // Setup the jsdom environment // @see https://github.com/facebook/react/issues/5046 global.document = jsdom.jsdom('<!doctype html><html><body></body></html>'); global.window = document.defaultView; global.navigator = global.window.navigator; 

With the --require arg:

$ mocha --compilers js:babel/register --require ./test/setup-jsdom.js test/*.js 

See https://github.com/facebook/react/issues/5046#issuecomment-146222515

Source

like image 43
leplatrem Avatar answered Oct 15 '22 21:10

leplatrem