如何测试与连接组件的反应路线

时间:2018-11-22 13:41:57

标签: reactjs jestjs enzyme

我在这里尝试遵循本指南,但未显示如何使用连接的组件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">&times;</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

0 个答案:

没有答案