T0wn3R
T0wn3R

Reputation: 11

Cannot read property 'value' when simulating a form submit

I am trying to do a complete istanbul coverage test with jest. At this moment I have a component almost all tested but there is a handleSubmit function where I make a dispatch receiving form event data and when I run the test it tells me

TypeError: Cannot read property 'value' of undefined

      10 |             payload: {
      11 |                 name: name.value,
    > 12 |                 item: item.value, 
         |                            ^
      13 |                 status: status.value  }
      14 |         })
      15 |     }

I am loading a mockstore, mounted all the component, its all tested but the submit still fails. My test function is as simple as:

it('testing submit', () => {
const form = component.find(`[data-test="submit"]`).first()
form.simulate('submit')
... and continues expecting some existences, but there aren't problems there

I already tried this: enzyme simulate submit form, Cannot read property 'value' of undefined

And tried to parse the event values in the simulate action...

The complete module code is...

class Filters extends Component {
    handleSubmit = event => {
        event.preventDefault()
        const {name, items, status} = event.target;
        this.props.dispatch({
            type: 'SEARCH_PLAYER',
            payload: {
                name: name.value,
                item: item.value, 
                status: status.value  }
        })
    }


    render() {
        return(
            <div>
                <form onSubmit={this.handleSubmit} data-test="submit">
                    <div className="form-group col-md-12 text-center"> ...

Another really crazy thing is that my test recognize the "event.target.name.value" and not the items and status. In fact if i delete items and status from the dispatch the test runs successfully.

Upvotes: 1

Views: 1350

Answers (2)

Matt Carlotta
Matt Carlotta

Reputation: 19762

The way you chose to handle values is a bit strange. Instead, handle values through state like so: Controlled Components

Then you can test that this.props.dispatch() was called with the correct values.

Side note: Avoid using data attributes when unnecessary, as they'll start to clog up your DOM with superfluous attributes. You have plenty of options to find by element, element.className, className, ...and so on.

Working example: https://codesandbox.io/s/5j4474rkk (you can run the test defined below by clicking on the Tests tab at the bottom left of the screen.

components/Form/Form.js

import React, { Component } from "react";
import PropTypes from "prop-types";

export default class Form extends Component {
  state = {
    test: ""
  };

  static propTypes = {
    dispatch: PropTypes.func.isRequired
  };

  handleChange = ({ target: { name, value } }) => {
    this.setState({ [name]: value });
  };

  handleSubmit = e => {
    e.preventDefault();
    this.props.dispatch({
      type: "SEARCH_PLAYER",
      payload: {
        test: this.state.test
      }
    });
  };

  render = () => (
    <form onSubmit={this.handleSubmit} className="form-container">
      <h1>Form Testing</h1>
      <input
        className="uk-input input"
        type="text"
        name="test"
        placeholder="Type something..."
        onChange={this.handleChange}
        value={this.state.test}
      />
      <button type="submit" className="uk-button uk-button-primary submit">
        Submit
      </button>
    </form>
  );
}

components/Form/__tests__/Form.js (shallowWrap and checkProps are custom functions that can be found in test/utils/index.js)

import React from "react";
import { shallowWrap, checkProps } from "../../../test/utils";
import Form from "../Form";

const dispatch = jest.fn();

const initialProps = {
  dispatch
};

const initialState = {
  test: ""
};

const wrapper = shallowWrap(<Form {...initialProps} />, initialState);
describe("Form", () => {
  it("renders without errors", () => {
    const formComponent = wrapper.find(".form-container");
    expect(formComponent).toHaveLength(1);
  });

  it("does not throw PropType warnings", () => {
    checkProps(Form, initialProps);
  });

  it("submits correct values to dispatch", () => {
    const name = "test";
    const value = "Hello World!";
    const finalValues = {
      type: "SEARCH_PLAYER",
      payload: {
        [name]: value
      }
    };

    wrapper.find("input").simulate("change", { target: { name, value } }); // simulates an input onChange event with supplied: name (event.target.name) and value (event.target.value)

    wrapper
      .find(".form-container")
      .simulate("submit", { preventDefault: () => null }); // simulates a form submission that has a mocked preventDefault (event.preventDefault()) to avoid errors about it being undefined upon form submission

    expect(dispatch).toBeCalledWith(finalValues); // expect dispatch to be called with the values defined above
  });
});

Upvotes: 2

n9iels
n9iels

Reputation: 895

Looks like you are using item on line 12, but extracting items from the event.target.

Upvotes: 2

Related Questions