New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to change the value of form inputs #364

Open
MoonTahoe opened this Issue May 5, 2016 · 31 comments

Comments

@MoonTahoe
Copy link

MoonTahoe commented May 5, 2016

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.

@aweary

This comment has been minimized.

Copy link
Collaborator

aweary commented May 5, 2016

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

FYI this has been reported previously here: #308

@aweary

This comment has been minimized.

Copy link
Collaborator

aweary commented May 5, 2016

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

@MoonTahoe

This comment has been minimized.

Copy link

MoonTahoe commented May 5, 2016

Yea, thanks. The issue, in my case is that I cannot change the TheForm, I only have access to the test for the form.

@MoonTahoe

This comment has been minimized.

Copy link

MoonTahoe commented May 5, 2016

I was able to create a test that passed based on what I found in issue #76.

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

    let wrapper;

    before(() => {
        wrapper = mount(<TheForm />);
        wrapper.ref('first').get(0).value = 'David';
        wrapper.ref('last').get(0).value = 'Blane';
        wrapper.find('form').simulate('submit');
    });

    it("can set first name", () => expect(wrapper.ref('first').get(0).value).to.equal("David"));

    it("can set last name", () => expect(wrapper.ref('last').get(0).value).to.equal("Blane"));

});

It would be nice if there were a cleaner way to do this as requested in Issue #76

@aweary

This comment has been minimized.

Copy link
Collaborator

aweary commented May 6, 2016

@MoonTahoe I'm curious, does this work?

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

This comment has been minimized.

Copy link
Collaborator

aweary commented May 6, 2016

@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)
@ffxsam

This comment has been minimized.

Copy link

ffxsam commented May 13, 2016

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

@tarjei

This comment has been minimized.

Copy link

tarjei commented Jun 12, 2016

Hi, I'm sorry for hijacking this thread, but the examples listed here should go into the documentation. Form handling is badly documented (IMHO).

Also, how about a setValue(value, triggerChange=true) method that could work on select and input? I.e. you could use the method to both set a value and emit the change event. this would improve usability for formhandling a lot.

Then you can do wrapper.find('input').setValue("val") instead of a three line bolierplate invocation.

T

@ghost

This comment has been minimized.

Copy link

ghost commented Jun 17, 2016

using the solution proposed by @aweary, I get TypeError: Can't add property value, object is not extensible

@aweary

This comment has been minimized.

Copy link
Collaborator

aweary commented Jun 17, 2016

@Moezalez what testing environment are you using (jsdom, karma, etc.)?

@ghost

This comment has been minimized.

Copy link

ghost commented Jun 17, 2016

@aweary nyc + mocha

@aweary

This comment has been minimized.

Copy link
Collaborator

aweary commented Jun 17, 2016

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

@whitejava

This comment has been minimized.

Copy link

whitejava commented Oct 4, 2016

This works fine for me:

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

This comment has been minimized.

Copy link

bochen2014 commented Oct 10, 2016

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

@julia-kits

This comment has been minimized.

Copy link

julia-kits commented Nov 21, 2016

@bochen2014 thanks so much, your solution is the only one working for me! you really helped me!

@andela-cdike

This comment has been minimized.

Copy link

andela-cdike commented Jan 15, 2017

@bochen2014 another +1 for you. Thanks a lot. Your solution alone worked for me.

@Rob-Leggett

This comment has been minimized.

Copy link

Rob-Leggett commented Jan 29, 2017

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'}})
@ConAntonakos

This comment has been minimized.

Copy link

ConAntonakos commented Apr 26, 2017

@aweary's solution worked for my testing purposes, yet I still don't know why. 😉

@nerfologist

This comment has been minimized.

Copy link

nerfologist commented Jun 21, 2017

Hi, I opened PR #995 adding a test case to (hopefully) demonstrate this issue.

@Falieson

This comment has been minimized.

Copy link

Falieson commented Aug 8, 2017

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')
})
@ljharb

This comment has been minimized.

Copy link
Member

ljharb commented Aug 8, 2017

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.

@Falieson

This comment has been minimized.

Copy link

Falieson commented Aug 8, 2017

Good to know, thanks - I probably spent an hour trying to hunt down the solution w/o anyone saying that a particular way is the better way to do it.

@DannyRyman

This comment has been minimized.

Copy link

DannyRyman commented Sep 27, 2017

Hmm, using node.value no longer works "Attempted to access ReactWrapper::node, which was previously a private property on Enzyme ReactWrapper instances, but is no longer and should not be relied upon. Consider using the getElement() method instead". Is there an alternative way of setting the value?

@ljharb

This comment has been minimized.

Copy link
Member

ljharb commented Sep 27, 2017

There's a getNode function.

@bjudson

This comment has been minimized.

Copy link

bjudson commented Sep 28, 2017

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

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

This comment has been minimized.

Copy link

igorify commented Oct 6, 2017

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

@abhinavsingi

This comment has been minimized.

Copy link

abhinavsingi commented May 25, 2018

seems better than #76 (comment)
and also works
as mentioned above - wrapper.find('input').instance().value = "foo"

@Quadriphobs1

This comment has been minimized.

Copy link

Quadriphobs1 commented Jul 16, 2018

I am trying this with a redux state component, The dispatch is working as I can confirm from the action logging out the output but the reducer is not working as I cant seems to get a logged out output at all
Any idea what could be wrong

@engmyahya

This comment has been minimized.

Copy link

engmyahya commented Dec 5, 2018

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);
 });
@saicharanp

This comment has been minimized.

Copy link

saicharanp commented Dec 7, 2018

@engmyahya Your code throws a typescript error for me - Property value does not exist on type Element

@anupammaurya

This comment has been minimized.

Copy link

anupammaurya commented Jan 2, 2019

here is my code..

const input = MobileNumberComponent.find('input')
// when
input.props().onChange({target: {
   id: 'mobile-no',
   value: '1234567900'
}});
MobileNumberComponent.update()
const Footer = (loginComponent.find('Footer'))
expect(Footer.find('Buttons').props().disabled).equals(false)

I have update my DOM with componentname.update()
And then checking submit button validation(disable/enable) with length 10 digit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment