125

I'm confused about how to access <input> value when using mount. Here's what I've got as my test:

  it('cancels changes when user presses esc', done => {
    const wrapper = mount(<EditableText defaultValue="Hello" />);
    const input = wrapper.find('input');

    console.log(input.render().attr('value'));
    input.simulate('focus');
    done();
  });

The console prints out undefined. But if I slightly modify the code, it works:

  it('cancels changes when user presses esc', done => {
    const wrapper = render(<EditableText defaultValue="Hello" />);
    const input = wrapper.find('input');

    console.log(input.val());
    input.simulate('focus');
    done();
  });

Except, of course, the input.simulate line fails since I'm using render now. I need both to work properly. How do I fix this?

EDIT:

I should mention, <EditableText /> is not a controlled component. But when I pass defaultValue into <input />, it seems to set the value. The second code block above does print out the value, and likewise if I inspect the input element in Chrome and type $0.value in the console, it shows the expected value.

ffxsam
  • 26,428
  • 32
  • 94
  • 144

17 Answers17

122

I think what you want is:

input.simulate('change', { target: { value: 'Hello' } })

Here's my source.

You shouldn't need to use render() anywhere to set the value. And just FYI, you are using two different render()'s. The one in your first code block is from Enzyme, and is a method on the wraper object mount and find give you. The second one, though it's not 100% clear, is probably the one from react-dom. If you're using Enzyme, just use shallow or mount as appropriate and there's no need for render from react-dom.

