Comments (74)
Not sure if this helps, but personally I've been using:
wrapper.find('input').simulate('change', {target: {value: 'My new value'}});
to test onChange
methods and this has worked quite well for me.
from enzyme.
i just need to set value to the input
How about below?
const wrapper = mount(<input />);
wrapper.find('input').node.value = 'Test';
from enzyme.
I found that I had to re-find the input
after my change
simulation.
const input = mounted.find('input');
expect(input.prop('value')).toBe('Initial'); // PASS
input.simulate('change', { target: { value: 'Changed' } });
// whuuuut?
expect(input.prop('value')).toBe('Changed'); // FAIL...
expect(input.prop('value')).toBe('Initial'); // PASS, seriously?!
// but...:
expect(mounted.find('input').prop('value')).toBe('Changed'); // PASS!
from enzyme.
@jackfranklin the purpose of enzyme is mostly to assert things about rendered react components and their behavior.... by manipulating input values you are manipulating the behavior of the component in an unpredictable way...
Some things you can do:
- if the input is getting rendered with a prop or state variable, try using the
.setProps()
or.setState()
methods of enzyme. - try simulating keypresses using
.simulate('keydown', { which: 'a' })
or similar
from enzyme.
@lelandrichardson understood, thanks. I quite like the idea of simulating key presses as a replacement. Would you consider a method to help input lots of keypresses? Eg .simulateKeyPresses('hello')
(or whatever) that would call simulate with keydown
a bunch of times.
from enzyme.
It's not working for textarea element
from enzyme.
Anyone agrees that @levibuzolic solution should be added to documentation? One has to dig trough all the comments here, to find a how to properly simulate input change .. Something this trivial should feel like more fun to accomplish.
from enzyme.
@lelandrichardson I had a go at this and ended up with something like this:
simulateKeyPresses(characters, ...args) {
for(let i = 0; i < characters.length; i++) {
this.simulate('keyPress', extend({
which: characters.charCodeAt(i),
key: characters[i],
keyCode: characters.charCodeAt(i)
}, args));
}
}
This works in terms of calling an onKeyPress
handler if you call it correctly, however the value doesn't actually change, and I'm not so sure as to why.
Additionally, the React test util docs do suggest you should set value
and then simulate a change
event:
// <input ref="input" />
var node = this.refs.input;
node.value = 'giraffe'
ReactTestUtils.Simulate.change(node);
So I would come back again to the point that it would be nice to have a syntactic method for setting the input value and triggering a change - although I can see why you want to avoid that. I'd love to come up with a simulateKeyPresses
similar method that works correctly - any ideas you have would be really cool.
Thanks for a great library :)
from enzyme.
For those having problems with onChangeText
, the changeText
event worked for me.
rowInput.simulate('changeText', 'abc')
from enzyme.
In regards to the original topic (not specific to keypress events) this does seem unintuitive. I just started using enzyme today so maybe there is a easier way of writing this out...
Currently if I wanted to test some event on a input, let's say change event, it looks like this:
input.get(0).value = 'text' // have to get the node...
input.first().simulate('change') // have to use the wrapper...
Versus the TestUtils way:
node.value = str
TestUtils.Simulate.change(node)
The unintuitive part is using 2 different types to do 1 action. Where as with the React TestUtils you're only having to get and deal with the node.
Again new to this, so is there a more streamlined way of doing this common testing task? Right now I'm doing it the TestUtils way.
from enzyme.
@george-norris-salesforce this wont actually set the value of the text filed, instead it will just trigger your onChange
method. If you actually need your DOM to be updated and your onChange
doesn't handle this for you then you'll need to actually set the value programatically via wrapper.find('#my-input').node.value = 'abc';
from enzyme.
I feel like a better approach here would be making a generic and entirely separate library that provided an API, that generated a stream of simulated browser events. Then, enzyme could simply use that library, and could delegate any API decisions to that other module. That other module would then be useful for a number of other projects.
I don't know what that API would look like, but I think solving it in a generic way, for all events and element types, is a hard enough problem that it shouldn't be attempted directly inside a larger project.
from enzyme.
I think that's an interesting idea! The fact that simulating typing is so difficult has been something that's bothered me...
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1);
passwordInput.instance().value = 'y';
passwordInput.simulate('change');
component.find('form').first().simulate('submit');
from enzyme.
I managed to simulate setting a value for textarea.
wrapper.find("textarea#message").element.value = "a value" wrapper.find("textarea#message").trigger("input")
from enzyme.
It also doesn't work for components using currentTarget instead of target, (you cannot pass custom data to currentTarget the way you do it for target, currentTarget will ignore it)
from enzyme.
not working for me..
const input = wrapper.find('#my-input');
input.simulate('change',
{ target: { value: 'abc' } }
);
const val = input.node.value;
//val is ''
http://stackoverflow.com/questions/41732318/test-setting-text-value-with-react-and-enzyme
from enzyme.
Indeed; in v3, all sub-wrappers must be re-found from the root to see updates.
from enzyme.
this works for me:
wrapper.find('#input-value').simulate('change', { target: { '10' } })
then, to check if the value was changed:
expect(wrapper.find('#input-value').props().value).toEqual('10')
from enzyme.
wrapper.find('...').simulate('change', {target: {value: 'My new value'}});
triggers onChange
, but not onChangeText
for some reason (I'm using React Native 0.42.3).
from enzyme.
For anyone trying to update the caret position along with the value of an input:
const input = wrapper.find('input')
const position = value.length
input.simulate('change', { target: { value } })
input.getDOMNode().setSelectionRange(position, position)
input.simulate('select')
from enzyme.
@nemoDreamer I ran into this as well, thank you for the fix! I think it's happening because const input
is a stale reference, likely because the onChange
caused a re-render in your component. At least, that was what was happening in my component.
from enzyme.
textarea
works for me, @eduardonunesp.
I didn't think @levibuzolic's solution was working, but I had to swap mockStore
with createStore
to work for my situation.
import { reducer as formReducer } from 'redux-form'
...
let commentCreate
let form
beforeEach(() => {
commentCreate = sinon.spy()
form = mount(
- <Provider store={mockStore({ form: formReducer })}>
+ <Provider store={createStore(combineReducers({ form: formReducer }))}>
<Container commentCreate={commentCreate} />
</Provider>
).find('form')
})
it('valid and calls commentCreate', () => {
const textarea = form.find('textarea').first()
textarea.simulate('change', { target: { value: 'foo' } })
form.simulate('submit')
expect(commentCreate.callCount).to.equal(1)
})
it('invalid so does not call commentCreate', () => {
form.simulate('submit')
expect(commentCreate.callCount).to.equal(0)
})
I used Redux-Form Test to troubleshoot.
from enzyme.
For anyone who failed to get the value right with wrapper.find('input').simulate('change', {target: {value: 'My new value'}});
Here is what I did and it is working
const SomeInputComponent = ({onChangeCallback}) => {
const inputRef = useRef(null);
const handleOnChange = useCallback(()=>{
onChangeCallback(inputRef.current.value)
}, [])
return <div><input ref={inputRef} onChange={handleOnChange} /></div>
}
it('Should change the value ', () => {
let value = '';
const wrapper = mount(
<SomeInputComponent
onChangeCallback={res => {
value = res;
}}
/>,
);
const $input = wrapper.find('input');
$input.at(0).instance().value = 'Changed';
$input.simulate('change');
expect(value).toEqual('Changed');
});
from enzyme.
@jackfranklin don't even reference things by HTML tags. This is actually very bad to do from your tests even though 80% people do it, the 20% of devs who have tested for years know better, and we know how to keep tests less brittle. That is... by searching on 'input' or other tags, you're coupling your tests to the implementation (the DOM). Don't do that...ever. Seriously.
Just do this:
passwordInput = loginForm.find('.ft-password')
passwordInput.value='111'
('ft' stands for 'feature') but that's just a convention I use, you can use whatever you want.
and just add a cssClass to wherever your field is:
<FormControl
bsSize="small"
className="ft-password"
componentClass="input"
onChange={this.props.handlePasswordInput}
placeholder="Enter password"
style={{ width: 300}}
type="password"
/>
(there I'm using a React bootstrap component, you might be using whatever...point in case is I don't couple my tests to 'input' or 'FormControl' or anything like that. My tests are completely ignorant of the implementation always by using feature css markers and searching on those. Plus you can reuse those markers in Casper and other tests in the future).
That will save you from headaches in many ways if you just use css markers like this instead of trying to reference and couple your tests to DOM element names. This is common to do with good testers, we don't find() on HTML elements, we always use feature markers. This is a very common practice done by seasoned TDD'ists.
Places like CarGurus.com do this all the time, just look at their source, you'll see markers everywhere and that's why they're there.
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1); passwordInput.instance().value = 'y'; passwordInput.simulate('change'); component.find('form').first().simulate('submit');
this one actually worked for me;
All other solutions in this thread gave different errors of the no function x of undefined
sort (including and especially the doc's way with simulate('change', { target: /* ... */ })
)
from enzyme.
This should be added to docs!
from enzyme.
The solution I found for having a input with ref
to get the value instead of onChange
, for example:
<input ref={(value)=>{this._email = value}} >
is to use .instance().value
to add a value to the input, submitting the form if you want, and testing the value, like so:
wrapped.find('input[type="email"]').instance().value = "test";
expect(wrapped.find('input[type="email"]').instance().value).toEqual("test");
from enzyme.
@CWSites Sorry, my bad. The correct is:
wrapper.find('#input-value').simulate('change', { target: '10'})
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1); passwordInput.instance().value = 'y'; passwordInput.simulate('change'); component.find('form').first().simulate('submit');
I currently have tested this approach and works flawlessly
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1); passwordInput.instance().value = 'y'; passwordInput.simulate('change'); component.find('form').first().simulate('submit');
This is the only thing that seems to work at all with a password input type -- thank you!
from enzyme.
@levibuzolic Didn't seem to work for me. I'm getting Can't add property value, object is not extensible
.
edit: All good, I was shallow rendering that particular test, mounting fixed it π
from enzyme.
@abdennour I'm using the onBlur
event in my forms, so here's how I'm setting input values with node.value
and then making sure my methods get called.
const component = mount(<MyComponent {...props />);
const input = component.find("input[type='text']").first();
input.node.value = "something";
input.simulate("blur");
component.update();
expect(props.myUpdateMethod.mock.calls[0]).toEqual([{
// my update method arguments
}]);
from enzyme.
@teh-username I get: TypeError: ReactWrapper::simulate() event 'changeText' does not exist
from enzyme.
@anupammaurya can you create a codesandbox ?
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1); passwordInput.instance().value = 'y'; passwordInput.simulate('change'); component.find('form').first().simulate('submit');I currently have tested this approach and works flawlessly
I tried this out and typescript would complain about value not existing in instance()
This worked for me:
const attribute = document.createAttribute("value");
attribute.value = "[email protected]";
const emailInput = wrapper.find('input').at(0);
emailInput.getDOMNode().setAttributeNode(attribute);
emailInput.simulate('change');
wrapper.update();
from enzyme.
I have tried all the methods mentioned above but none of them worked for me.
from enzyme.
OK - I might have a play and ping a PR if I get anywhere? :)
from enzyme.
Thanks so much for these examples. It'd be great if you could add .value(...)
-- @jackfranklin have you been working on this?
from enzyme.
@ljharb i agree.
we felt the same pain and ended up building a capybara-like library that takes care of interactions
all: any thoughts on the scope/api/wishlist of such project will be highly appreciated, as i'd love to open source it
from enzyme.
@vesln have you made any progress on a capybara like library?
from enzyme.
@SpencerCDixon i've built something that we use internally, but truth to be told, it's far away from ready.
i was really hoping for some input from the community on potential API and feature set before i open source it
if you have any suggestions/ideas please let me know
from enzyme.
gotcha. Yeah I havn't really spent much time thinking about it but I think it would be cool to mimic capybara's API as much as possible so the library would be very intuitive for people to learn who are coming from a Rails/Ruby background.
I'd definitely be down to put some time into it/help you work on it if you were interested in open sourcing what you did for your company.
from enzyme.
@SpencerCDixon sounds awesome, let's join forces! i will ping u when i have something up, even if it's only the initial boilerplate. then we can discuss the api/features publicly and make it happen
from enzyme.
π sounds great!
from enzyme.
@levibuzolic your method worked great for me.
from enzyme.
@hnry Are you still using the TestUtils way?
from enzyme.
I feel like this could be related to the bug mentioned in this blog post:
http://ianmcnally.me/blog/2015/7/22/react-component-change-testing-works
So that if that was fixed enzyme would be as intuitive.
from enzyme.
@levibuzolic Is there a way if i could only set value , i dont have any Onchange or on Blur methods , its just
i just need to set value to the input
from enzyme.
Since my component was already an DOM input node, I simulated using the onInput
event:
component.simulate('input', { target: { value: 'Type Sample' } });
from enzyme.
Does not work at all .. Neither input nor textarea. . Neither "simulate" nor "node.value="..
from enzyme.
I wouldn't recommend using simulate at all - i'd suggest instead getting the prop you want and invoking it directly.
from enzyme.
Hereby I confirm that neither 'node' nor 'instance()' is working. Can anybody please help?
My test code:
const wrapper = mount(BeginEndDayPicker(propsBegin));
const input = wrapper.find('input');
expect(input).toHaveLength(2);
wrapper.html() =
<div><div class="row form-group"><div class="col col-md-4"><label for="begin" class=" form-control-label">Begin</label></div><div class="col-12 col-md-8"><div class="DayPickerInput"><input placeholder="Example: 2017-01-01" required="" value="2017-01-01"></div></div></div><div class="row form-group"><div class="col col-md-4"><label for="end" class=" form-control-label">End</label></div><div class="col-12 col-md-8"><div class="DayPickerInput"><input placeholder="Example: 2017-12-31" required="" value="2017-12-31"></div></div></div></div>
I tried with
input.get(0).value = '123';
but got error:
TypeError: Can't add property value, object is not extensible
Then I tried
input.get(0).instance().value = '123';
but got error:
TypeError: input.get(...).instance is not a function
Then I tried:
input.get(0).node.value = '123';
but got error:
TypeError: Cannot set property 'value' of undefined
Finally I tried:
input.first().getDOMNode().nodeValue = '123';
but the expected change function is not called:
expect(handleBeginChange).toBeCalledWith('123');
expect(jest.fn()).toBeCalledWith(expected)
Expected mock function to have been called with:
["123"]
But it was not called.
from enzyme.
.get
returns a raw node; Try .at(0).instance()
?
Either way though, changing the value will never emit a "change" event; there's no way to do that automatically. You don't need to test that React sets up handleBeginChange
properly; all you need to do is assert that that function ends up as the onChange
prop on the proper element, and you can just trust that React wires it up properly.
from enzyme.
I had same issue as above - onChange
triggers but doesn't fire the onChangeText
had to mock out the component using jest.
jest.mock('TextInput', () => {
const RealComponent = require.requireActual('TextInput');
const React = require('react');
class TextInput extends React.Component {
render() {
return React.createElement('TextInput', {
...this.props,
onChange: this.props.onChangeText,
}, this.props.children);
}
}
TextInput.propTypes = RealComponent.propTypes;
return TextInput;
});
Bit hacky but seems to work for now.
from enzyme.
Hello, everyone.
In the end, any working solution?
Simulating various interactions with the components, including onChange, seems quite essential.
from enzyme.
I just wanted to come here to say that for anyone trying to do events other than onChange events, the target value trick still works. I was able to combine two tricks on this thread to get what I need.
enzymeWrapper.find('#myId').first()
.simulate('keydown', {keyCode: 13, target: {value:'3537727,3537725,3537723'}});
from enzyme.
@jsonmaur your solution worked best π
from enzyme.
I tried almost all suggested ways but nothing seems to work for 'textarea'.
<textarea type="text" placeholder="A meaningful text." maxlength="400" role="textbox"></textarea>
Did anyone have any success with simulating (setting) value for textarea?
from enzyme.
It seems like a number of people have working solutions.
@jackfranklin happy to reopen if this is still an issue for you on latest enzyme.
from enzyme.
@jsonmaur, how did you clear the mock on the afterEach / afterAll block?
from enzyme.
@yuritoledo I get Unexpected token
on the closing }
immediately after '10'
@timbroder, your version works however I'm unable to expect
against the value prior to the change. How did you accomplish that?
from enzyme.
here is my code..
const input = MobileNumberComponent.find('input')
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.
from enzyme.
@CWSites I haven't tried expecting before changing it. The value at that point is a known assumption from the test setup
from enzyme.
@timbroder what I'm running into is that it only updates the value of instance()
and that value is not available to my function that is being called onChange
. I still haven't been able to accomplish adjusting the value of an input. Currently I'm serving the value from state so I have to manually alter the state and use that to control the value of my input.
it('validateEmail', () => {
const wrapper = mount(<Login history={[]} />);
const emailUpdated = jest.spyOn(wrapper.instance(), 'emailUpdated');
const validateEmail = jest.spyOn(wrapper.instance(), 'validateEmail');
const loginButton = wrapper.find('Button');
const emailInput = wrapper.find('Input');
wrapper.setState({ email: 'invalid email' });
// simulate login with invalid email
loginButton.simulate('click');
expect(validateEmail).toHaveBeenCalledTimes(1);
expect(wrapper.state().invalid).toEqual(true);
// since I can't update the value directly, update state directly
wrapper.setState({ email: '[email protected]' });
// simulate login with valid email
emailInput.simulate('change', emailInput);
expect(emailUpdated).toHaveBeenCalledTimes(1);
expect(wrapper.state().invalid).toEqual(false);
expect(wrapper.state().email).toEqual('[email protected]');
});
These are the different ways that I've tried to update the input, but none of them pass the value to the function which is called onChange.
// value is not passed to `emailUpdated`
emailInput.instance().value = '[email protected]';
expect(emailInput.instance().value).toEqual('[email protected]'); // returns '[email protected]'
expect(emailInput.props().value).toEqual('[email protected]'); // returns ''
expect(emailInput.value).toEqual('[email protected]'); // returns undefined
// value is not passed to `emailUpdated`
emailInput.props().value = '[email protected]';
expect(emailInput.props().value).toEqual('[email protected]'); // returns '[email protected]'
expect(emailInput.value).toEqual('[email protected]'); // returns undefined
// value is not passed to `emailUpdated`
emailInput.value = '[email protected]';
expect(emailInput.value).toEqual('[email protected]'); // returns '[email protected]'
from enzyme.
This is what worked for me for simulating text entry into a textarea and testing for a secondary component that was loaded after text entry.
RTFM simulate. Also note that simulate is on the wrapper (the result of find
), not on the dom element (which would come from find('myThing').getDOMNode
).
it('mounts and comment text entry triggers suggestion menu open with proper results', () => {
const comment = mount(<Comment />);
// Create a fake event with the values needed by handleOnChange
const inputEvent = {persist: () => {}, target: { value: '@pdixon1'}};
comment.find('textarea#commentTextArea').simulate('change', inputEvent);
// Make sure to wait before testing for a dom result.
setTimeout(function(){
const suggestions = comment.find('#nameSuggestionsMenuList');
expect(suggestions.length).toBeGreaterThan(0);
expect(suggestions[0].id).toEqual('pdixon1');
}, 1000);
});
from enzyme.
This is working for me to test filling out the password field on a login form
const passwordInput = component.find('input').at(1); passwordInput.instance().value = 'y'; passwordInput.simulate('change'); component.find('form').first().simulate('submit');
This is the only thing that worked for me on a controlled input. Thanks!
from enzyme.
Not sure if this helps, but personally I've been using:
wrapper.find('input').simulate('change', {target: {, value: 'My new value'}});to test
onChange
methods and this has worked quite well for me.
I've been using this too, but I want to ask that what if I'm passing data in the onChange like this
onChange={e => this.myOnChange(e, data)}
Now, I'm confused about how can I pass data in the onChange on the test?
from enzyme.
@Puneet1796 that depends on where data
comes from. You can't alter that from just having access to the onChange
prop.
from enzyme.
@ljharb How does it matter?, According to the documentation simulate on mounted components can't let data pass through it, now here what enzyme fails to test the onChange.
from enzyme.
@Puneet1796 i'm not sure what you mean - it matters because the way the JS language works prevents any interception of data
there.
from enzyme.
@ljharb I have an array of values and I'm iterating over each value and renders a component everytime, say list item, but I want to pass index in the change so that I'll know which list item is updated. But in the case of testing, I found out that there's no way mentioned in the documentation to pass the value in onChange
via simulate.
The value that I want to pass is the index of that particular list item.
from enzyme.
Based on the code you passed, it's impossible via any means.
from enzyme.
What i had to do to update the value of a form field that was only referenced with a ref.
const inputField = wrapper.find('input[type="text"]');
inputField.getDOMNode().value = "my updated value";
inputField.simulate('whatever you are listening for');
from enzyme.
Related Issues (20)
- enzyme-adapter-react-18 HOT 2
- Component is not re-rendered with updated states HOT 5
- Cheerio 1.0.0-rc.11 no longer support deep imports HOT 3
- enzyme crash since Cheerio 1.0.0-rc.11 release HOT 5
- CSS selectors match component props rather than rendered DOM HOT 6
- Cannot read property 'child' of undefined on React 16 + enzyme-adapter-react-16 HOT 1
- Cannot read property 'child' of undefined enzyme-adapter-react-16 and react 17.0.2 HOT 3
- "TypeError: Cannot read properties of undefined (reading 'current')" in mount API HOT 7
- Method βpropsβ is only meant to be run on a single node. 0 found instead. HOT 1
- How to test the form which is rendered based on props in class component in react.js ? HOT 3
- How to test the state values which are setting the state from local storage inside componentDidMount in enzyme using reactjs ? HOT 5
- Function `mount` does not mock proper data HOT 5
- [email protected] does not include latest changes as per master HOT 5
- Document `getElement(s)` for full DOM rendering HOT 3
- TypeError: window.require is not a function HOT 5
- Does Enzyme support React 18.0.2 ? HOT 1
- Is this library 'dead'? HOT 5
- Explain how the synchronization works HOT 1
- Mount and simulate are failing after changing to Node 16 HOT 10
- enzyme-adapter-react-18 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from enzyme.