3

I'm trying to setup some integration tests of some react components that are connected to our redux store.

Our general pattern is a const (instead of a component) which is "connect"ed with redux to the store (I think this is just background to the issue, as I have the same problem with a normal component).

I am following https://testing-library.com/docs/example-react-redux

I get this error

   console.error node_modules/react/cjs/react.development.js:172
      Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

  Check your code at feedback.test.js:32.
console.error node_modules/react-dom/cjs/react-dom.development.js:19814
  The above error occurred in the <Provider> component:
      in Provider (at feedback.test.js:26)

This is my test

import React from 'react';
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import {render, getByTitle, fireEvent} from '@testing-library/react'
//import '@testing-library/cleanup-after-each'
import '@testing-library/jest-dom/extend-expect'

import {FeedbackContainer} from '../feedbackContainer'
import {defaultState, reducer} from '../../../../redux/modules/feedback'

function renderWithRedux(
  component,
  {
    initalState, store = createStore(reducer, defaultState)
  } = {}
) {
  return {
    ...render(<Provider store={store}>{component}</Provider>)
  }
}

it('increments the counter', () => {
  console.log(<FeedbackContainer/>)
  const {container} = renderWithRedux(<FeedbackContainer />) <-- line 32
})

My container component looks like this:

import { connect } from 'react-redux';
import { Feedback } from './feedback';
import { compose, lifecycle, withHandlers } from 'recompose';
import { actions } from '../../../redux/modules/feedback';

export const mapStateToProps = (state, ownProps) => ({
// snipped state maps
});

export const mapDispatchToProps = {
// snipped functions
};

const enhance = compose(
  connect(mapStateToProps, mapDispatchToProps),
  withHandlers({
    sendFeedbackHandler: props => () => props.sendFeedback(props),
    handleResize: props => () => props.setWindowSize(),
  }),
  lifecycle({
    componentDidMount() {
      const {handleResize} = this.props
      window.addEventListener('resize',handleResize);
    },
    componentWillUnmount() {
      this.props.clearFeedback();
      const {handleResize} = this.props
      window.removeEventListener('resize', handleResize);
    },
  }),
);

export const FeedbackContainer = enhance(Feedback);

The feedback.js looks like this:

import React from 'react';
import ReactStars from 'react-stars';

export const Feedback = (props) => {

    const ratingChanged = (rating) => {
      props.setRating(rating);
    }

    const {sendFeedbackHandler} = props

    var title = null;
    switch (props.type) {
      case "REF":
        title = "Rate this FAQ"
        break;
      default:
        title = "Rate my experience"
        break
    }

    var feebackLayout;
    var buttonClassNames = "btn btn-success btn-cons pull-right";
    if (props.apertureWidth > 768) {
      buttonClassNames += " margin-right-0px ";
      feebackLayout = (
        <div>
          <h5 className="light semi-bold text-center" style={{"margin":"0px"}}>
            {title}
          </h5>
          <div style={{"textAlign":"center"}}>
            <div data-test-id='feedback-stars' className="center" style={{"display":"inline-block","float":"none"}}>
              <ReactStars
                value={props.rating}
                count={5}
                onChange={ratingChanged}
                size={24}
                color2={'#ffd700'}
                half={false}
              />
            </div>
          </div>
        </div>
      )
    } else {
      buttonClassNames += " margin-right-14px ";
      title = "Rate this:"
      feebackLayout = (
        <div style={{"textAlign":"center"}}>
          <h5 className="light semi-bold text-center"
           style={{"margin":"0px","display":"inline","verticalAlign":"middle","paddingRight":"10px"}}>
            {title}
          </h5>
          <div style={{"textAlign":"center","display":"inline-block","verticalAlign":"middle"}}>
            <ReactStars
              value={props.rating}
              count={5}
              onChange={ratingChanged}
              size={24}
              color2={'#ffd700'}
              half={false}
            />
          </div>
        </div>
      )
    }

    return (
      <div className="card card-default feedbackCard" style={{"height": "100%","borderTopRightRadius": "2px", "borderTopLeftRadius": "2px"}}>
        <div className="card-block" style={{padding: '20px 20px 20px 20px'}}>
          <div hidden={props.feedbackSubmitted}>
            {feebackLayout}
            <div hidden={props.feedbackHidden}
             style={{"paddingTop": props.apertureWidth > 768 ? "0px" : "10px"}}>
              <div>
                <textarea
                  data-test-id='feedback-text'
                  className="form-control"
                  style={{"height":"unset"}}
                  placeholder="Add any comments"
                  rows={2}
                  onChange={event => props.setFeedback(event.target.value)}
                />
              </div>
              <div style={{paddingTop: "5px"}}>
                <button
                  data-test-id='feedback-submit'
                  className={buttonClassNames}
                  onClick={sendFeedbackHandler}
                >
                  {props.buttonText}
                </button>
              </div>
            </div>
          </div>
          <div hidden={!props.feedbackSubmitted}>
            <h5 className="light semi-bold text-center">
              {props.feedbackSubmittedMessage}
            </h5>
          </div>
        </div>
      </div>
    )
};

