How i can simulate 'change' event on react-select comonent?
wrapper.find('Select').simulate('change', { target: { value: 'text' }});
don't trigger change event
wrapper.props().onChange(event) working well
any ideas?
is this question about full DOM rendering or shallow rendering?
@nfcampos it's doesn't work for both cases
@idoo can you share your test case? If wrapper.props().onChange(event) works then it should work with shallow since all simulate does with a shallow wrapper is map the event name the event handler in props (so 'change' maps to props.onChange)
This is also potentially an issue with react-select. It might not actually be registering change events the way you expect it to.
@Aweary
it('call callback when value is changed', () => {
let callback = sinon.spy();
let props = extend({ onValueChanged: callback }, BASIC_PROPS);
const wrapper = mount(createElement(SelectInput, props));
wrapper.find('.Select-input input').simulate('change');
expect(callback.called).to.be.true; //—> false ˘o˘
});
@idoo so with shallow I was able to get a passing test using react-select. You can see it here. enzyme-test-repo/tree/issue-#400. It seems that your find query is not accurately querying the node that the change event should be simulated on. If you just use shallow and query for the <Select/> component you can easily test that the onChange prop is being called.
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]
export default class ReactSelectTestComponent extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event) {
if (this.props.onChange) {
this.props.onChange(event)
}
}
render() {
return (
<div>
<h1>Hello, world</h1>
<Select
name='test'
value='one'
options={options}
onChange={this.onChange}
/>
</div>
)
}
}
describe('react-select', () => {
it('should call onChange with shallow', () => {
const onChange = sinon.spy();
const wrapper = shallow(<ReactSelectTestComponent onChange={onChange}/>);
const selectWrapper = wrapper.find('Select');
selectWrapper.simulate('change');
expect(onChange.called).to.be.true;
});
})
As for mount it seems likely that react-select isn't actually registering a change event on the input your querying, I'd have to look at the source more but it doesn't seem like they actually register an onChange handler with their DOM nodes.
If you look at the test files for react-select you can see they are using a specific node to test change events:
var typeSearchText = (text) => {
TestUtils.Simulate.change(searchInputNode, { target: { value: text } });
};
Where searchInputNode seems to be defined as:
var searchInstance = ReactDOM.findDOMNode(instance.refs.input);
searchInputNode = null;
if (searchInstance) {
searchInputNode = searchInstance.querySelector('input');
if (searchInputNode) {
TestUtils.Simulate.focus(searchInputNode);
}
So you may want to follow that as an example of testing change events with mount, though shallow does test the onChange prop just fine.
@idoo I'm going to close this since there doesn't seem to be an issue with enzyme that is actionable, but feel free to keep discussion any issue your having here 👍
Had a similar issue trying to use Select.Async ..Thanks to Aweary's post I could achieve that in Enzyme
const wrapper = mount(< SomeComponentUsingSelectAsync onChange = {onChange} />);
const select = wrapper.find(Select.Async);
select.find('input').simulate('change', { target: {value:'some value'} }); ```
EDIT: rework for the Select.Async
Hi all, I put it to work with the following code (I hope I don't forget any portion of the code in the copy paste :-p ) :
import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import SearchableSelect from '../../../components/Form/SearchableSelect';
import Select from 'react-select';
describe('<SearchableSelect /> component', () => {
const input = {
value: undefined,
onChange: sinon.spy(),
};
const options = [
{ label: 'Tic', value: 'Tac' },
{ label: 'Timon', value: 'Pumbaa' },
];
const callback = sinon.spy();
let wrapper;
let select;
beforeEach(() => {
input.onChange.reset();
callback.reset();
});
context('with a `static` fetcher', () => { // <Select />
const props = { input, callback, options };
before(() => {
wrapper = mount(<SearchableSelect {...props} />);
select = wrapper.find(Select);
});
it('should call input.onChange and the callback on Select changes', () => {
const selectInput = select.find('input');
selectInput.simulate('change', { target: { value: options[0].value } });
selectInput.simulate('keyDown', { keyCode: 9, key: 'Tab' });
sinon.assert.callOrder(props.input.onChange, props.callback);
});
});
context('with an async fetcher', () => {
const promise = Promise.resolve({ options: [options[1]] });
const loadOptions = sinon.stub().withArgs(options[1].value).returns(promise);
const props = {
callback,
input,
loadOptions,
};
before(() => {
wrapper = mount(<SearchableSelect {...props} />);
select = wrapper.find(Select); // Select is a child component of Async
});
it('should call input.onChange and the callback on Select changes', () => {
// https://github.com/JedWatson/react-select/blob/8387e607666cca6dbfbf5860e53188b319cc43d5/test/Async-test.js#L33
// https://github.com/JedWatson/react-select/blob/8387e607666cca6dbfbf5860e53188b319cc43d5/test/Async-test.js#L129
select.props().onInputChange(options[1].value); // simulate a search and launch loadOptions
const inp = select.find('input'); // find the hidden input to simulate an option selection
inp.simulate('change', { target: { value: options[1].value } });
inp.simulate('keyDown', { keyCode: 9, key: 'Tab' }); // validate the selection
sinon.assert.callOrder(props.input.onChange, props.callback);
});
});
});
The key was to add an keyDown simulation to validate the change event
The simulate method called on a select tag did not update the value of the select tag, but it did call the event handler function.
To make my Jest test pass, I programmatically simulated changing the value of the select item. This took a lot of console logging to figure out the correct sequence of keys to use. In my JSX, the select tag is given the attribute ref="my-select"
const mySelect = myMountedComponent.find(some_selector_pattern)
// Now change the value of select drop down index
myMountedComponent['node']['refs']['my-select']['selectedIndex'] = 2
mySelect.simulate('change')
I created this utility function to Enzyme test react-select using Typescript. It returns a string reference to the component you can use to make assertions.
const selectOption = (
wrapper: ReactWrapper<{}, {}, React.Component<{}, {}, any>>,
selector: string,
option: string
): string => {
wrapper.find(`${selector} input`).instance().value = option.charAt(0)
wrapper.find(`${selector} input`).simulate('change', {
target: { value: option.charAt(0) }
})
wrapper
.find(`${selector} .react-select__menu div[children="${option}"]`)
.simulate('click')
return `${selector} .react-select__single-value`
}
Usage:
const select = selectOption(
component,
'#country',
'United States of America'
)
Assert, where component is added to the test using mount():
expect(component.find(select).text()).toEqual('United States of America')
I have had similar issues simulating changes with react-select, possibly due to using MaterialUI as well.
The solutions explained so far did not work for me unfortunately, but I finally came across a solution which may be useful to those using MaterialUI, react-select and enzyme:
const mockChangeCallBack = jest.fn();
const wrapper = mount(
<ReactSelectTestComponent options={someOptions} onChange={mockChangeCallBack} />
);
// Find input field on Select component (from the react-select module).
const input = wrapper.find(Select).find("input");
// Simulate the arrow down event to open the dropdown menu.
input.simulate("keyDown", { key: "ArrowDown", keyCode: 40 });
// Simulate the enter key to select the first option.
input.simulate("keyDown", { key: "Enter", keyCode: 13 });
// Assert that the onChange function has been called.
expect(mockOnChangeCB).toHaveBeenCalled();
/*
* Could further assert that the state has changed to the first
* option in the options list (e.g. someOptions[0]) etc...
*/
Thanks @scottbanyard that was helpful
What about if the react-select is given the prop searchable={false}? No input is rendered when this prop is given and it's just a plain dropdown. How do you simulate a change for that? I tried simulating a change on the Select element and it did not work.
@jamcreencia simulate doesn’t actually simulate anything, which is why i recommend avoiding it. It’s just sugar for invoking a prop function. If you want to invoke onChange, use .prop(‘onChange’)().
@ljharb simulate has a problem in fact. Sometimes it working but for one test simulate not trigger event change. Atfer many tests, with my team we are arrived to invoke onChange by using .invoke('onChange')() too.
I created this utility function to Enzyme test react-select using Typescript. It returns a string reference to the component you can use to make assertions.
const selectOption = ( wrapper: ReactWrapper<{}, {}, React.Component<{}, {}, any>>, selector: string, option: string ): string => { wrapper.find(`${selector} input`).instance().value = option.charAt(0) wrapper.find(`${selector} input`).simulate('change', { target: { value: option.charAt(0) } }) wrapper .find(`${selector} .react-select__menu div[children="${option}"]`) .simulate('click') return `${selector} .react-select__single-value` }Usage:
const select = selectOption( component, '#country', 'United States of America' )Assert, where component is added to the test using
mount():expect(component.find(select).text()).toEqual('United States of America')
this works for me for react-select 3.1.1 but only for Select not for Async Select
Most helpful comment
@idoo so with
shallowI was able to get a passing test usingreact-select. You can see it here. enzyme-test-repo/tree/issue-#400. It seems that yourfindquery is not accurately querying the node that the change event should be simulated on. If you just useshallowand query for the<Select/>component you can easily test that theonChangeprop is being called.ReactSelectTestComponent.js
test.js
As for
mountit seems likely thatreact-selectisn't actually registering a change event on the input your querying, I'd have to look at the source more but it doesn't seem like they actually register anonChangehandler with their DOM nodes.If you look at the test files for
react-selectyou can see they are using a specific node to test change events:Where
searchInputNodeseems to be defined as:So you may want to follow that as an example of testing change events with
mount, thoughshallowdoes test theonChangeprop just fine.