Closed dijs closed 8 years ago
We have just been facing a similar issue.
We found an existing issue: https://github.com/airbnb/enzyme/issues/218
It seems the correct way would be
wrapper.find('select').simulate('change', {target { value : 'hello'}});
I tried that line using mount, I got the same result. No luck...
You would think this should work also. But it doesn't...
const wrapper = mount(<Component {...props} />);
expect(wrapper.find('[selected=true]').node.value).to.equal('goodbye');
wrapper.find('select').find('option').at(0).simulate('click');
expect(wrapper.find('[selected=true]').node.value).to.equal('hello');
I tried regular TestUtils also. Still no luck...
const form = TestUtils.renderIntoDocument(<Form {...props} />);
const select = TestUtils.findRenderedDOMComponentWithTag(form, 'select');
const node = ReactDOM.findDOMNode(select);
const options = TestUtils.findAllInRenderedTree(addressLocationForm, comp => {
return comp instanceof HTMLOptionElement;
});
expect(node.value).to.equal('goodbye');
TestUtils.Simulate.click(options[0]);
expect(node.value).to.equal('hello');
Turn's out selected is only for "initial" values on options.
Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option
So that was my issue, I was depending on selected to always be on the "selected" option.
@dijs Today we solving same issue and found it can be resolved with using render
instead of mount
const wrapper = render(<StartChatForm {...props} />);
expect(wrapper.find('option')).to.have.length(2);
expect(wrapper.find('select [selected]').val()).to.equal('key');
I am still unable to test select changes
I solved this issue like this. Big thanks to @sattaman
it("when simulating a change, select should update its value", () => {
const wrapper = setupMount();
wrapper.find('select').simulate('change',{target: { value : '100'}});
expect(wrapper.find('select').props().value).toBe("100");
});
That didn't work for me. @knuterik91 Can you share what the setupMount function does?
@fernandopasik hmm setupMount is a just a function that calls mount on my react component.
function setupMount(props = initProps) { return mount(<myReactComponent{...props}/>); }
what type of errors are you getting ?
Thanks, I wasn't using mount. Now it works
I have a component that keeps a ref to the <select>
and reads its selectedIndex
on a later event.
class Prompt extends Component {
handleSubmit = () => {
const { value } = this.dropdown.options[this.dropdown.selectedIndex]; // ::READ::
this.props.onSubmit(value);
};
render () {
return (
<form onSubmit={ this.handleSubmit }>
<select ref={ r => this.dropdown = r } defaultValue='b'>
<option value='a'>A</option>
<option value='b'>B</option>
<option value='c'>C</option>
</select>
<input type='submit' value='Submit' />
</form>
);
}
}
I was able to test it by mutating the select's underlying node
.
it('should select C and submit it', () => {
const onSubmit = sinon.stub();
const wrapper = enzyme.mount(<Prompt onSubmit={ onSubmit } />);
wrapper.find('select').node.selectedIndex = 2; // ::MUTATE::
wrapper.find('form').simulate('submit');
expect(onSubmit).to.have.been.calledWithExactly('c');
});
@NealEhardt an unrelated note; you want to do this.handleSubmit = this.handleSubmit.bind(this)
in the constructor and onSubmit={this.handleSubmit}
in render
rather than doing the bind in the render path; it's much more performant.
@ljharb You're right. I was going for brevity but we don't want to encourage bad practices. Updated to the new instance-variable hottness.
@NealEhardt no worries :-) altho using class variables like that is slightly less performant than using a prototype method that's merely bound per-instance (as opposed to being defined per-instance)
[[hi guys i am facing issue while writing test cases for selectfield in material-ui.... Can anyone fix it!
<SelectField id='sex' name='sex' floatingLabelText='Sex*' onChange={(evt, newIndex, value) => this.sexDidChange(evt, newIndex, value)} value={this.state.sex} errorText={this.state.errors.sex} >
{
genders.map((w, index) => <MenuItem key={index.id} label={w.name} value={w.id}>{w.name}</MenuItem>)
}
</SelectField>
I want to match items presnt in menuitem..
it('finding the items present in SelectField of sex', () => {
const wrapper = mount(<ReduxForm />,
{
context: {
muiTheme: getMuiTheme(),
},
childContextTypes: {
muiTheme: React.PropTypes.object.isRequired,
},
});
expect(wrapper.find('MenuItem').first().text()).to.be.equal('Male');
});
const component = shallow(
<ComponentWithCustomSelectComponentAsChild
id="1"
name="test"
/>,
);
component.find(ImportedCustomSelectComponent).simulate('select', 'test');
expect(component.state('selectHandlerValueThatChangesStateValue')).toEqual('test');
works for me
Why this issue is closed? Because someone write something what "works for him" and its not confirmed by anyone else?
@suwi if the OP of the issue has solved it, it's solved. Please open a new issue if you're still having problems.
None of the solutions presented here is working for me.
describe('to test <SelectMenu /> functionality', () => {
const mockFunc = jest.fn();
const options = ['a', 'b', 'c', 'd']
const wrapper = mount(<SelectMenu label="the-label"
options={options}
name="the-name"
action={mockFunc} />)
it('should call action prop with correct input value as arguments when the input is changed', () => {
wrapper.find('select').simulate('change', { target: { value: 'a' } });
expect(mockFunc.mock.calls[0][0].value).toEqual('a');
})
})
This is how i do it.
Hi Ive been struggling with this and cant seem to get it to work.
it('should select transmission',() =>{
const wrapper = mount(<Transmission/>);
const select = wrapper.find('select').first();
select.simulate('change',{target: {value:"manual"}});
expect(select.props().value).toEqual('manual');
});
const Transmission=(props) =>{ return(
<select name="transmission" onChange={props.handleChange}>
<option value="automatic">Automatic</option>
<option value="manual">Manual</option>
</select>
);
} export default Transmission;
Even if I remove the onChange method from the select, the error is the same:
Expected value to equal:
"manual"
Received:
undefined
Difference:
Comparing two different types of values. Expected string but received undefined.
Appreciate some help.
If you want your test to behave as a user interaction would, your test needs to set the value, then simulate the change event. Enzyme doesn't set the value when you simulate a change.
It's a bit surprising that Enzyme works this way, but I think it allows Enzyme's implementation to be simple and reliable.
We were able to observe a change event on a select box by simulating a change event on the option itself.
wrapper.find('select option[value="my-value"]').simulate('change')
I've tried all the examples in this issue and in the #218 issue too, no dice.
Here's my code:
const App = () => (
<select>
<option value="one">One</option>
<option value="two">Two</option>
<option value="three">Three</option>
</select>
);
const wrapper = mount(<App />);
wrapper.find("select").simulate("change", { target: { value: "two" } });
expect(wrapper.find("select").props().value).toBe("two");
The result is always undefined
. Have also tried shallow(<App />)
too.
Here's how I ended up testing select elements in my app:
I have a component that wraps a <select>
element:
// code is simplified for clarity
class MyComponent extends React.Component {
render() {
return (
<div>
<select onChange={this.handleChange} value={this.props.value}>
{this.renderOptions()}
</select>
</div>
);
}
}
This component depends not only the value
of the select
element, but also on its options
(HTMLSelectElement.options
) and its selectedIndex
(HTMLSelectElement.selectedIndex
) for various reasons.
handleChange = e => {
const { value, selectedIndex, options } = e.target;
}
So while the some of solutions provided in the comments above work fine for simpler scenarios:
wrapper.find('select').simulate('change', { target: { value: 'some_value' } });
That did not work for me since I had to provide options
(as HTMLOptionsCollection
... ouch!) and selectedIndex
to the mock event object as the second argument of simulate
.
The trick that did it for me was to access and manipulate the DOM node of the select element and pass it back to .simulate
, as the target of the mock event object, to let it do its job.
I'm not sure how reliable this solution is, but I wanted to share it here :)
const handleOnChange = jest.fn();
const wrapper = mount(
<MyComponent onChange={handleOnChange} {...mockProps} />
);
const select = wrapper.find('select');
const selectDOMNode = select.getDOMNode(); // or .instance()
selectDOMNode.value = 'some_value';
// passing back the entire DOM node as the mock event target
select.simulate('change', { target: selectDOMNode });
expect(handleOnChange).toHaveBeenCalledWith(/* whatever MyComponent.props.onChange is supposed to receive */);
Fully understanding that refs are to be avoided whenever possible, this is the only solution I found to my problem.
//element constructor
constructor(props) {
super(props)
this.state = {user: {role: ''}}
this.roleRef = React.createRef()
}
//in render method:
<select id="role" value={this.state.user.role} onChange={this.handleChange} ref={this.roleRef}>
//...
</select>
//in test
it('should change value to match user role', done => {
var role = mount(<Form />).find('#role')
const roles = require('../assets/roles.json')
roles.forEach(element => {
role.props().onChange({ target: { id: 'role', value: element.title } })
expect(role.get(0).ref.current.value).toBe(element.title)
}
done()
}
UPDATE: I managed to get rid of refs by manually updating the wrapper.
roles.forEach(element => {
role.props().onChange({ target: { id: 'role', value: element.title } })
//update and reassign variables
wrapper = wrapper.update()
role = wrapper.find('#role')
expect(role.props().value).toBe(element.title)
}
ditto @mikefrancis. Trying to simulate a simple select action on a simple dropdown is proving impossible.
const wrapper = mount(<PredicateSelection/>);
// wrapper.find("select").simulate("change",
// {target: {value : "user_email"} }
// )
wrapper.find("select option[value='user_email']").simulate("change")
})
Above, the option that is commented out does what everybody else suggests. The second follows a pattern where we make the selected value. Both throw this error:
TypeError: Cannot read property '0' of undefined
It is strange
Don't use simulate
. If you want to invoke the onChange prop, do .prop('onChange')()
.
@ljharb forcing an explicit .prop()
invocation creates a tight coupling between the test and the implementation. Much nicer would be to be able to simulate clicking on the element in the DOM that results in an internal state change, which could then be verified by seeing the resulting update in the DOM. This way the tests simulate more accurately what a user would do, and it reduces the coupling between the test and the implementation, which makes refactoring easier over time.
@citypaul that's basically all that simulate
does. The simulation you want would be great, but neither enzyme's nor react-test-renderer's simulate
has ever actually done that.
enzyme is for unit tests, not for browser-level integration tests like you're describing.
@ljharb @citypaul Sooo... based on this:
enzyme is for unit tests, not for browser-level integration tests like you're describing.
If I wanted to simulate a click event, followed by a life cycle event, a component conditional render based on what the user chose in the drop down, and a form submission, I shouldn't be using Enzyme?
Sorry if these are silly questions. I just got started using Enzyme, and it's been fun until this wall; but, if it's the wrong tool for the job, I should be looking for that tool instead.
@mcshakes what are you trying to test with that, though? You don't need to test React, or the browser - if the onClick function is present, and does the right thing when invoked, your test is done.
Include select name in target:
wrapper.find('select[name="mySelect"]').simulate('change', {
target: {
name: 'mySelect',
value: '1',
},
})
expect(wrapper.find('select[name="mySelect"]').prop('value')).toEqual('1')
@ljharb Hello,
What if you are trying to test a form with conditional logic which hides/shows fields in the form? Should Enzyme be used in this case?
@596050 absolutely. shallow render, and assert the presence or absence of various other components.
Does anyone have an easy way to simulate and test select element changes?
This is what I am doing, and it is not working at all...
By the way the "option" elements that the component renders in the browser are not able to be selected using enzyme for some reason.