Theo Itzaris
Theo Itzaris

Reputation: 4681

How to test HOC with enzyme, chai

I have a HOC function that receives a React component and returns that react component with two new method properties (handleBack & moveitOnTop) like so:

import React, { Component } from "react";
import classNames from "classnames";

 export default WrappedComponent => {
  return class extends Component {
      constructor(props) {
          super(props);
            this.moveitOnTop = this.moveitOnTop.bind(this);
            this.handleBack = this.handleBack.bind(this);

        this.state = {
            isSearchActive: false
        };
    }
    moveitOnTop(flag) {
        this.setState({ isSearchActive: flag });
        window.scrollTo(0, -100);
    }

    handleBack() {
        this.setState({ isSearchActive: false });
        if (document.body.classList.contains("lock-position")) {
            document.body.classList.remove("lock-position");
        }
    }

    render() {
        const props = {
            ...this.props,
            isSearchActive: this.state.isSearchActive,
            moveitOnTop: this.moveitOnTop,
            goBack: this.handleBack
        };

        const classes = classNames({ "c-ftsOnTop": this.state.isSearchActive });
        return (
            <div className={classes}>
                <WrappedComponent {...props} />
            </div>
        );
    }
  };
 };

The component:

 //import fireAnalytics
import { fireAnalytics } from "@modules/js-utils/lib";
class MyComponent extender Component{
  constructor(){
     super(props);
     this.handleClick = this.handleClick.bind(this);
  }

   handleClick(e) {
     // calling analytics function by passing vals
    fireAnalytics({
        event: "GAEvent",
        category: "",
        action: `Clicked on the ${e.target.id} input`,
        label: "Click"
    });

     // I CALL THE HOC PROPERTY
     this.props.moveitOnTop(true);

     // I CALL THE HOC PROPERTY
     this.props.handleBack();
   }

  render(){
     return(
        <div className="emailBlock">
          <input type="text" onClick={handleClick} />
          <Button className="submit">Submit</Button>
        </div>
     )
  }
}

// export HOC component
export default hoc(MyComponent);

// export just MyComponent
export {MyComponent};

I want to test the HOC:

  1. I need to check that class .c-ftsOnTop exists
  2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
  3. I need to check if className emailBlock exists.

The test that I tried, but fails:

 import { mount, shallow } from 'enzyme';
 import sinon from 'sinon';
 import React from 'react';
 import { expect } from 'chai';
 import hoc from '....';
 import {MyComponent} from '...';
 import MyComponent from '....';

 it('renders component', () => {

const props = {}
const HocComponent = hoc(MyComponent);
const wrapper = mount(
  <HocComponent {...props} />
);

console.log('wrapper:', wrapper);
expect(wrapper.find('.c-ftsOnTop')).to.have.lengthOf(1);
expect(wrapper.hasClass('c-fts-input-container')).toEqual(true);
 })

Error

AssertionError: expected {} to have a length of 1 but got 0

console.log: wrapper: ReactWrapper {}

Can anybody help me on how to render the HOC?

Upvotes: 3

Views: 1930

Answers (1)

Brian Adams
Brian Adams

Reputation: 45820

Here is a working test:

import { mount } from 'enzyme';
import React from 'react';
import WrappedMyComponent from './MyComponent';

it('renders component', () => {
  const props = {}
  const moveitOnTopSpy = jest.spyOn(WrappedMyComponent.prototype, 'moveitOnTop');
  const handleBackSpy = jest.spyOn(WrappedMyComponent.prototype, 'handleBack');
  const wrapper = mount(
    <WrappedMyComponent {...props} />
  );

  // 1. I need to check that class .c-ftsOnTop exists
  wrapper.setState({ isSearchActive: true });  // <= set isSearchActive to true so .c-ftsOnTop is added
  expect(wrapper.find('.c-ftsOnTop')).toHaveLength(1);  // Success!

  // 2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
  window.scrollTo = jest.fn();  // mock window.scrollTo
  wrapper.find('input').props().onClick();
  expect(moveitOnTopSpy).toHaveBeenCalled();  // Success!
  expect(window.scrollTo).toHaveBeenCalledWith(0, -100);  // Success!
  expect(handleBackSpy).toHaveBeenCalled();  // Success!

  // 3. I need to check if className emailBlock exists
  expect(wrapper.find('.emailBlock')).toHaveLength(1);  // Success!
})

Details

.c-ftsOnTop is only added when isSearchActive is true so just set the state of the component so the class is added.

If you create your spies on the prototype methods for moveitOnTop and handleBack, then when the hoc creates its instance methods by binding them to this in the constructor, the instance methods will be bound to your spies.

window.scrollTo logs an error to the console by default in jsdom so you can mock it to avoid that error message and to verify that it was called with the expected arguments.


Note that the above test requires the following typos to be fixed in MyComponent:

  • extender should be extends
  • constructor should take a props argument
  • onClick should be bound to this.handleClick instead of just handleClick
  • handleClick should call this.props.goBack() instead of this.props.handleBack()

(I'm guessing MyComponent was just thrown together as an example of the actual component)

Upvotes: 1

Related Questions