import React from 'react'; import { mount, shallow } from 'enzyme'; import assert from 'assert'; import sinon from 'sinon'; import { filterTypes, rejectTypes } from '../../util/component-types'; import _ from 'lodash'; import { common } from '../../util/generic-tests'; import { SearchableSelectDumb as SearchableSelect } from './SearchableSelect'; import { DropMenuDumb as DropMenu } from '../DropMenu/DropMenu'; const { Placeholder, Option, OptionGroup, SearchField } = SearchableSelect as any; describe('SearchableSelect', () => { common(SearchableSelect, { exemptFunctionProps: ['optionFilter', 'richChildRenderer'] as any, }); describe('render', () => { it('should render a DropMenu', () => { const wrapper = shallow( control ); assert.equal(wrapper.find('DropMenu').length, 1); }); }); describe('props', () => { describe('children', () => { it('should not render any direct child elements which are not SearchableSelect-specific', () => { const wrapper = shallow( controlitalic

header

); assert.equal(wrapper.find('button').length, 0); assert.equal(wrapper.find('h1').length, 0); assert.equal(wrapper.find('i').length, 1); }); }); describe('hasReset', () => { let wrapper: any; afterEach(() => { if (wrapper) { wrapper.unmount(); } }); it('should render the placeholder option as the first one in the menu and be a null option', () => { wrapper = mount( select one ); const menuDOMNode: any = document.querySelector( '.lucid-ContextMenu-FlyOut .lucid-DropMenu-option-container' ); assert( _.includes( menuDOMNode.children[0].className, 'lucid-DropMenu-Option-is-null' ) ); }); it('should not render the placeholder null option as the first one in the menu', () => { wrapper = mount( select one ); const menuDOMNode: any = document.querySelector( '.lucid-ContextMenu-FlyOut .lucid-DropMenu-option-container' ); assert( !_.includes( menuDOMNode.children[0].className, 'lucid-DropMenu-Option-is-null' ) ); }); }); describe('isDisabled', () => { it('should pass the `isDisabled` prop thru to the underlying DropMenu', () => { const wrapper = shallow( select one ); const dropMenuWrapper = wrapper.find('DropMenu'); assert.equal(dropMenuWrapper.prop('isDisabled'), true); }); it('should apply the appropriate classNames to the control', () => { const wrapper = shallow( select one ); const controlWrapper = wrapper.find('.lucid-SearchableSelect-Control'); assert( controlWrapper.hasClass('lucid-SearchableSelect-Control-is-disabled') ); assert( !controlWrapper.hasClass('lucid-SearchableSelect-Control-is-selected') ); }); }); describe('isLoading', () => { it('should render a &-Loading Option and disable all Options', () => { const wrapper = shallow( select one ); const options = wrapper .find(DropMenu) .shallow() .find('.lucid-DropMenu-Option'); // first option should be the loading indicator. assert(options.at(0).hasClass('lucid-SearchableSelect-Loading')); assert(options.every('.lucid-DropMenu-Option-is-disabled')); }); }); describe('isSelectionHighlighted', () => { describe('default', () => { it('should apply the appropriate classNames to the control', () => { const wrapper = shallow( select one ); const controlWrapper = wrapper.find( '.lucid-SearchableSelect-Control' ); assert( controlWrapper.hasClass( 'lucid-SearchableSelect-Control-is-selected' ) ); assert( controlWrapper.hasClass( 'lucid-SearchableSelect-Control-is-highlighted' ) ); }); }); describe('false', () => { it('should apply the appropriate classNames to the control', () => { const wrapper = shallow( select one ); const controlWrapper = wrapper.find( '.lucid-SearchableSelect-Control' ); assert( !controlWrapper.hasClass( 'lucid-SearchableSelect-Control-is-selected' ) ); assert( !controlWrapper.hasClass( 'lucid-SearchableSelect-Control-is-highlighted' ) ); }); }); }); describe('selectedIndex', () => { it('should pass the selectedIndex in an array of 1 to the underlying DropMenu', () => { const wrapper = shallow( select one ); const dropMenuWrapper = wrapper.find('DropMenu'); assert(_.isEqual(dropMenuWrapper.prop('selectedIndices'), [2])); }); it('should render selected option in the control', () => { const wrapper = shallow( select one ); const dropMenuControlWrapper = wrapper.find('DropMenu').childAt(0); assert.equal( 'option c', dropMenuControlWrapper .find('.lucid-SearchableSelect-Control-content') .text() ); }); }); describe('maxMenuHeight', () => { it('should pass through to DropMenu prop `optionContainerStyle.maxHeight`', () => { const wrapper = shallow( select one ); const dropMenuWrapper = wrapper.find(DropMenu); const optionContainerStyle: any = dropMenuWrapper.prop( 'optionContainerStyle' ); assert.equal( 123, optionContainerStyle.maxHeight, 'must match prop value' ); }); }); describe('onSelect', () => { /* eslint-disable no-console */ let error: any, wrapper: any; beforeEach(() => { error = console.error; console.error = jest.fn(); }); afterEach(() => { if (wrapper) { wrapper.unmount(); } console.error = error; }); it('should be called when an option is selected with the appropriate arguments', () => { const onSelect: any = sinon.spy(); wrapper = mount( select one ); const menuDOMNode: any = document.querySelector( '.lucid-ContextMenu-FlyOut .lucid-DropMenu-option-container' ); menuDOMNode.children[2].click(); assert(onSelect.called); const [optionIndex, { props, event }] = onSelect.lastCall.args; assert.equal(optionIndex, 2); assert(props); assert.equal(props.testProp, 'foo'); assert(event); }); /* eslint-enable no-console */ }); describe('onSearch', () => { it('should be called when a new value is entered into the search input', () => { const onSearch = sinon.spy(); const wrapper = shallow( select one ); const searchFieldWrapper = wrapper.find(SearchField); searchFieldWrapper.simulate('change', 'asdf', { event: {} }); assert(onSearch.calledWith('asdf')); }); }); describe('DropMenu', () => { it('should pass thru all DropMenu props to the underlying DropMenu', () => { const explicitDropMenuProps: any = { isExpanded: true, direction: 'up', focusedIndex: 2, }; const wrapper = shallow( control ); const dropMenuProps: any = wrapper.find('DropMenu').props(); _.forEach(explicitDropMenuProps, (value, key) => { assert(_.isEqual(dropMenuProps[key], value)); }); }); }); describe('Error', () => { it('should pass the error class if the Error prop is passed', () => { const wrapper = shallow( control ); const searchWrapper = wrapper.find( '.lucid-SearchableSelect-Control-is-error' ); const errorWrapper = wrapper.find( '.lucid-SearchableSelect-error-content' ); expect(errorWrapper.exists()).toBeTruthy(); expect(searchWrapper.exists()).toBeTruthy(); expect(errorWrapper.text()).toEqual('Error message'); }); }); }); describe('child elements', () => { describe('SearchField', () => { it('should pass the searchfield props through to the underlying SearchField element', () => { const wrapper = shallow( ); const dropMenuHeader = wrapper.childAt(0).childAt(1); const searchFieldWrapper = dropMenuHeader.childAt(0); assert.equal(searchFieldWrapper.prop('placeholder'), 'custom'); }); it('should pass send focus to the underlying SearchField element when opened', () => { const wrapper = mount( ); const control: any = wrapper.find('.lucid-Icon'); control.at(0).simulate('click'); expect(wrapper.state('isFocusOnSearchFieldRequired')).toBe(true); }); }); describe('Placeholder', () => { it('should pass the placeholder thru to the underlying DropMenu Control when no option is selected', () => { const wrapper = shallow( select one ); // navigate down the virutal DOM tree to find the Control content const dropMenuWrapper = wrapper.find('DropMenu'); const dropMenuChildren = dropMenuWrapper.prop('children'); const controlProps = _.first( _.map(filterTypes(dropMenuChildren, DropMenu.Control), 'props') ); const dropMenuControlChildElement: any = _.first( React.Children.toArray(controlProps.children) ); const SearchableSelectControlChildren = React.Children.toArray( dropMenuControlChildElement.props.children ); const SearchableSelectControlContent: any = SearchableSelectControlChildren[0]; assert.equal( React.Children.toArray( SearchableSelectControlContent.props.children )[0], 'select one' ); }); it('should pass the placeholder thru to the underlying DropMenu NullOption when an option is selected', () => { const wrapper = shallow( select one ); // navigate down the virutal DOM tree to find the Control content const dropMenuWrapper = wrapper.find('DropMenu'); const dropMenuChildren = dropMenuWrapper.prop('children'); const nullOptionProps = _.first( _.map(filterTypes(dropMenuChildren, DropMenu.NullOption), 'props') ); assert.equal( React.Children.toArray(nullOptionProps.children)[0], 'select one' ); }); }); describe('Option', () => { it('should pass options thru to the underlying DropMenu', () => { const wrapper = shallow( select one ); const dropMenuWrapper = wrapper.find('DropMenu'); const dropMenuChildren = dropMenuWrapper.prop('children'); const optionsProps = _.map( filterTypes(dropMenuChildren, DropMenu.Option), 'props' ); assert.equal(_.size(optionsProps), 3); assert( _.isEqual(optionsProps[0], { name: 'OptionA', children: 'option a', isDisabled: false, isHidden: false, isWrapped: true, }) ); assert( _.isEqual(optionsProps[1], { name: 'OptionB', children: 'option b', isDisabled: false, isHidden: false, isWrapped: true, }) ); assert( _.isEqual(optionsProps[2], { name: 'OptionC', children: 'option c', isDisabled: false, isHidden: false, isWrapped: true, }) ); }); it('should render Option.Selected in the Placeholder area', () => { expect( shallow( select one ) ).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( select one ) ).toMatchSnapshot(); }); }); describe('OptionGroup', () => { let wrapper: any; let dropMenuWrapper; let dropMenuChildren; let optionGroupProps: any; beforeEach(() => { wrapper = shallow( select one Group Label ); dropMenuWrapper = wrapper.find('DropMenu'); dropMenuChildren = dropMenuWrapper.prop('children'); optionGroupProps = _.first( _.map(filterTypes(dropMenuChildren, DropMenu.OptionGroup), 'props') ); }); it('should pass thru all props to the underlying DropMenu OptionGroup', () => { assert.equal(optionGroupProps.name, 'TestGroup'); }); it('should pass options thru to the underlying DropMenu OptionGroup Options', () => { const optionsProps = _.map( filterTypes(optionGroupProps.children, DropMenu.Option), 'props' ); assert.equal(_.size(optionsProps), 3); assert( _.isEqual(optionsProps[0], { name: 'OptionA', children: 'option a', isDisabled: false, isHidden: false, isWrapped: true, }) ); assert( _.isEqual(optionsProps[1], { name: 'OptionB', children: 'option b', isDisabled: false, isHidden: false, isWrapped: true, }) ); assert( _.isEqual(optionsProps[2], { name: 'OptionC', children: 'option c', isDisabled: false, isHidden: false, isWrapped: true, }) ); }); it('should pass all other elemens thru to the underlying DropMenu OptionGroup', () => { const otherOptionGroupChildren = rejectTypes( optionGroupProps.children, [Placeholder, Option, OptionGroup] ); assert.equal(_.first(otherOptionGroupChildren), 'Group Label'); }); }); }); });