2

We have a form with a simple input and submit button. Upon submit, there will be li's added inside of the section.user-list.

After typing ni and clicking the Search button, 2 lis will enter the view, one of them containing "Nick Sims"

enter image description here

enter image description here

However after running the test below, the view never changed, the test picks up the default "Search for a user" string which should be replaced by the new lis.

Note, in our app, there is a dispatch function which calls a Redux action to SEARCH_USERS in order to return the user list, could this be the problem?

enter image description here

import React from 'react'
import { shallow, mount } from 'enzyme'
import toJson from 'enzyme-to-json'

import { UserListContainer } from './UserListContainer'

const userListContainer = mount(<UserListContainer />);

describe('<UserListContainer /> tests', () => {
    let wrapper;

    beforeAll(() => {
        wrapper = mount(<UserListContainer searchUser={() => {}} />);
    });

    it('Should contain an input', () => {
        expect(wrapper.find('input')).toHaveLength(1);
    });

    it('Should show User after a user searches', () => {
        expect(wrapper.find('input.input-user')).toHaveLength(1);
        wrapper.find('input.input-user').simulate('keydown', { which: 'r' });
        wrapper.find('input.input-user').simulate('keydown', { which: 'o' });

        const text = wrapper.find('input.input-user').text();
        const value = wrapper.find('input.input-user').get(0).value;

        console.log('value:', value)
        console.log('text:', text)

        wrapper.find('button.btn-blue').simulate('click');

        expect(wrapper.find('.user-population li').text()).toEqual('Nick Sims');
    });
});

UserListContainer (Smart)

import React, { Component } from "react"
import { connect } from 'react-redux'
import { UserList } from '../../components'
import { searchUser } from '../../actions'

export class UserListContainer extends Component {
    constructor(props) {
        super(props);
    }

    onFormSubmit(e, user) {
        e.preventDefault();
        this.props.searchUser(user)
    }

    render() {
        return (
            <div className='users-container'>
                <UserList
                    users={this.props.users}
                    lastUserSearched={this.props.lastUserSearched}
                    onFormSubmit={this.onFormSubmit.bind(this)}
                />
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        users: state.usersReducer.users,
        lastUserSearched: state.usersReducer.lastUserSearched
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        searchUser: (user) => {dispatch(searchUser(user))},
    }
}

const userList = UserListContainer;
export default connect(mapStateToProps, mapDispatchToProps)(userList)

UserList (Dumb)

import React from 'react'
import { Link } from 'react-router-dom'

export default class UserList extends React.Component {
  constructor(props) {
    super(props);
  }

  onFormSubmit(e) {
    const value = this.input.value;
    this.props.onFormSubmit(e, value);
  }

  populateUsers() {
    const users = this.props.users;
    if(!this.props.lastUserSearched) {
      return <li>Search for a user</li>
    }

    { /* Search / Auto Suggest functionality */ }
    const regex = this.props.lastUserSearched;
    let re = new RegExp(regex, "gi");

    let filteredUsers = users.filter(v => {
      if(v.name.match(re)) {
        return v;
      }
      return;
    });

    if(filteredUsers.length < 1) {
      return <li>No users found with that name</li>
    }

    return filteredUsers.map(x => {
      return (
        <li key={x._id}>
          <Link to="#" className="user-link">
            { x.name }
          </Link>
        </li>
      )
    });
  }

  render () {
    return (
      <section className="user-list">
        <form onSubmit={ this.onFormSubmit.bind(this) }>
          <label>
            <input
              type="text"
              className="input-user"
              placeholder="Search.."
              ref={(ref) => { this.input = ref; }}
            />
            <button className="btn-blue" type="submit">Search</button>
          </label>
        </form>
        <h2>User List</h2>
        <ul className="user-population">
          { this.populateUsers() }
        </ul>
      </section>
    )
  }
}

Also tried this, same results

// wrapper.find('input.input-user').simulate('input', { target: { value: 'ni' } });
wrapper.find('input.input-user').simulate('submit', { target: { value: 'ni' } });
Leon Gaban
  • 36,509
  • 115
  • 332
  • 529
  • `simulate('keydown', ...)` simulates a keydown event being emitted by the DOM element, i.e. the same thing that happens in the browser *after* the user presses a key. It does not simulate characters being inserted into the text field (because characters being inserted into the text field is something that happens *along with* the event being emitted; not *because of* the event). Of course you're not the first person to have this particular misconception. Here's a discussion worth reading in the Enzyme issues: https://github.com/airbnb/enzyme/issues/76 – Jordan Running Jul 21 '17 at 15:48
  • @JordanRunning I also tried this `wrapper.find('input.input-user').simulate('submit', { target: { value: 'ni' } });` adding to question, how would you solve this problem then? – Leon Gaban Jul 21 '17 at 15:51

1 Answers1

1

This is an overly complex test, the action dispatches an event which then updates the state via Redux.

What we're testing needs to be broken down into several steps.

  • Mocking fake data
  • Testing the actions in the reducers
  • Enzyme doesn't support dispatched events yet

https://github.com/airbnb/enzyme/blob/master/docs/future.md

Leon Gaban
  • 36,509
  • 115
  • 332
  • 529