Skip to content

Instantly share code, notes, and snippets.

@emil-alexandrescu
Created January 26, 2018 15:48
Show Gist options
  • Save emil-alexandrescu/e4fa5726aabde7ae1b838d2733e6a94a to your computer and use it in GitHub Desktop.
Save emil-alexandrescu/e4fa5726aabde7ae1b838d2733e6a94a to your computer and use it in GitHub Desktop.
Typeahead unit test
import React from 'react';
import { shallow, mount } from 'enzyme';
import Typeahead from 'components/Typeahead';
describe('components', () => describe('<Typeahead />', () => {
let props = {};
let options = [];
beforeEach(() => {
options = [
{ id: 'emil', label: 'awesome' },
{ id: 'anthony', label: 'great' },
{ id: 'max', label: 'fantastic' }
];
props = {
className: '',
onChange: jest.fn(),
placeholder: 'dummy placeholder',
value: undefined,
disabled: false,
options
};
});
describe('should render correctly', () => {
it('with default props', () => {
const wrapper = shallow(<Typeahead {...props} />);
// main container
expect(wrapper.hasClass('typeahead')).toBe(true);
expect(wrapper.hasClass('dropdown')).toBe(true);
// header
const header = wrapper.find('.dropdown__header');
expect(header).toHaveLength(1);
expect(header.find('input')).toHaveLength(1);
const icon = header.find('Icon');
expect(icon).toHaveLength(1);
expect(icon.props().name).toEqual('arrow-down');
// body
const body = wrapper.find('.dropdown__body');
expect(body).toHaveLength(1);
expect(body.find('.typeahead__item')).toHaveLength(3);
});
it('when an item is highlighted', () => {
const wrapper = shallow(<Typeahead {...props} />);
wrapper.setState({ isOpen: true, highlightIndex: 1 });
const highlightItem = wrapper.find('.typeahead__item.typeahead__item--active');
expect(highlightItem).toHaveLength(1);
expect(highlightItem.text()).toEqual('great');
});
it('when component is disabled', () => {
const wrapper = shallow(<Typeahead {...props} disabled />);
expect(wrapper.hasClass('dropdown--disabled')).toBe(true);
});
it('when component has value selected', () => {
const wrapper = shallow(<Typeahead {...props} />);
wrapper.setState({ selectedValue: 'emil' });
expect(wrapper.hasClass('typeahead__selected-value')).toBe(true);
const clearButton = wrapper.find('ActionButton');
expect(clearButton).toHaveLength(1);
expect(clearButton.hasClass('typeahead__icon-clear')).toBe(true);
expect(clearButton.props().iconName).toEqual('close-circle');
expect(clearButton.props().onClick).toEqual(wrapper.instance().onClear);
});
it('when component is loading', () => {
const wrapper = shallow(<Typeahead {...props} loadOptions={jest.fn()} />);
wrapper.setState({ isLoading: true, options });
expect(wrapper.find('.typeahead__loading')).toHaveLength(1);
expect(wrapper.find('.typeahead__item')).toHaveLength(0);
});
});
describe('should filter and render options on type', () => {
describe('when input is synchronous', () => {
let wrapper;
let input;
beforeEach(() => {
props = {
...props,
loadOptions: undefined,
minCharacters: 3
};
wrapper = mount(<Typeahead {...props} />);
input = wrapper.find('input');
});
it('and you type less than minCharacters', () => {
expect(wrapper.state()).toHaveProperty('isOpen', false);
input.simulate('change', { target: { value: 'em' } });
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(wrapper.state()).toHaveProperty('typeInput', 'em');
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(wrapper.find('.typeahead__item')).toHaveLength(3);
});
it('and you type more than minCharacters', () => {
// lower case
expect(wrapper.state()).toHaveProperty('isOpen', false);
input.simulate('change', { target: { value: 'awes' } });
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(wrapper.state()).toHaveProperty('typeInput', 'awes');
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(wrapper.find('.typeahead__item')).toHaveLength(1);
expect(wrapper.instance().getFilteredOptions()[0]).toEqual({ id: 'emil', label: 'awesome' });
// case insensitive
input.simulate('change', { target: { value: 'GreAT' } });
expect(wrapper.find('.typeahead__item')).toHaveLength(1);
expect(wrapper.instance().getFilteredOptions()[0]).toEqual({ id: 'anthony', label: 'great' });
});
it('and type unmatching text', () => {
input.simulate('change', { target: { value: 'bad' } });
expect(wrapper.find('.typeahead__item')).toHaveLength(0);
expect(wrapper.find('.typeahead__no-result')).toHaveLength(1);
expect(wrapper.instance().getFilteredOptions()).toHaveLength(0);
});
});
describe('when input is async', () => {
let wrapper;
let input;
beforeEach(() => {
props = {
...props,
minCharacters: 3,
loadOptions: jest.fn().mockReturnValue(Promise.resolve(options)),
options: []
};
wrapper = mount(<Typeahead {...props} />);
input = wrapper.find('input');
});
it('and you type less than min characters', () => {
expect(wrapper.state()).toHaveProperty('isOpen', false);
input.simulate('change', { target: { value: 'em' } });
expect(wrapper.state()).toHaveProperty('isLoading', true);
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(wrapper.state()).toHaveProperty('options', []);
expect(props.loadOptions).not.toBeCalled();
});
it('and you type more than min characters', done => {
input.simulate('change', { target: { value: 'aweso' } });
expect(props.loadOptions).toBeCalledWith('aweso');
expect(wrapper.state()).toHaveProperty('isLoading', true);
expect(wrapper.state()).toHaveProperty('typeInput', 'aweso');
setTimeout(() => {
expect(wrapper.state()).toHaveProperty('isLoading', false);
expect(wrapper.state().options).toEqual(options);
done();
});
});
it('and load option is failed', done => {
props = {
...props,
minCharacters: 3,
loadOptions: jest.fn().mockReturnValue(Promise.reject('error message')),
options: []
};
wrapper = mount(<Typeahead {...props} />);
input = wrapper.find('input');
input.simulate('change', { target: { value: 'badbad' } });
expect(props.loadOptions).toBeCalledWith('badbad');
setTimeout(() => {
expect(wrapper.state()).toHaveProperty('isLoading', false);
done();
});
});
});
});
describe('should handle key events and item click', () => {
let wrapper;
let input;
let nativeEvent;
beforeEach(() => {
wrapper = mount(<Typeahead {...props} />);
jest.spyOn(wrapper.instance(), 'getFilteredOptions').mockReturnValue(options);
input = wrapper.find('input');
nativeEvent = {
preventDefault: jest.fn(),
stopImmediatePropagation: jest.fn()
};
});
it('when arrow up is pressed', () => {
wrapper.setState({ highlightIndex: 1, isOpen: false });
input.simulate('keydown', { keyCode: 38, which: 38, nativeEvent });
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(wrapper.state()).toHaveProperty('isOpen', true);
input.simulate('keydown', { keyCode: 38, which: 38, nativeEvent });
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(nativeEvent.preventDefault).toHaveBeenCalledTimes(2);
expect(nativeEvent.stopImmediatePropagation).toHaveBeenCalledTimes(2);
});
it('when arrow down is pressed', () => {
wrapper.setState({ highlightIndex: 1, isOpen: false });
input.simulate('keydown', { keyCode: 40, which: 40, nativeEvent });
expect(wrapper.state()).toHaveProperty('highlightIndex', 2);
expect(wrapper.state()).toHaveProperty('isOpen', true);
input.simulate('keydown', { keyCode: 40, which: 40, nativeEvent });
expect(wrapper.state()).toHaveProperty('highlightIndex', 2);
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(nativeEvent.preventDefault).toHaveBeenCalledTimes(2);
expect(nativeEvent.stopImmediatePropagation).toHaveBeenCalledTimes(2);
});
it('when enter is pressed', () => {
wrapper.setState({ highlightIndex: 10, isOpen: true });
const setFocus = jest.spyOn(wrapper.instance(), 'setFocus');
const option = options[10 % options.length];
input.simulate('keydown', { keyCode: 13, which: 13, nativeEvent });
expect(props.onChange).toHaveBeenCalledWith(option.id);
expect(wrapper.state()).toHaveProperty('typeInput', option.label);
expect(wrapper.state()).toHaveProperty('isOpen', false);
expect(wrapper.state()).toHaveProperty('selectedValue', true);
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(setFocus).toHaveBeenCalled();
expect(nativeEvent.preventDefault).toHaveBeenCalled();
expect(nativeEvent.stopImmediatePropagation).toHaveBeenCalled();
});
it('when item is clicked', () => {
wrapper.setState({ isOpen: true });
const item = wrapper.find('.typeahead__item').at(1);
const option = options[1];
item.simulate('mousedown');
expect(props.onChange).toHaveBeenCalledWith(option.id);
expect(wrapper.state()).toHaveProperty('typeInput', option.label);
expect(wrapper.state()).toHaveProperty('isOpen', false);
expect(wrapper.state()).toHaveProperty('selectedValue', true);
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
});
});
it('should close dropdown on outside click', () => {
const wrapper = shallow(<Typeahead {...props} />);
wrapper.setState({ isOpen: true });
document.body.click();
expect(wrapper.state()).toHaveProperty('isOpen', false);
});
it('should set value to state when prop is changed', () => {
const wrapper = shallow(<Typeahead {...props} />);
wrapper.setProps({ value: 'emil' });
expect(wrapper.state()).toHaveProperty('selectedValue', true);
expect(wrapper.state()).toHaveProperty('typeInput', 'awesome');
wrapper.setState({ selectedValue: false, typeInput: '' });
wrapper.setProps({ value: 'not found' });
expect(wrapper.state()).toHaveProperty('selectedValue', false);
expect(wrapper.state()).toHaveProperty('typeInput', '');
});
it('should clear value when onClear is called', () => {
const wrapper = mount(<Typeahead {...props} />);
wrapper.setState({ selectedValue: true, typeInput: 'anthony' });
wrapper.instance().onClear();
expect(wrapper.state()).toHaveProperty('typeInput', '');
expect(wrapper.state()).toHaveProperty('selectedValue', false);
expect(wrapper.state()).toHaveProperty('isOpen', false);
expect(wrapper.state()).toHaveProperty('highlightIndex', 0);
expect(props.onChange).toHaveBeenCalledWith('');
});
it('should set focus on input on clicking header', () => {
const wrapper = mount(<Typeahead {...props} />);
const inputFocusSpy = jest.spyOn(wrapper.instance().inputEl, 'focus');
wrapper.find('.dropdown__header').simulate('click');
expect(inputFocusSpy).toHaveBeenCalled();
inputFocusSpy.mockRestore();
});
it('should open dropdown on focus', () => {
const onFocus = jest.fn();
const wrapper = mount(<Typeahead {...props} onFocus={onFocus} />);
wrapper.find('input').simulate('focus');
expect(wrapper.state()).toHaveProperty('isOpen', true);
expect(onFocus).toBeCalled();
const wrapperWithoutFocus = mount(<Typeahead {...props} onFocus={undefined} />);
wrapperWithoutFocus.find('input').simulate('focus');
expect(wrapperWithoutFocus.state()).toHaveProperty('isOpen', true);
expect(onFocus).toBeCalled();
});
it('should close dropdown on blur', () => {
const onBlur = jest.fn();
const wrapper = mount(<Typeahead {...props} onBlur={onBlur} />);
wrapper.setState({ isOpen: true });
expect(wrapper.hasClass('dropdown--open')).toBe(false);
wrapper.find('input').simulate('blur');
expect(wrapper.state()).toHaveProperty('isOpen', false);
expect(onBlur).toBeCalled();
const wrapperWithoutBlur = mount(<Typeahead {...props} onBlur={undefined} />);
wrapper.setState({ isOpen: true });
wrapperWithoutBlur.find('input').simulate('blur');
expect(wrapperWithoutBlur.state()).toHaveProperty('isOpen', false);
expect(onBlur).toBeCalled();
});
}));
@bhargav11-crest
Copy link

can you add the js file as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment