enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.95k stars 2.01k forks source link

Unable to access component jsx when exported with Redux #2063

Open maksimgm opened 5 years ago

maksimgm commented 5 years ago

I have a test for a React component which is being exported with connect. I was able to have my tests pass, but my Snapsnot is not what I expect it to be.

My Component

note I took out some of the methods, since they are not applicable to the issue.

import React, { Component } from 'react';
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import { isString } from '../../helpers/typeCheckHelper';
import classNames from 'classnames';
import * as actionCreators from '../../actions/modal';
import GenericModal from '../GenericModal';
import Notify from '../../components/Notify';
import s from './AnalyzeModal.scss';

export class AnalyzeModal extends Component {

  static propTypes = {
    data: PropTypes.object,
    sendData: PropTypes.func,
    isOpen: PropTypes.bool,
    openModal: PropTypes.func,
    closeModal: PropTypes.func,
    customStyles: PropTypes.object,
    wasClicked: PropTypes.bool,
    isSpinner: PropTypes.bool,
    toggleSpinner: PropTypes.func,
    handleAnalyzeListOfBonds: PropTypes.func,
    id: PropTypes.string,
  }

  constructor(props) {
    super(props);
    this.state = {
      notes: '',
    };
    this.closeModal = this.closeModal.bind(this);
    this.handleNotesChange = this.handleNotesChange.bind(this);
  }

  render() {
    const { isOpen, customStyles, wasClicked, isSpinner, id } = this.props;

    return (
      <GenericModal
        isOpen={isOpen}
        style={customStyles}
        contentLabel="List/Portfolio Review"
      >
        <div className={classNames('buy-btn-modal row', s['analyze-modal'])}>
          <h1 className="column small-11 header">Portfolio Review</h1>
          <button
            className="column small-1 close-modal"
            data-id={`${MODAL}-close-button`}
            onClick={() => { this.closeModal(); }}
          >X</button>
          <div className="column small-12">
            <div className={classNames('column small-12', s['table-container'], s['force-show-scrollbar'])}>
              <div className={classNames('row', s['table-header'])}>
                <div className={classNames('columns small-3 text-left', s['table-header-label'])}>QUANTITY</div>
              </div>
              {this.renderTableData()}
            </div>
            <div className="column small-12 field-container">
              <label
                className="label column small-3 text-right"
                htmlFor="notes"
              >NOTES</label>
              <textarea
                className={classNames('notes-field', s['notes-field'])}
                data-id={`${MODAL}-form-notes`}
                onChange={this.handleNotesChange}
              />
            </div>
            <div className="column small-12 button-wrapper">
              <button
                className={classNames('small-4 bright-blue submit-btn', s['submit-btn'])}
                data-id={`${MODAL}-submit-button`}
                onClick={() => { this.handleSubmit(); }}
                disabled={!!wasClicked}
              >
                {isSpinner ?
                  <div className="spinner-full-page modal-spinner" /> :
                  'Submit'
                }
              </button>
              <button
                className={classNames('small-4 bright-blue cancel-btn', s['cancel-btn'])}
                onClick={() => { this.closeModal(); }}
              >Cancel</button>
            </div>
          </div>
        </div>
      </GenericModal>
    );
  }
}

const mapDispatchToProps = dispatch => (
  bindActionCreators(actionCreators, dispatch)
);

const mapStateToProps = state => ({
  isOpen: state.modal.isModalOpen,
  id: state.modal.id,
  wasClicked: state.modal.wasClicked,
  isSpinner: state.modal.isSpinner,
  data: state.modal.data,
});

export default withStyles(s)(connect(mapStateToProps, mapDispatchToProps)(AnalyzeModal));

My Test

import React from 'react';
import AnalyzeModal from '../components/AnalyzeModal';
import Link from '../components/Link';
import { render,  mount, shallow } from 'enzyme';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';

