enzyme: Unable to change the value of form inputs

First… let me mention that I love using enzyme. Great work. I am currently putting togther training materials for React that will use enzyme for testing.

Problem

I am having a problem testing a form. Specifically changing input values on the form.

wrapper.ref('first').simulate('change', {target: {value: 'David'}}); is not working wrapper.find("input#first").simulate('change', {target: {value: 'David'}}); is not working

I have created a reduced test case that demonstrates the problem. The output for this example looks like:

Output

  Testing a form
first name: Mike
last name: Tyson
    ✓ can fill out the form

No matter what the default Mike Tyson is always the name, despite the fact that I am chanign it. Why does this happen? Is there another way to change the values for the form?

Test

import { Component } from 'react'
import { expect } from 'chai'
import { mount } from 'enzyme'

class TheForm extends Component {

    submit() {
        console.log('first name:', this.refs.first.value);
        console.log('last name:', this.refs.last.value);
    }

    render() {
        return (
            <form action="javascript:void(0)"
                  onSubmit={this.submit.bind(this)}>

                <div>
                    <input ref="first"
                           type="text"
                           placeholder="your first name..."
                           required="required"
                           defaultValue="Mike"/>
                </div>

                <div>
                    <input ref="last"
                           type="text"
                           placeholder="your last name..."
                           required="required"
                           defaultValue="Tyson" />
                </div>

                <button>ADD</button>

            </form>
        );
    }

}

describe("Testing a form", () => {

    it("can fill out the form", () => {
        const wrapper = mount(<TheForm />);
        wrapper.ref('first').simulate('change', {target: {value: 'David'}});
        wrapper.ref('last').simulate('change', {target: {value: 'Blane'}});
        wrapper.find('form').simulate('submit');
    });

});

also… wrapper.find(‘form’).simulate(‘submit’) works, but clicking the button does not. Even though clicking the button submits the form in the browser.

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 24
  • Comments: 32 (2 by maintainers)

Most upvoted comments

@MoonTahoe also, following the example of ReactTestUtils.Simulate.change, this should work:

const wrapper = mount(<TheForm/>);
const first = wrapper.find('first');
first.node.value = 'David'
first.simulate('change', first)

Using mocha 3, react 16, and enzyme 3, this worked for me:

wrapper.find('input').instance().value = "foo";

Thanks @bjudson . Start using wrapper.find('input').instance().value = "foo" instead of
wrapper.find('input').node.value = "foo",

because according to documentation, node method is private in enzyme3 http://airbnb.io/enzyme/docs/guides/migration-from-2-to-3.html#private-properties-and-methods-have-been-removed

The following worked for me also.

const username = wrapper.ref('username')
username.node.value='test'
username.simulate('change', username)

However I am disappointed the following did not work.

wrapper.ref('username').simulate('change', {target: {value: 'test'}})

just to help people who are struggling with changing input value (and do some further checks, like validation). here is something that works for me after I read through this post

 it('should display business trade name correctly', () => {
        const business_trade_name = wrapper.findWhere(n => n.name() === 'Field'
            && n.prop('name') === 'business_trade_name')
        expect(business_trade_name).toExist('cannot find business_trade_name. did you accidently renamed the field?')


        const input = business_trade_name.find('input')

        input.node.value='bo chen**&&))'
        input.simulate('change', input)

        wrapper.find('form').simulate('submit')

       const errorSpan = business_trade_name.find('span')
       expect(errorSpan.text()).toBe('Invalid business trade name')


    })


my rendered dom tree is like

<form>
       <Field name='business_trade_name'>
           <input />
          <span />
      </Field>

      <Field name='another_field'>
         <input />
        <span />
      </Field>
</form>

and yes, i’m using redux-form

@MoonTahoe I’m curious, does this work?

wrapper.find('input').first().simulate('change', {target: {value: 'David'}});`

This worked with me:

it("Successfully add an employee to the employees' list when the form submitted",function(){
   const wrapper = mount(<App/>);
   const addEmpWapper = wrapper.find('AddEmployee');
   addEmpWapper.find("#txtName").getDOMNode().value = "Youki";
   addEmpWapper.find("#txtImgUrl").getDOMNode().value = "ImageUrl1";
   addEmpWapper.find("#txtDesc").getDOMNode().value = "Cute baby.";

   const form = addEmpWapper.find('form');
   form.simulate("submit");
   // I already had 3 employees in the app's states bag.
   expect(wrapper.state().employees).to.have.lengthOf(4);
 });

seems better than https://github.com/airbnb/enzyme/issues/76#issuecomment-189606849 and also works as mentioned above - wrapper.find('input').instance().value = "foo"

I’m able to reproduce your issue with the current release of enzyme, I’ll look into how this is expected to work. For what it’s worth, you can use controlled components to handle the input state, which works as you expected:

class TheForm extends React.Component {

    constructor(props) {
      super(props);
      this.state = {
        firstName: 'Mike',
        lastName: 'Tyson'
      }
      this.onFirstNameChange = this.onFirstNameChange.bind(this);
      this.onLastNameChange = this.onLastNameChange.bind(this);
    }

    submit() {
        console.log('first name:', this.state.firstName);
        console.log('last name:', this.state.lastName);
    }

    onFirstNameChange(e) {
      this.setState({
        firstName: e.target.value
      })
    }

    onLastNameChange(e) {
      this.setState({
        lastName: e.target.value
      })
    }

    render() {
        return (
            <form action="javascript:void(0)"
                  onSubmit={this.submit.bind(this)}>

                <div>
                    <input ref="first"
                           type="text"
                           placeholder="your first name..."
                           required="required"
                           onChange={this.onFirstNameChange}
                           value={this.state.firstName}/>
                </div>

                <div>
                    <input ref="last"
                           type="text"
                           placeholder="your last name..."
                           required="required"
                           onChange={this.onLastNameChange}
                           value={this.state.lastName} />
                </div>

                <button>ADD</button>

            </form>
        );
    }

}

Controlled components are typically recommended as well: https://facebook.github.io/react/docs/forms.html

It’s not meant to cause a change; it’s mean to call the onChange function.

I recommend avoiding simulate, and manually invoking the prop function you want instead.

I also am getting errors related to this subject (simulate(‘change’ …) not causing a change)

test('TaskHeader changes Value after Inputted', () => {
  const component = mount(<TaskHeader listName="Demo" totalTasks={55} />)

  const taskInput = component.find('.app-tasks-newTask-input')
  expect(taskInput.props().value).toEqual('')

  // const event =  { target: { value: 'First Task' } }
  
  taskInput.props().onChange({currentTarget: {value: 'First Task'}}) // NOTE: this works
  expect(taskInput.props().value).toEqual('First Task')

  // taskInput.simulate('change',{target: {value: 'Second Task'}}) // NOTE: doesn't work
  taskInput.node.value='Second Task'
  taskInput.simulate('change', taskInput) // NOTE: works as a replacement
  expect(taskInput.props().value).toEqual('Second Task')
})

This works fine for me:

// <div><input/></div>
let a = mount(<TestComponent/>);
a.find('input').node.value = 'Task1';

@Moezalez so you’re using jsdom? What version of React and what version of enzyme? Can you share a small reproducable case?

I also ran into issues getting and setting values on <input /> tags. input.node.value works great, but I’m curious why this doesn’t work:

    const wrapper = mount(<EditableText defaultValue="Hello" />);
    const input = wrapper.find('input');

    console.log(input.render().val());

http://stackoverflow.com/questions/37219772/enzyme-how-to-access-and-set-input-value