Вызов setState в тестах на основе jsdom, вызывающий ошибку «Не удается отобразить разметку в рабочем потоке».

Я тестирую свои компоненты React под jsdom, используя мой собственный крошечный "виртуальный браузер". Работает нормально, пока я не попытаюсь setState. Например, при тестировании элемента управления вводом «детский возраст»:

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);
    });
});

… в строке setState я получаю:

Неперехваченная ошибка: нарушение инварианта:опасноRenderMarkup(...): не удается отобразить разметку в рабочем потоке. Убедитесь, что window и document доступны глобально, прежде чем требовать React при модульном тестировании или используйте React.renderToString для рендеринга на сервере.

Я знаю, что глобальные переменные window и document действительно устанавливаются TestBrowser на основе jsdom, например:

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

Я даже пытался обернуть setState в setTimeout(..., 0). Это не помогает. Как я могу заставить тестирование изменений состояния работать?


person Ivan Krechetov    schedule 11.11.2014    source источник
comment
Похоже, вы являетесь автором упомянутой вами утилиты. Я не вижу, чтобы он разветвлялся откуда-либо. Так что вы должны упомянуть, что это ваш инструмент. (Это политика SO.) Кроме того, кажется, что в вопросе отсутствует некоторая важная информация: а именно, как jsdom инициализируется и используется вашим кодом. Отправка нас на внешний веб-сайт не заменяет указание этой информации в самом вопросе.   -  person Louis    schedule 11.11.2014
comment
Я обновил текст. Спасибо за ответ.   -  person Ivan Krechetov    schedule 11.11.2014


Ответы (4)


Во время загрузки React определяет, может ли он использовать DOM, и сохраняет его как логическое значение.

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

Это означает, что если он загружен до того, как эти условия станут истинными, предполагается, что он не может использовать DOM.

Вы можете исправить это в файле beforeEach.

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

Или вы можете сначала подделать это:

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

Или убедитесь, что вы не загружаете React, прежде чем настраивать JSDOM, это единственный способ, в котором я уверен, но он также самый сложный и подвержен ошибкам.

Или вы можете сообщить об этом как о проблеме или проверить источник шутки и посмотреть, как он ее решает.

person Brigand    schedule 11.11.2014
comment
ExecutionEnvironment, кажется, переехал: const ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); - person ehrencrona; 24.03.2016

Если вы используете Mocha и Babel, вам нужно будет настроить jsdom до того, как компиляторы оценят код React.

// 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;

С аргументом --require:

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

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

Источник

person leplatrem    schedule 07.10.2015
comment
Вы можете просто добавить require('babel/register'); в setup-jsdom.js и использовать mocha --compilers js:setup-jsdom.js - person senornestor; 14.12.2015
comment
Эта ссылка на источник была очень полезна для меня — я устранил, вероятно, 80% шаблонов дерьма, которые я использовал раньше. Спасибо! Порекомендуйте сделать эту ссылку более заметной в ответе — ее легко пропустить. - person killthrush; 06.01.2016
comment
Эта ссылка на источник теперь не работает. - person Ryan Ore; 14.04.2016

Я также получил эту ошибку, используя Babel для тестирования кода ES6. Дело в том, что между import React from 'react' и const React = require('react') есть разница. Если вы используете синтаксис import, то все ваши импорты будут выполняться раньше всего, поэтому вы не сможете имитировать window или document раньше.

Итак, первый бит - заменить import на require в тестах (где это важно)

Второй бит — имитировать global.navigator с помощью global.navigator = global.window.navigator.

Надеюсь, это поможет кому-то.

person Alexey Kureev    schedule 09.07.2015

У меня ничего не работало, пока я не попытался потребовать jsdom-global в команде mocha:

--require jsdom-global/register

Источник: https://github.com/rstacruz/jsdom-global/blob/master/README.md#mocha

person markau    schedule 16.01.2017
comment
Хотя теоретически это может ответить на вопрос, было бы предпочтительнее включить сюда основные части ответа и предоставить ссылку для справки. . - person Baum mit Augen; 16.01.2017