describe('<Analyze />', () => {
  // create any initial state needed to render Connected component
  const initialState = {
    modal: {
      isModalOpen: false,
      id: '',
      wasClicked: false,
      isMarkIncluded: false,
      isSpinner: false,
      isOpen: null,
      openModal: null,
      closeModal: null,
      data: null,
    },
  };

  const middlewares = [thunk];
  const mockStore = configureMockStore(middlewares);
  let wrapper;
  let store;

  beforeEach(() => {
    store = mockStore(initialState);
    wrapper = mount(<AnalyzeModal store={store} />);
  });

  test('should be defined', () => {
    expect(wrapper).toBeDefined();
  });

  test('matches the snapshot', () => {
    expect(wrapper).toMatchSnapshot();
  })
});

My Test Snapshot

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Analyze /> matches the snapshot 1`] = `
<Connect(AnalyzeModal)
  store={
    Object {
      "clearActions": [Function],
      "dispatch": [Function],
      "getActions": [Function],
      "getState": [Function],
      "replaceReducer": [Function],
      "subscribe": [Function],
    }
  }
>
  <AnalyzeModal
    closeModal={[Function]}
    data={null}
    id=""
    isOpen={false}
    isSpinner={false}
    openModal={[Function]}
    sendData={[Function]}
    store={
      Object {
        "clearActions": [Function],
        "dispatch": [Function],
        "getActions": [Function],
        "getState": [Function],
        "replaceReducer": [Function],
        "subscribe": [Function],
      }
    }
    toggleMark={[Function]}
    toggleSpinner={[Function]}
    wasClicked={false}
  />
</Connect(AnalyzeModal)>
`;

Current behavior

Currently, the snapshot is returning the component without the JSX inside of the component itself.

Expected behavior

I expect the component to container jsx elements such as: button, div, etc.

Your environment

API

Version

library version
enzyme "^3.9.0"
react "^15.3.1"
react-dom "^15.3.1"
react-test-renderer "^15.3.1"
adapter (below) "^1.3.1"

Adapter

ljharb commented 5 years ago

enzyme doesn't have any snapshot support, nor do we endorse snapshot testing at all.

Is the output of wrapper.debug() unexpected?

maksimgm commented 5 years ago

Thank you for replying @ljharb. I meant Jest snapshot testing. That's good to know(regarding not endorsing snapshot testing), considering most of the jest and enzyme tutorials that I've watched start off their tests by creating a snapshot.

The output is unexpected:

    <Connect(AnalyzeModal) store={{...}}>
      <AnalyzeModal store={{...}} isOpen={false} id="" wasClicked={false} isSpinner={false} data= 
        {{...}} closeModal={[Function]} openModal={[Function]} sendData={[Function]} toggleMark= 
        {[Function]} toggleSpinner={[Function]} />
    </Connect(AnalyzeModal)>

I expect the AnalyzeModal to be output as displayed in my code, but for some reason it is returning the methods and props related to the component, but not the jsx itself.

ljharb commented 5 years ago

You can try wrapper.debug({ verbose: true }); but specifically that shows what your component renders, not necessarily the jsx you typed.

maksimgm commented 5 years ago

There is no difference in wrapper.debug({ verbose: true }) v. wrapper.debug(). From the few articles that I read about modal testing with enzyme, it seems like the isOpen property needs to be set to true. I set that boolean, but it didn't change that was logged. This is what I am referencing:

-https://remarkablemark.org/blog/2017/05/17/testing-react-modal/ -https://github.com/reactjs/react-modal/issues/563

ljharb commented 5 years ago

Certainly if one of the components will choose not to render its children absent isOpen being true, then they wouldn't show up.

maksimgm commented 5 years ago

isOpen is set to true and the output is the same

ljharb commented 5 years ago

@maksimgm could you perhaps provide a codepen or repo that i could use to reproduce this problem? I'm not sure how to help here, otherwise.

mayacode commented 5 years ago

try import { AnalyzeModal } from '../components/AnalyzeModal'; and pass whatever props should be there. I think you don't want to test redux functionality, just your component