jest + enzyme, используя mount (), document.getElementById() возвращает null для компонента, который появляется после вызова метода
Я столкнулся с проблемой с моим тестированием jest+enzyme mount()
. Я тестирую функцию, которая переключает отображаемые компоненты.
Переключение между компонентами: когда state.infoDisplayContent = 'mission'
компонент missionControl
монтируется, когда state.infoDisplayContent = 'profile'
- другие компоненты входят в:
_modifyAgentStatus () {
const { currentAgentProfile, agentsDatabase } = this.state;
const agentToMod = currentAgentProfile;
if (agentToMod.status === 'Free') {
this.setState({
infoDisplayContent: 'mission'
});
agentToMod.status = 'Waiting';
} else if (agentToMod.status === 'Waiting') {
const locationSelect = document.getElementById('missionLocationSelect');
agentToMod.location = locationSelect[locationSelect.selectedIndex].innerText;
agentToMod.status = 'On Mission';
this.setState({
infoDisplayContent: 'profile'
});
}
}
Когда я запускаю эту функцию, все выглядит нормально, этот тест выполняется хорошо и тест успешно проходит с требуемым компонентом:
import React from 'react';
import { mount } from 'enzyme';
import App from '../containers/App';
const result = mount(
<App />
)
test('change mission controls', () => {
expect(result.state().currentAgentProfile.status).toBe('Free');
result.find('#statusController').simulate('click');
expect(result.find('#missionControls')).toHaveLength(1);
expect(result.find('#missionLocationSelect')).toHaveLength(1);
expect(result.state().currentAgentProfile.status).toBe('Waiting');
});
But when I simulate onClick two times:
test('change mission controls', () => {
expect(result.state().currentAgentProfile.status).toBe('Free');
result.find('#statusController').simulate('click');
expect(result.find('#missionControls')).toHaveLength(1);
expect(result.find('#missionLocationSelect')).toHaveLength(1);
expect(result.state().currentAgentProfile.status).toBe('Waiting');
result.find('#statusController').simulate('click');
expect(result.state().currentAgentProfile.status).toBe('On Mission');
});
Я получаю это утверждение:
TypeError: Cannot read property 'selectedIndex' of null
at App._modifyAgentStatus (development/containers/App/index.js:251:68)
at Object.invokeGuardedCallback [as invokeGuardedCallbackWithCatch] (node_modules/react-dom/lib/ReactErrorUtils.js:26:5)
at executeDispatch (node_modules/react-dom/lib/EventPluginUtils.js:83:21)
at Object.executeDispatchesInOrder (node_modules/react-dom/lib/EventPluginUtils.js:108:5)
at executeDispatchesAndRelease (node_modules/react-dom/lib/EventPluginHub.js:43:22)
at executeDispatchesAndReleaseSimulated (node_modules/react-dom/lib/EventPluginHub.js:51:10)
at forEachAccumulated (node_modules/react-dom/lib/forEachAccumulated.js:26:8)
at Object.processEventQueue (node_modules/react-dom/lib/EventPluginHub.js:255:7)
at node_modules/react-dom/lib/ReactTestUtils.js:350:22
at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-dom/lib/Transaction.js:140:20)
at Object.batchedUpdates (node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:62:26)
at Object.batchedUpdates (node_modules/react-dom/lib/ReactUpdates.js:97:27)
at node_modules/react-dom/lib/ReactTestUtils.js:348:18
at ReactWrapper.<anonymous> (node_modules/enzyme/build/ReactWrapper.js:776:11)
at ReactWrapper.single (node_modules/enzyme/build/ReactWrapper.js:1421:25)
at ReactWrapper.simulate (node_modules/enzyme/build/ReactWrapper.js:769:14)
at Object.<anonymous> (development/tests/AgentProfile.test.js:26:38)
at process._tickCallback (internal/process/next_tick.js:109:7)
Очевидно, что:
document.getElementById('missionLocationSelect');
Возвращение ноль, но я не могу понять почему. Элемент проходит испытания, как я уже упоминал.
expect(result.find('#missionLocationSelect')).toHaveLength(1);
Но его нельзя было захватить с помощью document.getElementById()
.
Пожалуйста, помогите мне решить эту проблему и запустить тесты.
2 ответа:
Нашел решение благодаря https://stackoverflow.com/users/853560/lewis-chung и боги Google:
Прикрепил мой компонент к DOM через
attachTo
param:
const result = mount( <App />, { attachTo: document.body } );
Изменена строка багги в моем методе на строку, которая работает с объектом элемента
agentToMod.location = locationSelect.options[locationSelect.selectedIndex].text;
:_modifyAgentStatus () {
const { currentAgentProfile, agentsDatabase } = this.state; const agentToMod = currentAgentProfile; if (agentToMod.status === 'Free') { this.setState({ infoDisplayContent: 'mission' }); agentToMod.status = 'Waiting'; } else if (agentToMod.status === 'Waiting') { const locationSelect = document.getElementById('missionLocationSelect'); agentToMod.location = agentToMod.location = locationSelect.options[locationSelect.selectedIndex].text; agentToMod.status = 'On Mission'; this.setState({ infoDisplayContent: 'profile' }); } }
Прикрепил ваш компонент к DOM через
attachTo
param.import { mount} from 'enzyme'; // Avoid Warning: render(): Rendering components directly into document.body is discouraged. beforeAll(() => { const div = document.createElement('div'); window.domNode = div; document.body.appendChild(div); }) test("Test component with mount + document query selector",()=>{ const wrapper = mount(<YourComponent/>,{ attachTo: window.domNode }); });
Зачем нам это нужно?
mount
только отрисовка компонента к элементу div, не привязанному к дереву DOM.// Enzyme code of mount renderer. createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { render(el, context, callback) { if (instance === null) { const ReactWrapperComponent = createMountWrapper(el, options); const wrappedEl = React.createElement(ReactWrapperComponent, { Component: el.type, props: el.props, context, }); instance = ReactDOM.render(wrappedEl, domNode); if (typeof callback === 'function') { callback(); } } else { instance.setChildProps(el.props, context, callback); } }, unmount() { ReactDOM.unmountComponentAtNode(domNode); instance = null; }, getNode() { return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); } // eslint-disable-next-line react/no-find-dom-node eventFn(ReactDOM.findDOMNode(node.instance), mock); }, batchedUpdates(fn) { return ReactDOM.unstable_batchedUpdates(fn); }, }; }