If I change my test to use the underlying Feedback component rather than the container it doesn't crash (but then I don't think the store will be correctly connected as mapstatetoprops isn't going to be called.

If I console.log the components in my test they both return objects from what I can see so I don't quite understand the error.

FeedbackContainer:

{ '$$typeof': Symbol(react.element),
        type:
         { mapStateToProps: [Function],
           mapDispatchToProps: [Function: mapDispatchToProps],
           reactComponent: { [Function: WithHandlers] displayName: 'withHandlers(lifecycle(Component))' },
           mockDispatch:
            { [Function: mockConstructor]
              _isMockFunction: true,
              getMockImplementation: [Function],
              mock: [Getter/Setter],
              mockClear: [Function],
              mockReset: [Function],
              mockReturnValueOnce: [Function],
              mockReturnValue: [Function],
              mockImplementationOnce: [Function],
              mockImplementation: [Function],
              mockReturnThis: [Function],
              mockRestore: [Function] } },
        key: null,
        ref: null,
        props: {},
        _owner: null,
        _store: {} }

Feedback:

{ '$$typeof': Symbol(react.element),
        type: [Function],
        key: null,
        ref: null,
        props: {},
        _owner: null,
        _store: {} }

From some reading I think this could be down to some version compatabilites, this is what I have in my package.json:

"jest": "^21.2.1",
"@testing-library/jest-dom": "^4.1.0",
"@testing-library/react": "^9.1.4",
"react": "^16.8.6",
"react-dom": "^16.0.0",
"react-redux": "^5.0.5",
"redux": "^3.7.2",
"redux-saga": "^0.15.6",

Where am I going wrong?!

8
  • It looks like Feedback is returning an object. Maybe you can try to debug it to figure out why Commented Sep 16, 2019 at 18:47
  • @mattb can you share the source for Feedback too? Commented Sep 16, 2019 at 19:36
  • With the following versions, your code seems to work for me: "jest": "^24.7.1", "@testing-library/jest-dom": "^4.1.0", "@testing-library/react": "^9.1.4", "react": "^16.8.6", "react-dom": "^16.8.6", "react-redux": "^7.1.1", "redux": "^4.0.1", Commented Sep 16, 2019 at 19:37
  • @skovy - edited my post. thanks Commented Sep 16, 2019 at 20:25
  • 1
    but if my component can be displayed in a browser it must be a valid component surely? Commented Sep 17, 2019 at 21:29

1 Answer 1

0

Please see my answer here: What is wrong with react-testing-library render function? It returns an error on certain objects

expect(() =>
        render(
            <AddAssessmentFormModal
                recordDetailView={<RaDeathDecisionEditView />}
                addRecord={(record: any) => addAssessment(record)}
            />,
            {
                wrapper: () => <AppWrapper store={store} />,
            }
        )
    ).not.toThrow();
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.