我在这里尝试遵循本指南,但未显示如何使用连接的组件https://medium.com/@antonybudianto/react-router-testing-with-jest-and-enzyme-17294fefd303
到目前为止,我已经设法通过了以下测试
import React from 'react';
import ReactDOM from 'react-dom';
import { mount, shallow } from 'enzyme';
import { MemoryRouter } from 'react-router';
//START imports from APP
import Admin from '../../app/components/admin/admin';
import AggregateView from '../../app/components/commsmatrix/aggregate_view';
import Approve from '../../app/components/commsmatrix/approve';
...
import UAM from '../../app/components/reports/UAM';
import WorkerJobs from '../../app/components/reports/workerjobs';
//END imports from APP
import App from '../../app/App';
const jQuery = require('jquery');
window.jQuery = window.$ = jQuery;
import configureStore from 'redux-mock-store';
import promise from 'redux-promise';
const modals = {confirmTitle: ''};
//create any initial state needed
const initialState = { activeUser: {id:1}, modals };
// here it is possible to pass in any middleware if needed into //configureStore
const middlewares = [promise]
const mockStore = configureStore(middlewares);
const mockedCallback = () => Promise.resolve([]);
test('valid path should not redirect to 404', () => {
let store = mockStore(initialState)
const wrapper = shallow(mount(
<MemoryRouter initialEntries={[ '/' ]}>
<App store={store} modals = {modals} />
</MemoryRouter>
) );
expect(wrapper.find(HomeContent)).toHaveLength(1);
});
现在抛出一个错误,我似乎无法解决。 TypeError: ShallowWrapper can only wrap valid elements
更新
如果我去除浅层,我就会得到
(node:45153) UnhandledPromiseRejectionWarning: SyntaxError: The string did not match the expected pattern.
at XMLHttpRequest.open (/var/SP/httpd/ccp-test-alpha/ccp-test-alpha.com/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:482:15)
at dispatchXhrRequest (/var/SP/httpd/ccp-test-alpha/ccp-test-alpha.com/node_modules/axios/lib/adapters/xhr.js:45:13)
at new Promise (<anonymous>)
at xhrAdapter (/var/SP/httpd/ccp-test-alpha/ccp-test-alpha.com/node_modules/axios/lib/adapters/xhr.js:12:10)
at dispatchRequest (/var/SP/httpd/ccp-test-alpha/ccp-test-alpha.com/node_modules/axios/lib/core/dispatchRequest.js:59:10)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
(node:45153) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
(node:45153) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
如果我卸下挂架,我会得到
Expected value to have length:
1
Received:
{Symbol(enzyme.__root__): {Symbol(enzyme.__root__): [Circular], Symbol(enzyme.__unrendered__): <MemoryRouter initialEntries={["/"]}><withRouter(Connect(App)) modals={{"confirmTitle": ""}} store={{"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}} /></MemoryRouter>, Symbol(enzyme.__renderer__): {"batchedUpdates": [Function batchedUpdates], "getNode": [Function getNode], "render": [Function render], "simulateError": [Function simulateError], "simulateEvent": [Function simulateEvent], "unmount": [Function unmount]}, Symbol(enzyme.__node__): {"instance": null, "key": undefined, "nodeType": "class", "props": {"children": <withRouter(Connect(App)) modals={{"confirmTitle": ""}} store={{"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}} />, "history": {"action": "POP", "block": [Function block], "canGo": [Function canGo], "createHref": [Function createPath], "entries": [{"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}], "go": [Function go], "goBack": [Function goBack], "goForward": [Function goForward], "index": 0, "length": 1, "listen": [Function listen], "location": {"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}, "push": [Function push], "replace": [Function replace]}}, "ref": null, "rendered": {"instance": null, "key": undefined, "nodeType": "function", "props": {"modals": {"confirmTitle": ""}, "store": {"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}}, "ref": null, "rendered": null, "type": [Function C]}, "type": [Function Router]}, Symbol(enzyme.__nodes__): [{"instance": null, "key": undefined, "nodeType": "class", "props": {"children": <withRouter(Connect(App)) modals={{"confirmTitle": ""}} store={{"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}} />, "history": {"action": "POP", "block": [Function block], "canGo": [Function canGo], "createHref": [Function createPath], "entries": [{"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}], "go": [Function go], "goBack": [Function goBack], "goForward": [Function goForward], "index": 0, "length": 1, "listen": [Function listen], "location": {"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}, "push": [Function push], "replace": [Function replace]}}, "ref": null, "rendered": {"instance": null, "key": undefined, "nodeType": "function", "props": {"modals": {"confirmTitle": ""}, "store": {"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}}, "ref": null, "rendered": null, "type": [Function C]}, "type": [Function Router]}], Symbol(enzyme.__options__): {"adapter": {"options": {"enableComponentDidUpdateOnSetState": true, "lifecycles": {"componentDidUpdate": {"onSetState": true}, "getDerivedStateFromProps": true, "getSnapshotBeforeUpdate": true, "setState": {"skipsComponentDidUpdateOnNullish": true}}}}}}, Symbol(enzyme.__unrendered__): null, Symbol(enzyme.__renderer__): {"batchedUpdates": [Function batchedUpdates], "getNode": [Function getNode], "render": [Function render], "simulateError": [Function simulateError], "simulateEvent": [Function simulateEvent], "unmount": [Function unmount]}, Symbol(enzyme.__node__): undefined, Symbol(enzyme.__nodes__): [], Symbol(enzyme.__options__): {"adapter": {"options": {"enableComponentDidUpdateOnSetState": true, "lifecycles": {"componentDidUpdate": {"onSetState": true}, "getDerivedStateFromProps": true, "getSnapshotBeforeUpdate": true, "setState": {"skipsComponentDidUpdateOnNullish": true}}}}}, Symbol(enzyme.__rootNodes__): [{"instance": null, "key": undefined, "nodeType": "class", "props": {"children": <withRouter(Connect(App)) modals={{"confirmTitle": ""}} store={{"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}} />, "history": {"action": "POP", "block": [Function block], "canGo": [Function canGo], "createHref": [Function createPath], "entries": [{"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}], "go": [Function go], "goBack": [Function goBack], "goForward": [Function goForward], "index": 0, "length": 1, "listen": [Function listen], "location": {"hash": "", "key": "xxk9vz", "pathname": "/", "search": "", "state": undefined}, "push": [Function push], "replace": [Function replace]}}, "ref": null, "rendered": {"instance": null, "key": undefined, "nodeType": "function", "props": {"modals": {"confirmTitle": ""}, "store": {"clearActions": [Function clearActions], "dispatch": [Function anonymous], "getActions": [Function getActions], "getState": [Function getState], "replaceReducer": [Function replaceReducer], "subscribe": [Function subscribe]}}, "ref": null, "rendered": null, "type": [Function C]}, "type": [Function Router]}]}
received.length:
0
70 | </MemoryRouter>
71 | ) ;
> 72 | expect(wrapper.find(HomeContent)).toHaveLength(1);
73 | });
74 |
家庭住所
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchContents } from '../actions';
import { Link } from 'react-router-dom';
import { fetchActiveUser } from '../actions/index';
import { bindActionCreators } from 'redux';
export class HomeContent extends Component {
constructor(props){
super(props);
this.state = {
message: '',
data: []
}
this.meta = { title: 'Home', description: '' };
this.runOnce = false;
this.passMetaBack = this.passMetaBack.bind(this);
}
passMetaBack = () => {
this.props.passMetaBack(this.meta);
}
componentDidMount() {
document.title = "Home | Connectivity Compliance Portal (CCP)";
this.passMetaBack();
}
htmlDecode(input){
let e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
initData(){
if(this.props.isReady && this.props.priv != undefined){
let self = this;
this.props.fetchContents()
.then(response => {
let data = response.payload.data;
if (data.header.error) {
self.handleShowError({
show: true,
errorMsg: data.header.message
});
} else {
self.setState({
data: _.mapKeys(data.body.recordset.record, 'target')
});
}
})
}
}
renderContent(classname) {
if(Object.keys(this.state.data).length>0 ){
let content = this.props.content[classname].content;
return (
<div dangerouslySetInnerHTML={{ __html: content}} />
);
}
}
render() {
if(!this.runOnce && this.props.isReady){
this.runOnce = true;
this.initData();
}
return (
<div className="container-fluid">
<div className="row-fluid" >
<div className="panel panel-default disclaimer">
<div className="panel-heading">
<h3 className="panel-title">INTRODUCTION</h3>
</div>
<div className="panel-body panel_introduction ql-editor">
{this.renderContent('panel_introduction')}
</div>
</div>
</div>
<div className="row-fluid top-buffer">
<div className="panel panel-default disclaimer">
<div className="panel-heading">
<h3 className="panel-title">Whats New!</h3>
</div>
<div className="panel-body panel_whats_new ql-editor">{this.renderContent('panel_whats_new')}</div>
</div>
</div>
<div className="row-fluid top-buffer">
<div className="panel panel-default disclaimer">
<div className="panel-heading">
<h3 className="panel-title">DISCLAIMER</h3>
</div>
<div className="panel-body panel_disclaimer ql-editor">{this.renderContent('panel_disclaimer')}</div>
</div>
</div>
<div className="modal fade bs-example-modal-lg" id="modalIntro" tabIndex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title" id="myModalLabel">Welcome to the Connectivity Compliance Portal (CCP)</h4>
</div>
<div className="modal-body">
You are receiving this message as you are a guest and require elevated privileges to make best use of the service. In order to do this you'll need to:
<ol start="1">
<li>Go to <a target="_blank" href="https://iam.com/identity" target="_blank" >WIAM (https://iam.com/identity)</a></li>
<li>Click on <i>"Request Access"</i> at the top</li>
<li>Search for <i>"CCP"</i></li>
<li>Select the relevant group and add it to the cart</li>
<li>Check Out!</li>
</ol>
<p>You can confirm if you have been added to the group by going to <Link to={`/ldapuser`}>https://ccp.com/ldapuser</Link>. Once you see you have been added to the group log out of CCP and log back in and you should have the correct permissions. The process can take a few hours to a couple of days depending on how long the approvers take.</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return { content: state.content }
}
//Anything returned from this function will end up as props
//on this container
function mapDispatchToProps(dispatch){
// Whenever getUser is called, the result should be passed
// to all our reducers
return bindActionCreators({ fetchContents, fetchActiveUser }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeContent);
我尽了最大努力创建了一个沙箱。它是否正确? https://codesandbox.io/s/9yyvnrkrrp