import { Icon, AutoComplete } from '../../index'; import { noop } from 'lodash'; import sinon from 'sinon'; import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants'; import keyCode from '@douyinfe/semi-foundation/utils/keyCode'; function getAc(props, needAttachTo) { return mount(, { attachTo: document.getElementById('container') }); } let stringData = ['semi', 'ies', 'design', 'platform']; let objectData = [ { email: 'semi@abc.com', value: 'abc' }, { email: 'semi@bytedance.com', value: 'bytedance' }, { email: 'semi@vigo.com', value: 'vigo' }, ]; let commonProps = { // AutoComplete use Popup Layer to show candidate option, // but all Popup Layer which extends from Tooltip (eg Popover, Dropdown) have animation and delay. // Turn off animation and delay during testing, to avoid wating (something like setTimeOut/balabala...) in the test code motion: false, mouseEnterDelay: 0, mouseLeaveDelay: 0, }; describe('AutoComplete', () => { beforeEach(() => { // Avoid `attachTo: document.body` Warning // document.body.innerHTML = ''; const div = document.createElement('div'); div.setAttribute('id', 'container'); document.body.appendChild(div); }); afterEach(() => { const div = document.getElementById('container'); if (div) { document.body.removeChild(div); } document.body.innerHTML = ''; }); it('【style & className】custom className & style', () => { let props = { className: 'test', style: { color: 'red', }, }; const wrapper = getAc(props); expect(wrapper.hasClass('test')).toEqual(true); expect(wrapper.find('div.test')).toHaveStyle('color', 'red'); wrapper.unmount(); }); it('【placeholder】with placeholder', () => { const props = { placeholder: 'semi' }; const ac = getAc(props); expect(ac.find('input').instance().placeholder).toEqual('semi'); }); it('【size】different size', () => { const props = { size: 'small' }; const ac = getAc(props); expect(ac.exists(`.${BASE_CLASS_PREFIX}-input-small`)).toEqual(true); ac.setProps({ size: 'large' }); expect(ac.exists(`.${BASE_CLASS_PREFIX}-input-large`)).toEqual(true); }); it('【disabled】disabled component when disabled is true', () => { const props = { disabled: true }; const ac = getAc(props); expect(ac.exists(`.${BASE_CLASS_PREFIX}-autocomplete-disabled`)).toEqual(true); }); it('【prefix & suffix】custom prefix & suffix', () => { let prefix =
prefix content
; let suffix =
suffix content
; const props = { prefix, suffix, }; let ac = getAc(props); expect(ac.contains(prefix)).toEqual(true); expect(ac.contains(suffix)).toEqual(true); }); it('【dropdownClassName & dropdownStyle】custom dropdownClassName & dropdownStyle', () => { let props = { dropdownClassName: 'ddc', dropdownStyle: { color: 'red', }, defaultOpen: true, ...commonProps, }; let ac = getAc(props); expect(ac.exists(`.${BASE_CLASS_PREFIX}-autocomplete-option-list.ddc`)).toEqual(true); expect(ac.find(`.${BASE_CLASS_PREFIX}-autocomplete-option-list.ddc`)).toHaveStyle('color', 'red'); }); it('【position】different position', () => { let props = { position: 'top', data: stringData, defaultOpen: true, ...commonProps, }; let ac = getAc(props); expect( ac .find(`.${BASE_CLASS_PREFIX}-popover-wrapper`) .instance() .getAttribute('x-placement') ).toEqual('top'); }); it('【defaultValue】with defaultValue(not candidate in data)', () => { let props = { defaultValue: 'semi', data: [], ...commonProps, }; let ac = getAc(props); expect(ac.find('input').instance().value).toEqual('semi'); }); it('【defaultValue】with defaultValue(can match in data)', () => { let props = { defaultValue: 'semi', data: stringData, ...commonProps, }; let ac = getAc(props); expect(ac.find('input').instance().value).toEqual('semi'); }); it('【onSearch】trigger onSearch when input change', () => { let onSearch = value => {}; let spyOnSearch = sinon.spy(onSearch); let props = { onSearch: spyOnSearch, }; let ac = getAc(props); let inputValue = 'semi'; let event = { target: { value: inputValue } }; ac.find('input').simulate('change', event); expect(spyOnSearch.calledOnce).toBe(true); expect(spyOnSearch.calledWithMatch(inputValue)).toBe(true); }); it('optionList should show when defaultOpen is true & data not empty', () => { let props = { defaultOpen: true, data: stringData, ...commonProps, }; let ac = getAc(props); expect(ac.state().visible).toEqual(true); let candidate = ac.find(`.${BASE_CLASS_PREFIX}-autocomplete-option-list`).children(); expect(candidate.length).toEqual(4); expect(candidate.at(0).getDOMNode().textContent).toEqual('semi'); expect(candidate.at(1).getDOMNode().textContent).toEqual('ies'); }); it('【data】updateOptionList when data change', () => { let props = { defaultOpen: true, data: ['semi'], ...commonProps, }; let ac = getAc(props); let candidate = ac.find(`.${BASE_CLASS_PREFIX}-autocomplete-option-list`).children(); expect(candidate.length).toEqual(1); expect(candidate.at(0).getDOMNode().textContent).toEqual('semi'); ac.setProps({ data: ['ies', 'design'] }); ac.update(); candidate = ac.find(`.${BASE_CLASS_PREFIX}-autocomplete-option-list`).children(); expect(candidate.length).toEqual(2); expect(candidate.at(0).getDOMNode().textContent).toEqual('ies'); expect(candidate.at(1).getDOMNode().textContent).toEqual('design'); }); it('【loading】hide optionList & show loading when loading is true', () => { let props = { loading: true, defaultOpen: true, data: stringData, ...commonProps, }; let ac = getAc(props); expect(ac.exists(`.${BASE_CLASS_PREFIX}-autocomplete-loading-wrapper`)).toEqual(true); expect(ac.exists(`.${BASE_CLASS_PREFIX}-autoComplete-option`)).toEqual(false); }); it('【onSelect】trigger onSelect when click candidate option', () => { let onSelect = () => {}; let spyOnSelect = sinon.spy(onSelect); let props = { defaultOpen: true, data: stringData, onSelect: spyOnSelect, ...commonProps, }; let ac = getAc(props); let options = ac.find(`.${BASE_CLASS_PREFIX}-autoComplete-option`); let firstOption = options.at(0); const nativeEvent = { nativeEvent: { stopImmediatePropagation: noop } }; firstOption.simulate('click', nativeEvent); expect(spyOnSelect.calledOnce).toBe(true); expect(spyOnSelect.calledWithMatch(`${BASE_CLASS_PREFIX}`)).toBe(true); }); it('【onSelect】callback with object when onSelectWithObject is true', () => { let onSelect = v => {}; let spyOnSelect = sinon.spy(onSelect); let props = { defaultOpen: true, data: objectData, onSelect: spyOnSelect, onSelectWithObject: true, ...commonProps, }; let ac = getAc(props); let options = ac.find(`.${BASE_CLASS_PREFIX}-autoComplete-option`); let firstOption = options.at(0); const nativeEvent = { nativeEvent: { stopImmediatePropagation: noop } }; firstOption.simulate('click', nativeEvent); expect(spyOnSelect.calledOnce).toBe(true); expect(spyOnSelect.calledWithMatch(objectData[0])).toBe(true); }); it('show candidate option list after click AutoComplete(when data not empty)', () => { let props = { data: stringData, ...commonProps, }; let ac = getAc(props); let acCls = `.${BASE_CLASS_PREFIX}-autocomplete`; ac.find(acCls).simulate('click', {}); expect(ac.state().visible).toEqual(true); expect(ac.exists(`.${BASE_CLASS_PREFIX}-autocomplete-option-list`)).toEqual(true); }); // TODO // it('show clear button when input has value and showClear is true', () => { // let props = { // data: stringData, // showClear: true, // ...commonProps // }; // let ac = getAc(props); // // input Clear button only show when focus/hover // ac.find(`.${BASE_CLASS_PREFIX}-autocomplete`).simulate('click', {}); // ac.find('input').simulate('change', { target: { value: '${BASE_CLASS_PREFIX}' }}); // expect(ac.exists(`.${BASE_CLASS_PREFIX}-input-clearbtn`)).toEqual(true); // }); // it('trigger onSearch when click clear button', () => { // }); it('renderSelectedItem', () => { let props = { data: objectData, defaultOpen: true, ...commonProps, renderSelectedItem: option => option.email, }; let ac = getAc(props); let options = ac.find(`.${BASE_CLASS_PREFIX}-autoComplete-option`); let firstOption = options.at(0); const nativeEvent = { nativeEvent: { stopImmediatePropagation: noop } }; firstOption.simulate('click', nativeEvent); expect(ac.find('input').instance().value).toEqual(objectData[0].email); }); it('renderItem in controlled mode', () => { const fakeOnChange = sinon.spy(); let props = { data: [ { name: '夏可漫', label: '夏可漫', value: 'xiakeman@example.com', email: 'xiakeman@example.com', abbr: 'XK', color: 'amber', }, { name: '申悦', label: '申悦', value: 'shenyue@example.com', email: 'shenyue@example.com', abbr: 'SY', color: 'indigo', }, ], motion: false, defaultOpen: true, mouseEnterDelay: 0, mouseLeaveDelay: 0, renderSelectedItem: option => option.abbr, onChange: fakeOnChange, value: '', }; const ac = getAc(props); ac.find(`.${BASE_CLASS_PREFIX}-autoComplete-option`) .at(0) .simulate('click'); expect(fakeOnChange.calledWith('XK')).toEqual(true); }); it('【value】controlled mode', () => { let props = { data: [], ...commonProps, value: 'semi', }; let ac = getAc(props); expect(ac.find('input').instance().value).toEqual('semi'); ac.setProps({ value: 'ies' }); ac.update(); expect(ac.find('input').instance().value).toEqual('ies'); }); it('【autoFocus】works', () => { let onFocus = () => {}; let spyOnFocus = sinon.spy(onFocus); const props = { autoFocus: true, onFocus: spyOnFocus, }; const ac = getAc(props); expect(spyOnFocus.calledOnce).toBe(true); expect(ac.exists(`.${BASE_CLASS_PREFIX}-input-wrapper-focus`)).toBe(true); }); it('【emptyContent】shows when data is empty', () => { const props = { defaultOpen: true, data: [], emptyContent:
emptycontent
, ...commonProps, }; const ac = getAc(props); expect(ac.exists('.custom-empty-content')).toBe(true); }); it('【emptyContent】not show when data is not empty', () => { const props = { defaultOpen: true, data: [1, 2, 3], emptyContent:
emptycontent
, ...commonProps, }; const ac = getAc(props); expect(ac.exists('.custom-empty-content')).toBe(false); }); it('【onChange should be triggered in right occasion】', () => { const spyOnChange = sinon.spy(); const props = { defaultOpen: true, onChange: spyOnChange, data: ['hello', 'bytedance', 'semi'], showClear: true, motion: false, mouseEnterDelay: 0, mouseLeaveDelay: 0, }; const component = getAc(props); let event = { target: { value: 'abc' } }; component.find('input').simulate('change', event); expect(spyOnChange.calledWith('abc')).toEqual(true); let options = component.find(`.${BASE_CLASS_PREFIX}-autoComplete-option`); let firstOption = options.at(0); const nativeEvent = { nativeEvent: { stopImmediatePropagation: noop } }; firstOption.simulate('click', nativeEvent); expect(spyOnChange.calledWith('hello')).toEqual(true); }); it('maxHeight', () => { let props = { maxHeight: 400, data: ['a', 'b', 'c'], defaultOpen: true, ...commonProps }; let ac = getAc(props); let dom = document.querySelector('.semi-autocomplete-option-list'); expect(dom.style.maxHeight).toEqual('400px'); }); it('zIndex', () => { const props = { defaultOpen: true, data: ['a', 'b', 'c'], zIndex: 998, ...commonProps }; const ac = getAc(props); let popupDom = document.querySelector('.semi-portal'); expect(popupDom.style.zIndex).toEqual('998'); }); it('set trigger width', () => { let numberWidth = { defaultOpen: true, style: { width: 200 }, ...commonProps }; let numberAc = getAc(numberWidth); let dom = document.querySelector('.semi-autocomplete-option-list'); expect(dom.style.minWidth).toEqual('200px'); let stringWidth = { defaultOpen: true, style: { width: '80px' }, ...commonProps }; let stringAc = getAc(stringWidth); let stringDom = document.querySelector('.semi-autocomplete-option-list'); expect(stringDom.style.minWidth).toEqual('80px'); }) // it('onBlur', () => { // }); // it('keyboard event', () => { // let numberWidth = { // defaultOpen: true, // style: { // width: 200 // }, // defaultActiveFirstOption: true, // data: ['a', 'b', 'c', 'd'], // ...commonProps // }; // let numberAc = getAc(numberWidth); // numberAc.simulate('keydown', { keyCode: keyCode['ENTER'] }); // numberAc.simulate('keydown', { keyCode: keyCode['UP'] }); // numberAc.simulate('keydown', { keyCode: keyCode['DOWN'] }); // numberAc.simulate('keydown', { keyCode: keyCode['ESC'] }); // }); // it('triggerRender', () => { // }); });