Enzyme - How to access and set <input> value?
Asked Answered
M

17

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.

Montserrat answered 13/5, 2016 at 21:43 Comment(0)
M
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();
  });
Montserrat answered 13/5, 2016 at 23:51 Comment(4)
Curious how this works for you. We're using PhantomJS and mount() does not insert components into the DOM. So, they can't receive focus. We have to add a DOM element and use the context option for mount()Copyread
@Copyread I actually started using Jest instead of Enzyme. Highly recommended!Montserrat
@Montserrat : input.get(0).value always displays "undefined"Duntson
@Duntson try input.prop('value')Semang
W
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.

Warrick answered 26/5, 2016 at 5:8 Comment(5)
The input.render() is not react-dom render. It's this: airbnb.io/enzyme/docs/api/ShallowWrapper/render.htmlMontserrat
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)Montserrat
I should have been more clear. I meant the render that looks like render(<EditableText defaultValue="Hello" />). 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.Warrick
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?Sidwel
@Sidwel 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.Warrick
H
52

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).

Hyperaesthesia answered 6/10, 2017 at 17:35 Comment(3)
NOTE: can only be called on a wrapper instance that is also the root instance. - from the docs at airbnb.io/enzyme/docs/api/ShallowWrapper/instance.htmlOld
instance() can be called on any child wrapper if it was rendered via mount.My
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)Purifoy
M
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();
  });
Montserrat answered 13/5, 2016 at 23:51 Comment(4)
Curious how this works for you. We're using PhantomJS and mount() does not insert components into the DOM. So, they can't receive focus. We have to add a DOM element and use the context option for mount()Copyread
@Copyread I actually started using Jest instead of Enzyme. Highly recommended!Montserrat
@Montserrat : input.get(0).value always displays "undefined"Duntson
@Duntson try input.prop('value')Semang
M
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.

Monaxial answered 16/11, 2017 at 12:14 Comment(2)
This is the only answer which allowed me to interrogate the input's value.Penicillin
Of note, you can also use: input.prop('value') if you know the name of your prop key.Periodontal
I
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.

Insolvable answered 8/12, 2019 at 17:58 Comment(2)
I get getDOMNode() is not a function: this was updated in recent React: #33032016Bara
Joseph, simulate function is not updating/changing the input value. Do you have any idea?Keeton
M
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();
Mali answered 15/1, 2017 at 9:13 Comment(0)
B
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);
Barnebas answered 19/12, 2017 at 0:11 Comment(0)
L
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)

Lipetsk answered 11/9, 2019 at 17:20 Comment(1)
This worked for me. Although I had to put onChange event in act().Scaife
I
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);
Ideograph answered 25/10, 2016 at 13:29 Comment(2)
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.Forestation
This is no longer a public method.Jitterbug
F
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: '[email protected]', name: 'email' } })
emailField.simulate('blur')
Federalize answered 9/11, 2020 at 5:16 Comment(0)
O
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 = "[email protected]";
    user.simulate('change');
    expect(login.state('mailValid')).toBe(true);

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

O answered 17/10, 2016 at 23:16 Comment(0)
C
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.

Crooked answered 2/1, 2019 at 6:35 Comment(0)
A
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

Alda answered 21/10, 2020 at 14:43 Comment(0)
W
0

This worked for me:

let wrapped = mount(<Component />);
expect(wrapped.find("input").get(0).props.value).toEqual("something");
Wisteria answered 3/7, 2019 at 15:3 Comment(0)
R
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' });
Rica answered 16/3, 2023 at 7:28 Comment(0)
A
-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')
Armyworm answered 26/6, 2018 at 11:14 Comment(0)
S
-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!

Skindive answered 31/10, 2018 at 2:49 Comment(2)
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. ```Wallin
@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.Skindive

© 2022 - 2024 — McMap. All rights reserved.