Tyler Collier
  • 11,489
  • 9
  • 73
  • 80
  • The `input.render()` is not `react-dom` render. It's this: http://airbnb.io/enzyme/docs/api/ShallowWrapper/render.html – ffxsam Jun 05 '16 at 03:13
  • 3
    Also, `shallow()` doesn't work for some reason.. the `focus` event triggers a method which tries to reference `this.refs.input`, which fails. But when I swap out `shallow` for `mount`, it works as expected. Mostly.. (one more issue with simulating ESC key) – ffxsam Jun 05 '16 at 03:43
  • I should have been more clear. I meant the render that looks like `render()`. I think your use case is more specialized than I thought; I see it deals note just with setting the input value but with focus and "canceling changes". It would be great if you could create a [plunker](http://plnkr.co/). – Tyler Collier Jun 05 '16 at 05:08
  • Hi, I got issue with default values such as `this.state = {inputNum: 10};`, and the **Received** will always be 1 though the **Expected** is 100 as set in `input.simulate('change', { target: { value: '100' } })`. Could anyone please help? – nambk Dec 26 '20 at 07:51
  • @nambk It'd be best to ask a new question. Perhaps it has something to do with the quotes around the `'100'`. I'm not sure about the discrepancy with 10 and 1. – Tyler Collier Dec 28 '20 at 19:39
51

With Enzyme 3, if you need to change an input value but don't need to fire the onChange function you can just do this (node property has been removed):

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

You can use wrapper.find('input').simulate("change", { target: { value: "foo" }}) to invoke onChange if you have a prop for that (ie, for controlled components).

bjudson
  • 4,073
  • 3
  • 29
  • 46
  • 9
    `NOTE: can only be called on a wrapper instance that is also the root instance.` - from the docs at http://airbnb.io/enzyme/docs/api/ShallowWrapper/instance.html – davidjb Dec 13 '17 at 06:34
  • 2
    `instance()` can be called on any child wrapper if it was rendered via `mount`. – Vladimir Chervanev Apr 06 '18 at 11:27
  • By the way, if you are using MUI it's the same, whith TextFields Select and more (you shoud call the "Native" elements and not the MUI ones) – Chen Peleg Nov 30 '21 at 19:05
46

Got it. (updated/improved version)

  it('cancels changes when user presses esc', done => {
    const wrapper = mount(<EditableText defaultValue="Hello" />);
    const input = wrapper.find('input');

    input.simulate('focus');
    input.simulate('change', { target: { value: 'Changed' } });
    input.simulate('keyDown', {
      which: 27,
      target: {
        blur() {
          // Needed since <EditableText /> calls target.blur()
          input.simulate('blur');
        },
      },
    });
    expect(input.get(0).value).to.equal('Hello');

    done();
  });
ffxsam
  • 26,428
  • 32
  • 94
  • 144
23

So lots of different opinions here. The only thing that worked for me was none of the above, it was using input.props().value. I hope that helps.

Y2H
  • 2,419
  • 1
  • 19
  • 37
7

I'm using react with TypeScript and the following worked for me

wrapper.find('input').getDOMNode<HTMLInputElement>().value = 'Hello';
wrapper.find('input').simulate('change');

Setting the value directly

wrapper.find('input').instance().value = 'Hello'` 

was causing me a compile warning.

Joseph King
  • 5,089
  • 1
  • 30
  • 37
  • I get getDOMNode() is not a function: this was updated in recent React: https://stackoverflow.com/questions/33031516/reactjs-finddomnode-and-getdomnode-are-not-functions – trainoasis Sep 15 '21 at 13:35
  • Joseph, simulate function is not updating/changing the input value. Do you have any idea? – Shruthi R Mar 29 '23 at 05:50
4

I am using create-react-app which comes with jest by default and enzyme 2.7.0.

This worked for me:

const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input')[index]; // where index is the position of the input field of interest
input.node.value = 'Change';
input.simulate('change', input);
done();
Felipe Augusto
  • 7,733
  • 10
  • 39
  • 73
erika_dike
  • 251
  • 4
  • 7
3

None of the above worked for me. This is what worked for me on Enzyme ^3.1.1:

input.instance().props.onChange(({ target: { value: '19:00' } }));

Here is the rest of the code for context:

const fakeHandleChangeValues = jest.fn();
  const fakeErrors = {
    errors: [{
      timePeriod: opHoursData[0].timePeriod,
      values: [{
        errorIndex: 2,
        errorTime: '19:00',
      }],
    }],
    state: true,
  };
const wrapper = mount(<AccessibleUI
    handleChangeValues={fakeHandleChangeValues}
    opHoursData={opHoursData}
    translations={translationsForRendering}
  />);
const input = wrapper.find('#input-2').at(0);
input.instance().props.onChange(({ target: { value: '19:00' } }));
expect(wrapper.state().error).toEqual(fakeErrors);
3

In case anyone is struggling, I found the following working for me

const wrapper = mount(<NewTask {...props} />); // component under test
const textField = wrapper.find(TextField);

textField.props().onChange({ target: { value: 'New Task 2' } })
textField.simulate('change');
// wrapper.update() didn't work for me, need to find element again

console.log(wrapper.find(TextField).props()); // New Task 2

It seems that you need to define what happens in the change event first and then simulate it (instead of simulating the change event with data)

2

This works for me using enzyme 2.4.1:

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

console.log(input.node.value);
SunshinyDoyle
  • 3,441
  • 1
  • 18
  • 21
  • 4
    When I started using Jest/enzyme I would often `console.log` an object and dig through (sub-)properties to get what I needed. Doing so, I often ended up using `.node` in some form, like you have. However, I don't recall seeing `.node` being mentioned in any of the official documentation, suggesting it could change/break between releases as it's not officially part of the publically-advertised API. Also, there often seem to be alternatives. e.g. `input.node.value` === `input.get(0).value`. So, `.node` might work, and I suspect that sometimes it will provide a good hack, but use with caution. – Andrew Willems Jan 26 '17 at 15:30
  • This is no longer a public method. – 0x777C Aug 14 '18 at 15:22
2

None of the solutions above worked for me because I was using Formik and I needed to mark the field "touched" along with changing the field value. Following code worked for me.

const emailField = orderPageWrapper.find('input[name="email"]')

emailField.simulate('focus')
emailField.simulate('change', { target: { value: 'test@example.com', name: 'email' } })
emailField.simulate('blur')
Farhan Haider
  • 1,244
  • 1
  • 13
  • 22
1

In my case i was using ref callbacks,

  <input id="usuario" className="form-control" placeholder="Usuario"
                                                       name="usuario" type="usuario"
                                                       onKeyUp={this._validateMail.bind(this)}
                                                       onChange={()=> this._validateMail()}
                                                       ref={(val) =>{ this._username = val}}
                                                    >

To obtain the value. So enzyme will not change the value of this._username.

So i had to:

login.node._username.value = "mario@com.com";
    user.simulate('change');
    expect(login.state('mailValid')).toBe(true);

To be able to set the value then call change . And then assert.

cabaji99
  • 1,305
  • 11
  • 9
1

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.

Anupam Maurya
  • 1,927
  • 22
  • 26
1

I solved in a very simple way:

  1. Set the value from props:
  const wrapper: ShallowWrapper = shallow(<ProfileViewClass name: 'Sample Name' />);
  1. Html code:
  <input type='text' defaultValue={props.name} className='edituser-name' />
  1. Access the attribute from wrapper.find(element).props().attribute-name:
  it('should render user name', () => {
    expect(wrapper.find('.edituser-name').props().defaultValue).toContain(props.name);
  });

Cheers

Daniel Santana
  • 1,493
  • 20
  • 19
0

This worked for me:

let wrapped = mount(<Component />);
expect(wrapped.find("input").get(0).props.value).toEqual("something");
Mahmoud Abd AL Kareem
  • 615
  • 2
  • 10
  • 23
0

Maybe my answer doesn't suit for this question. But i had a problem that value for field during testing wasn't changing and tested logic wasn't fired. In my case problem was that callback and value were pass as parameters for component. And just this simple fix helped me to trigger inner logic in tested component:

rendered.setProps({ zipCode: '1234567' });
alexis_dia
  • 156
  • 3
  • 13
-1

I use Wrapper's setValue[https://vue-test-utils.vuejs.org/api/wrapper/#setvalue-value] method to set value.

inputA = wrapper.findAll('input').at(0)
inputA.setValue('123456')
Calvin
  • 953
  • 1
  • 9
  • 16
-1

.simulate() doesn't work for me somehow, I got it working with just accessing the node.value without needing to call .simulate(); in your case:

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

// Get the value
console.log(input.node.value); // Hello

// Set the value
input.node.value = 'new value';

// Get the value
console.log(input.node.value); // new value

Hope this helps for others!

Jee Mok
  • 6,157
  • 8
  • 47
  • 80
  • Throws ``` 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. ``` – Davi Lima Apr 23 '19 at 18:18
  • 2
    @DaviLima for the newer version of Enzyme, instead of `.node` you should use `.instance()` or `.getDOMNode()`, depends if you used the result as a ReactElement or DOMComponent. – Jee Mok Apr 23 '19 at 23:56