import _, { forEach, has } from 'lodash';
import React from 'react';
import { shallow } from 'enzyme';
import { common } from '../../util/generic-tests';
import { SearchableMultiSelectDumb as SearchableMultiSelect } from './SearchableMultiSelect';
import { DropMenuDumb as DropMenu } from '../DropMenu/DropMenu';
const { Option, OptionGroup } = SearchableMultiSelect;
describe('SearchableMultiSelect', () => {
common(SearchableMultiSelect, {
exemptFunctionProps: ['optionFilter'] as any,
});
describe('render', () => {
it('should render selections', () => {
const wrapper: any = shallow(
);
expect(wrapper.find(DropMenu).length).toBe(1);
});
it('should pass `isDisabled` to `Options`', () => {
const wrapper: any = shallow(
);
const [first, second, third] = wrapper
.find(DropMenu.Option)
.map((option: any) => option.prop('isDisabled'));
expect(first).toBe(true);
expect(second).toBe(false);
expect(third).toBe(false);
});
});
describe('props', () => {
it('onRemoveAll', () => {
const onRemoveAll = jest.fn();
const wrapper: any = shallow(
);
wrapper.find('Selection').first().prop('onRemove')({ event: 'nert' });
expect(onRemoveAll).toHaveBeenCalled();
});
describe('onSearch', () => {
it('should work', () => {
const onSearch = jest.fn();
const wrapper: any = shallow(
);
const expected = {
event: 'fake',
props: {
callbackId: 'one',
children: 'One',
isDisabled: false,
isHidden: false,
isWrapped: true,
},
};
wrapper.find('SearchField').prop('onChange')('ero', { event: 'fake' });
expect(onSearch).toHaveBeenCalledWith('ero', 1, expected);
});
});
describe('onSelect', () => {
it('should work when fired from the DropMenu', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: {
preventDefault: _.noop,
},
props: {},
};
const wrapper: any = shallow(
);
wrapper.find('DropMenu').first().prop('onSelect')(
10,
mockSelectionCallback
);
expect(onSelect).toHaveBeenCalledWith(9, mockSelectionCallback);
});
it('should work when removing a selection', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: {},
props: {
callbackId: 1,
},
};
const expected = {
event: {},
props: {
callbackId: 'custom',
isDisabled: false,
isHidden: false,
isWrapped: true,
},
};
const wrapper: any = shallow(
);
wrapper.find('Selection').at(1).prop('onRemove')(mockSelectionCallback);
expect(onSelect).toHaveBeenCalledWith(1, expected);
});
it('should work when selecting all', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: { stopPropagation: _.noop, preventDefault: _.noop },
props: {},
};
const wrapper: any = shallow(
);
wrapper.find('DropMenu').first().prop('onSelect')(
0,
mockSelectionCallback
);
expect(onSelect).toHaveBeenCalledWith([0, 1], mockSelectionCallback);
});
it('should work when deselecting all', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: { stopPropagation: _.noop, preventDefault: _.noop },
props: {},
};
const selectedIndices = [0, 1];
const wrapper: any = shallow(
);
wrapper.find('DropMenu').first().prop('onSelect')(
0,
mockSelectionCallback
);
expect(onSelect).toHaveBeenCalledWith([0, 1], mockSelectionCallback);
});
it('should work when selecting some', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: { stopPropagation: _.noop, preventDefault: _.noop },
props: {},
};
const selectedIndices = [0];
const wrapper: any = shallow(
);
wrapper.find('DropMenu').first().prop('onSelect')(
0,
mockSelectionCallback
);
expect(onSelect).toHaveBeenCalledWith([1], mockSelectionCallback);
});
it('should work when selecting filtered options', () => {
const onSelect = jest.fn();
const mockSelectionCallback = {
event: { stopPropagation: _.noop, preventDefault: _.noop },
props: {},
};
const wrapper: any = shallow(
);
wrapper.find('DropMenu').first().prop('onSelect')(
0,
mockSelectionCallback
);
expect(onSelect).toHaveBeenCalledWith([1], mockSelectionCallback);
});
});
describe('Error', () => {
it('should apply the appropriate classNames to the saerch', () => {
const wrapper: any = shallow(
);
const searchWrapper = wrapper.find(
'.lucid-SearchableMultiSelect-search-is-error'
);
expect(searchWrapper.exists()).toBeTruthy();
});
it('should render out the error div', () => {
const wrapper: any = shallow(
);
const searchWrapper = wrapper.find(
'.lucid-SearchableMultiSelect-error-content'
);
expect(searchWrapper.text()).toEqual('Erroring out');
});
it('should not render the error div', () => {
const wrapper: any = shallow(
);
const searchWrapper = wrapper.find(
'.lucid-SearchableMultiSelect-search-is-error'
);
const errorWrapper = wrapper.find(
'.lucid-SearchableMultiSelect-error-content'
);
expect(errorWrapper.exists()).toBeFalsy();
expect(searchWrapper).toBeTruthy();
});
});
});
describe('custom formatting', () => {
it('should render Option.Selected in the SelectedItems area', () => {
expect(
shallow(
)
).toMatchSnapshot();
});
it('should render OptionGroup.Selected in the SelectedItems area', () => {
expect(
shallow(
Foo bar baz
)
).toMatchSnapshot();
});
it('should render Option child function by passing in {searchText}, setting filterText on each option and using a custom optionFilter', () => {
const optionFilter = (searchText: any, { filterText }: any) => {
if (filterText) {
return new RegExp(_.escapeRegExp(searchText), 'i').test(filterText);
}
return true;
};
expect(
shallow(
)
).toMatchSnapshot();
});
});
describe('pass throughs', () => {
let wrapper: any;
beforeEach(() => {
const props = {
className: 'wut',
style: { marginRight: 10 },
initialState: { test: true },
callbackId: 1,
'data-testid': 10,
};
wrapper = shallow();
});
afterEach(() => {
wrapper.unmount();
});
it('passes through props not defined in `propTypes` to the root element.', () => {
const rootProps = wrapper.find('.lucid-SearchableMultiSelect').props();
expect(wrapper.first().prop(['className'])).toContain('wut');
expect(wrapper.first().prop(['style'])).toMatchObject({
marginRight: 10,
});
expect(wrapper.first().prop(['data-testid'])).toBe(10);
// Note: 'className' is plucked from the pass through object
// but still appears becuase it is also directly passed to the root element as a prop.
forEach(['className', 'style', 'data-testid', 'children'], (prop) => {
expect(has(rootProps, prop)).toBe(true);
});
});
it('omits the props defined in `propTypes` from the root element, plus, in addition, `initialState` and `callbackId`.', () => {
const rootProps = wrapper.find('.lucid-SearchableMultiSelect').props();
// initialState and callbackId are always both omitted
forEach(
[
'isDisabled',
'isLoading',
'maxMenuHeight',
'onSearch',
'onSelect',
'onRemoveAll',
'optionFilter',
'searchText',
'selectedIndices',
'DropMenu',
'Option',
'responsiveMode',
'hasRemoveAll',
'hasSelections',
'hasSelectAll',
'selectAllText',
'Error',
'FixedOption',
'NullOption',
'OptionGroup',
'SearchField',
'Label',
'initialState',
'callbackId',
],
(prop) => {
expect(has(rootProps, prop)).toBe(false);
}
);
});
});
});