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', () => {
// });
});