Ula
Ula

Reputation: 624

React + Chai + Enzyme

I'm trying to run some unit test with Chai&Enzyme to my React app.

Enzyme seems to have an issue with props I passed to components. They got undefined in tests (not in app).

main file:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import TaskList from './components/task_list';
import AddingInput from './components/input';

const titleHeader = <h1 className="app__title">Simple to-do list:</h1>;

class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            tasks: [
                {id: 0, name: 'Complete task'},
                {id: 1, name: 'Add task'},
                {id: 2, name: 'Delete task'}
            ],
            id: 3,
        };
    }

(...)

  render () {
    return (
        <div className="app">
        {titleHeader}
        <TaskList
            tasks={this.state.tasks}
            deleteMe={this.deleteMe.bind(this)} />
        <AddingInput addTodo={this.addTodo.bind(this)} />
        </div>
    );
  }
}

ReactDOM.render(
<App />,
    document.getElementById('root')
);

Task_list - first component I want to test:

import React from 'react';
import TaskItem from './task_item';

const TaskList = (props) => {
    const taskItems = props.tasks.map((task) => {
        return (
            <TaskItem
                key={task.id}
                task={task}
                deleteMe={props.deleteMe}
                />
        );
    });

    return (
        <ol className="ordered-list">
            {taskItems}
        </ol>
    );
};

export default TaskList;

And the second:

import React from 'react';

const TaskItem = React.createClass({
  onCompleted: function(e) {
    e.target.className += " ordered-list__item--completed"
  },
  render: function() {

    return (
        <li><span className="ordered-list__item" onClick={this.onCompleted}>{this.props.task.name}</span>
            <span onClick={() => this.props.deleteMe(this.props.task)}
                  className='btn btn--delete'>Delete</span>
        </li>
    );
  }
});


export default TaskItem;

An here are my tests. Two of them are passed (both component exist) but in other two got undefined on props.tasks:

import React from 'react';
import { expect, assert } from 'chai';
import { shallow, mount } from 'enzyme';
import TaskList from './src/components/task_list';
import TaskItem from './src/components/task_item';

describe('TaskList', () => {
    it('should render TaskItem', () => {
        const wrapper = shallow(<TaskList />);
        expect(wrapper.containsAllMatchingElements([
            <TaskItem />,
        ])).to.equal(true);
    });
    it('should exists', () => {
        assert.isDefined(TaskList)
    })
});
describe('TaskItem', () => {
    it('should render one item of unordered list', () => {
        const item = mount(<TaskItem />);
        expect(item).to.contain('li');
    });
    it('should exists', () => {
        assert.isDefined(TaskList)
    })
});

Problems: 1) TaskList should render TaskItem: TypeError: Cannot read property 'map' of undefined 2) TaskItem should render one item of unordered list: TypeError: Cannot read property 'name' of undefined

Upvotes: 1

Views: 429

Answers (1)

Josh Kelley
Josh Kelley

Reputation: 58372

Your App component passes a tasks prop:

<TaskList
    tasks={this.state.tasks}
    deleteMe={this.deleteMe.bind(this)} />

And TaskList expects it to be always present. Similarly, TaskItem expects to always have certain props available (from TaskList).

If you add the necessary props in your unit tests, it should work. For TaskList, this might look something like the following (untested):

const tasks = [
    {id: 0, name: 'Complete task'},
    {id: 1, name: 'Add task'},
    {id: 2, name: 'Delete task'}
];
const wrapper = shallow(<TaskList tasks={tasks}/>);

Upvotes: 2

Related Questions