Created
January 26, 2018 15:48
-
-
Save emil-alexandrescu/e4fa5726aabde7ae1b838d2733e6a94a to your computer and use it in GitHub Desktop.
Typeahead unit test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
}); | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
can you add the js file as well?