import React from 'react'
import { mount, shallow } from 'enzyme'
import { ItemStatus } from '../_internals/item'
import { ItemChoice } from '../itemChoice'
import { ItemInfo } from '../itemInfo'
import { AutoComplete } from './AutoComplete'
const initialFakeItems = [
{ id: '1', label: 'title1', labelInfo: 'description1' },
{ id: '2', label: 'title2', labelInfo: 'description2' },
]
// Returns an array with a new reference
const fakeSearchForItems = () => initialFakeItems.slice()
const defaultProps = {
name: 'cities',
items: initialFakeItems,
searchOnMount: true,
isSearching: false,
searchForItems: fakeSearchForItems,
searchForItemsMinChars: 1,
debounceTimeout: 0,
}
jest.useFakeTimers()
describe('AutoComplete', () => {
it('Renders no result items when mouting', () => {
const wrapper = mount()
expect(wrapper.find('AutoCompleteList').prop('visible')).toBe(false)
})
it('Disable native browser autocomplete', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('autoComplete')).toBe('off')
})
it('Renders result items when items prop has changed', () => {
const wrapper = mount()
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: 'Lyon' })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: fakeSearchForItems(), isSearching: false })
expect(wrapper.find('AutoCompleteList').prop('visible')).toBe(true)
wrapper.setProps({ showList: false })
expect(wrapper.find('AutoCompleteList').prop('visible')).toBe(false)
})
it('Renders with a custom className', () => {
const customClassName = 'my-search'
const wrapper = shallow()
expect(wrapper.hasClass(customClassName)).toBe(true)
})
it('Renders a title on the close button', () => {
const wrapper = mount(
,
)
expect(wrapper.find('button').prop('title')).toBe('buttonTitle')
})
it('Can trigger onDoneAnimationEnd callback', () => {
const event = jest.fn()
const wrapper = mount(
,
)
wrapper.instance().onInputChange({ value: 'title' })
const items = fakeSearchForItems()
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items, isSearching: false })
wrapper.find(ItemChoice).first().simulate('click')
expect(event).not.toBeCalled()
jest.advanceTimersByTime(3000)
expect(event).toBeCalled()
})
describe('#maxItems', () => {
it('Renders only `maxItems` when specified', () => {
const wrapper = mount()
wrapper.instance().onInputChange({ value: 'title' })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: fakeSearchForItems(), isSearching: false })
expect(wrapper.find('li')).toHaveLength(1)
})
})
describe('#searchForItemsMinChars', () => {
it('Do search for items when typed at least `searchForItemsMinChars` chars', () => {
const query = 'Lyon'
const searchForItemsSpy = jest.fn()
const wrapper = shallow(
,
)
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: query })
expect(searchForItemsSpy).toHaveBeenCalledWith(query)
})
it('Do not search for items when typed less than `searchForItemsMinChars` chars', () => {
const query = 'a'
const searchForItemsSpy = jest.fn()
const wrapper = shallow(
,
)
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: query })
expect(searchForItemsSpy).not.toHaveBeenCalled()
})
})
describe('#renderNoResults', () => {
it('Renders an empty state when no results', () => {
const renderNoResults = jest.fn(() => 'No results found')
const wrapper = mount()
const query = 'Lyon'
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: query })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: [], isSearching: false })
expect(wrapper.find('AutoCompleteList').prop('visible')).toBe(false)
expect(wrapper.find(ItemInfo)).toHaveLength(1)
expect(wrapper.find(ItemInfo).prop('mainTitle')).toBe('No results found')
expect(renderNoResults).toHaveBeenCalledWith({ query })
wrapper.setProps({ showList: false })
expect(wrapper.find(ItemInfo)).toHaveLength(0)
})
it('Renders no results with a custom className', () => {
const bodyClassName = 'custom'
const wrapper = shallow()
const query = 'Lyon'
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: query })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: [], isSearching: false })
expect(wrapper.find(`.${bodyClassName}`).exists()).toBe(true)
})
})
describe('#renderBusy', () => {
const busyTimeout = 1
it('Renders a busy state after `busyTimeout` milliseconds', () => {
const wrapper = mount(
}
/>,
)
expect(wrapper.state('busy')).toBe(false)
wrapper.instance().onInputChange({ value: 'Lyon' })
// Fast forward and exhaust only currently pending timers
jest.runOnlyPendingTimers()
wrapper.update()
expect(wrapper.state('busy')).toBe(true)
expect(wrapper.find('.busy')).toHaveLength(1)
wrapper.setProps({ showList: false })
expect(wrapper.find('.busy')).toHaveLength(0)
})
})
describe('renderEmptySearch', () => {
const emptySearch = [
{ id: '1', title: 'title1', description: 'description1' },
{ id: '2', title: 'title2', description: 'description2' },
{ id: '3', title: 'title3', description: 'description3' },
]
it('renders empty search when query is lower than minChar', () => {
const wrapper = mount(
}
/>,
)
expect(wrapper.find('li')).toHaveLength(emptySearch.length)
})
it('renders the result list when query is greater than minChar', () => {
const wrapper = mount(
}
/>,
)
wrapper.instance().onInputChange({ value: 'Lyon' })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: fakeSearchForItems(), isSearching: false })
expect(wrapper.find('li')).toHaveLength(initialFakeItems.length)
})
it('empties the result list when setting a new defaultValue', () => {
const initialDefaultValue = 'initialDefaultValue'
const wrapper = mount(
}
/>,
)
wrapper.instance().onInputChange({ value: 'Lyon' })
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items: fakeSearchForItems(), isSearching: false })
expect(wrapper.find('li')).toHaveLength(initialFakeItems.length)
wrapper.setProps({ defaultValue: 'pouet' })
expect(wrapper.find('li')).toHaveLength(0)
})
})
describe('#onInputChange', () => {
it('Invokes `onInputChange` when typing text in TextField', () => {
const onInputChange = jest.fn()
const wrapper = mount()
const value = 'ab'
wrapper.find('input').simulate('change', { target: { value } })
expect(onInputChange).toHaveBeenCalledWith({ value })
})
})
describe('#onSelect', () => {
it('Invokes `onSelect` when selecting a result item', () => {
const onSelectSpy = jest.fn()
const wrapper = mount()
wrapper.instance().onInputChange({ value: 'title' })
const items = fakeSearchForItems()
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items, isSearching: false })
wrapper.find(ItemChoice).first().simulate('click')
expect(onSelectSpy).toHaveBeenCalledWith({
name: defaultProps.name,
value: items[0].label,
item: items[0],
})
})
})
describe('#onClear', () => {
it("Invokes `onClear` when clearing the field's value", () => {
const onClear = jest.fn()
const wrapper = mount(
,
)
wrapper.find('button').simulate('click')
expect(onClear).toHaveBeenCalledTimes(1)
})
})
describe('#getItemValue', () => {
it('Can use a custom `getItemValue` when selecting a result item', () => {
const onSelectSpy = jest.fn()
const wrapper = mount(
item.id} />,
)
wrapper.instance().onInputChange({ value: 'title' })
const items = fakeSearchForItems()
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items, isSearching: false })
wrapper.find('ItemChoice').first().simulate('click')
expect(onSelectSpy).toHaveBeenCalledWith({
name: defaultProps.name,
value: items[0].id,
item: items[0],
})
})
})
describe('#renderQuery', () => {
it('Can use a custom `renderQuery` when selecting a result item', () => {
const wrapper = mount( item.id} />)
wrapper.instance().onInputChange({ value: 'title' })
const items = fakeSearchForItems()
// Simulate a "searchForItems" cycle by passing `isSearching` from `true` to `false`
wrapper.setProps({ isSearching: true })
wrapper.setProps({ items, isSearching: false })
// Simulate the selection of the first list item
wrapper.find('ItemChoice').first().simulate('click')
expect(wrapper.find('TextField').prop('defaultValue')).toBe(items[0].id)
})
})
describe('#focus', () => {
it('Can add focus to TextField', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('focus')).toBe(true)
})
it('Should not add border focus on Textfield', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('focusBorder')).toBe(false)
})
})
describe('#autoCorrect', () => {
it('Can set autoCorrect attribute on TextField', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('autoCorrect')).toBe('off')
wrapper.setProps({ autoCorrect: 'on' })
expect(wrapper.find('TextField').prop('autoCorrect')).toBe('on')
})
})
describe('#disabled', () => {
it('Can set disabled attribute on TextField', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('disabled')).toBe(false)
wrapper.setProps({ disabled: true })
expect(wrapper.find('TextField').prop('disabled')).toBe(true)
})
})
describe('#readOnly', () => {
it('Can set readOnly attribute on TextField', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('readOnly')).toBe(false)
wrapper.setProps({ readOnly: true })
expect(wrapper.find('TextField').prop('readOnly')).toBe(true)
})
})
describe('#required', () => {
it('Can set required attribute on TextField', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('required')).toBe(false)
wrapper.setProps({ required: true })
expect(wrapper.find('TextField').prop('required')).toBe(true)
})
})
describe('#defaultValue', () => {
const initialDefaultValue = 'initialDefaultValue'
it('Can have a default value in TextField when mouting', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('defaultValue')).toBe(initialDefaultValue)
})
it('Can update the textfield value when getting a new default value', () => {
const wrapper = mount()
expect(wrapper.find('TextField').prop('defaultValue')).toBe(initialDefaultValue)
// Simulate a user typing in the textfield
wrapper.instance().onInputChange({ value: 'abc' })
expect(wrapper.find('TextField').state('value')).toBe('abc')
// Simulate a new defaultValue being passed to autoComplete
const newDefaultValue = 'coincoin'
wrapper.setProps({ defaultValue: newDefaultValue })
expect(wrapper.find('TextField').state('value')).toBe(newDefaultValue)
// Simulate a user typing in the textfield again
wrapper.instance().onInputChange({ value: 'a' })
expect(wrapper.find('TextField').state('value')).toBe('a')
})
it('uses the default value to search for items when mouting', () => {
const searchForItems = jest.fn()
shallow(
,
)
expect(searchForItems).toHaveBeenCalledWith(initialDefaultValue)
})
it('Does not trigger a search when mounting with a default value in TextField', () => {
const searchForItems = jest.fn()
shallow(
,
)
expect(searchForItems).not.toHaveBeenCalled()
})
})
describe('#debounceTimeout', () => {
it('Do not limit the number of calls to `searchForItems` without `debounceTimeout`', () => {
const searchForItemsSpy = jest.fn()
const wrapper = shallow(
,
)
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: 'abc' })
autocomplete.onInputChange({ value: 'abcd' })
expect(searchForItemsSpy).toHaveBeenCalledTimes(2)
expect(searchForItemsSpy).toHaveBeenCalledWith('abc')
expect(searchForItemsSpy).toHaveBeenCalledWith('abcd')
})
it('Do limit the number of calls to `searchForItems` with `debounceTimeout`', () => {
const searchForItemsSpy = jest.fn()
const wrapper = shallow(
,
)
const autocomplete: AutoComplete = wrapper.instance()
autocomplete.onInputChange({ value: 'abc' })
autocomplete.onInputChange({ value: 'abcd' })
jest.runAllTimers()
expect(searchForItemsSpy).toHaveBeenCalledTimes(1)
expect(searchForItemsSpy).toHaveBeenCalledWith('abcd')
})
})
})