UNPKG

wix-style-react

Version:
604 lines (533 loc) • 20.6 kB
import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; import { mount } from 'enzyme'; import inputWithOptionsDriverFactory from './InputWithOptions.driver'; import InputWithOptions from './InputWithOptions'; import { createDriverFactory } from 'wix-ui-test-utils/driver-factory'; import { makeControlled } from '../../test/utils'; import { inputWithOptionsTestkitFactory } from '../../testkit'; import { inputWithOptionsTestkitFactory as enzymeInputWithOptionsTestkitFactory } from '../../testkit/enzyme'; describe('InputWithOptions', () => { const ControlledInputWithOptions = makeControlled(InputWithOptions); const createDriver = createDriverFactory(inputWithOptionsDriverFactory); const options = [ { id: 0, value: 'Option 1' }, { id: 1, value: 'Option 2' }, { id: 2, value: 'Option 3', disabled: true }, { id: 3, value: 'Option 4' }, { id: 'divider1', value: '-' }, { id: 'element1', value: <span style={{ color: 'brown' }}>Option 4</span>, }, ]; it('should NOT show dropdown when autofocus is on', () => { const { inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} autoFocus />, ); expect(inputDriver.isFocus()).toBeTruthy(); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should have an Input and an hidden DropdownLayout', () => { const { inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); expect(inputDriver.exists()).toBeTruthy(); expect(dropdownLayoutDriver.exists()).toBeTruthy(); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should show DropdownLayout when input get focused', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.focus(); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); describe('showOptionsIfEmptyInput property', () => { describe('show options if input is empty (default behaviour)', () => { it('should show DropdownLayout if input is empty and down arrow pressed', () => { const { driver, dropdownLayoutDriver } = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput options={options} />, ); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBe(true); }); }); describe('do not show options if input is empty', () => { it('should not show DropdownLayout if input is empty and focused', () => { const { driver, dropdownLayoutDriver } = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput={false} options={options} />, ); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBe(false); }); it('should show DropdownLayout if initial value passed and input focused', () => { const { driver, dropdownLayoutDriver } = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput={false} value={options[0].value} options={options} />, ); expect(dropdownLayoutDriver.isShown()).toBe(false); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBe(true); }); it('should show DropdownLayout if text was entered', () => { const driver = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput={false} options={options} />, ); expect(driver.dropdownLayoutDriver.isShown()).toBe(false); driver.inputDriver.focus(); expect(driver.dropdownLayoutDriver.isShown()).toBe(false); driver.inputDriver.enterText('some value'); expect(driver.dropdownLayoutDriver.isShown()).toBe(true); }); it('should not show DropdownLayout if input was emptied', () => { const driver = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput={false} options={options} />, ); driver.inputDriver.enterText('some value'); driver.inputDriver.clearText(); expect(driver.dropdownLayoutDriver.isShown()).toBe(false); }); it('should not show DropdownLayout if input is empty and no char was produced by keypress', () => { const driver = createDriver( <ControlledInputWithOptions showOptionsIfEmptyInput={false} options={options} />, ); driver.inputDriver.trigger('keyDown', { key: 37, // <Left Arrow> key code }); expect(driver.dropdownLayoutDriver.isShown()).toBe(false); }); it('should hide options on option select', () => { const driver = createDriver( <ControlledInputWithOptions value="some value" showOptionsIfEmptyInput={false} options={options} closeOnSelect onSelect={function(option) { this.setState({ value: option.value }); }} />, ); driver.inputDriver.focus(); driver.dropdownLayoutDriver.clickAtOption(0); expect(driver.dropdownLayoutDriver.isShown()).toBe(false); }); }); }); it('should not show DropdownLayout when a non whitelisted key is pressed', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.pressKey('Any'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should show DropdownLayout on down key', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBeTruthy(); }); it('should not show DropdownLayout on modifier keys', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.pressKey('Shift'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); driver.pressKey('Alt'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); driver.pressKey('Control'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should hide DropdownLayout on enter and esc key press', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBeTruthy(); driver.pressKey('Enter'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); driver.pressKey('Escape'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should start keyboard navigation from last selected option when re-opening the dropdown layout', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} selectedId={1} />, ); driver.focus(); dropdownLayoutDriver.clickAtOption(1); driver.outsideClick(); driver.focus(); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isOptionSelected(1)).toBeTruthy(); driver.pressKey('ArrowDown'); // going to skip disabled option at index 2 expect(dropdownLayoutDriver.isOptionHovered(3)).toBeTruthy(); }); it('should call onManuallyInput on enter key press with a trimed value', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} />, ); inputDriver.enterText('my text '); driver.pressKey('Enter'); expect(onManuallyInput).toBeCalledWith('my text', undefined); }); it('should call onManuallyInput on enter key press', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} />, ); inputDriver.enterText('my text'); driver.pressKey('Enter'); expect(onManuallyInput).toBeCalledWith('my text', undefined); }); it('should call onManuallyInput on tab key press', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} />, ); inputDriver.enterText('my text'); driver.pressKey('Tab'); expect(onManuallyInput).toBeCalledWith('my text', undefined); }); it('should close dropdown on press tab key', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} />, ); inputDriver.focus(); driver.pressKey('ArrowDown'); expect(inputDriver.isFocus()).toBe(true); expect(dropdownLayoutDriver.isShown()).toBe(true); driver.pressKey('Tab'); // todo: jest limitation of mimicking native Tab browser behaviour // expect(inputDriver.isFocus()).toBe(false); expect(dropdownLayoutDriver.isShown()).toBe(false); }); it('should open options when clicked', () => { const { inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); expect(dropdownLayoutDriver.isShown()).toBe(false); inputDriver.click(); expect(dropdownLayoutDriver.isShown()).toBe(true); }); it('should NOT close options when input clicked before 2 seconds passed from last opening', () => { const { inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); const originalNow = Date.now; Date.now = () => 0; inputDriver.click(); expect(dropdownLayoutDriver.isShown()).toBe(true); Date.now = () => 1500; inputDriver.click(); expect(dropdownLayoutDriver.isShown()).toBe(true); Date.now = originalNow; }); it('should close options when input clicked after 2 seconds from last opening', () => { const { inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); const originalNow = Date.now; Date.now = () => 0; inputDriver.click(); expect(dropdownLayoutDriver.isShown()).toBe(true); Date.now = () => 2500; inputDriver.click(); expect(dropdownLayoutDriver.isShown()).toBe(false); Date.now = originalNow; }); it('should stay focused on tab key press with closeOnSelect=false', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} closeOnSelect={false} />, ); inputDriver.focus(); inputDriver.enterText('Option 1'); driver.pressKey('ArrowDown'); expect(inputDriver.isFocus()).toBe(true); driver.pressKey('Tab'); expect(inputDriver.isFocus()).toBe(true); expect(dropdownLayoutDriver.isShown()).toBe(true); }); it('should suggest an option when calling onManuallyInput', () => { const onManuallyInput = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManuallyInput} />, ); inputDriver.enterText('Option 2'); driver.pressKey('Enter'); expect(onManuallyInput).toBeCalledWith('Option 2', { id: 1, value: 'Option 2', }); }); it('should hide options on selection by default', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.focus(); dropdownLayoutDriver.clickAtOption(0); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should hide options on outside click', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); driver.outsideClick(); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); it('should not hide options on selection', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} closeOnSelect={false} />, ); driver.focus(); dropdownLayoutDriver.clickAtOption(0); expect(dropdownLayoutDriver.isShown()).toBeTruthy(); }); it('should call onSelect when an option is pressed', () => { const onSelect = jest.fn(); const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} onSelect={onSelect} />, ); driver.focus(); dropdownLayoutDriver.clickAtOption(0); expect(onSelect).toBeCalledWith(options[0]); }); it('should call onSelect when a selected option is pressed', () => { const onSelect = jest.fn(); const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} onSelect={onSelect} selectedId={options[0].id} />, ); driver.focus(); dropdownLayoutDriver.clickAtOption(0); expect(onSelect).toBeCalled(); }); it('should call onFocus', () => { const onFocus = jest.fn(); const { driver } = createDriver( <InputWithOptions options={options} onFocus={onFocus} />, ); driver.focus(); expect(onFocus).toBeCalled(); }); it('should call onBlur if clicked outside and input is focused', () => { const onBlur = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onBlur={onBlur} />, ); driver.outsideClick(); expect(onBlur).not.toBeCalled(); driver.focus(); driver.outsideClick(); inputDriver.blur(); // apparently, jsdom does not fire onBlur after input.blur() is called expect(onBlur).toBeCalled(); }); it('should not call onManuallyInput when composing text via external means', () => { const onManualInput = jest.fn(); const { driver, inputDriver } = createDriver( <InputWithOptions options={options} onManuallyInput={onManualInput} />, ); inputDriver.startComposing(); driver.pressKey('Enter'); expect(onManualInput).not.toBeCalled(); inputDriver.endComposing(); driver.pressKey('Enter'); expect(onManualInput).toBeCalled(); }); it('should wrap all options to highlighter component if prop highlight true', () => { const { driver } = createDriver( <InputWithOptions options={options} highlight />, ); expect(driver.isOptionWrappedToHighlighter(options[0].id)).toBeTruthy(); }); it('should not wrap all options to highlighter component if prop highlight false', () => { const { driver } = createDriver( <InputWithOptions options={options} highlight={false} />, ); expect(driver.isOptionWrappedToHighlighter(options[0].id)).toBeFalsy(); }); // TODO it.skip('should change input value when an option is pressed', () => { const driver = createDriver(<InputWithOptions options={options} />); driver.inputDriver.focus(); const OPTION_INDEX = 0; driver.dropdownLayoutDriver.clickAtOption(OPTION_INDEX); expect(driver.inputDriver.getValue()).toBe(options[OPTION_INDEX].value); }); // TODO it.skip('should invoke onChange with proper event object when an option is pressed', () => { const onChange = jest.fn(); const driver = createDriver( <InputWithOptions options={options} value="some value" onChange={onChange} />, ); driver.inputDriver.focus(); const OPTION_INDEX = 0; driver.dropdownLayoutDriver.clickAtOption(OPTION_INDEX); expect(onChange).toBeCalled(); expect(onChange.mock.calls[0][0].target.value).toBe( options[OPTION_INDEX].value, ); }); it('should support autocomplete prop', () => { const { inputDriver } = createDriver( <InputWithOptions autocomplete="off" />, ); expect(inputDriver.getAutocomplete()).toBe('off'); }); it('should support tabIndex prop', () => { const { dropdownLayoutDriver } = createDriver( <InputWithOptions tabIndex={-1} />, ); expect(dropdownLayoutDriver.tabIndex()).toBe(-1); }); it('should support required prop', () => { const { inputDriver } = createDriver(<InputWithOptions required />); expect(inputDriver.getRequired()).toBeTruthy(); }); it('should support a divider option', () => { const { dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} />, ); expect(dropdownLayoutDriver.isOptionADivider(4)).toBeTruthy(); }); describe('onKeyArrowDown', () => { it('should behave normal when external onKeyArrowDown passed', () => { const { driver, dropdownLayoutDriver } = createDriver( <InputWithOptions options={options} onKeyArrowDown={() => null} />, ); driver.pressKey('ArrowDown'); expect(dropdownLayoutDriver.isShown()).toBeTruthy(); driver.pressKey('Enter'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); driver.pressKey('Escape'); expect(dropdownLayoutDriver.isShown()).toBeFalsy(); }); }); describe('onSelect', () => { it('should call onSelect on enter key press', () => { const onSelect = jest.fn(); const { driver } = createDriver( <InputWithOptions options={options} onSelect={onSelect} />, ); driver.pressKey('ArrowDown'); driver.pressKey('ArrowDown'); driver.pressKey('Enter'); expect(onSelect).toBeCalledWith(options[0]); }); it('should call onSelect on tab key press', () => { const onSelect = jest.fn(); const { driver } = createDriver( <InputWithOptions options={options} onSelect={onSelect} />, ); driver.pressKey('ArrowDown'); driver.pressKey('ArrowDown'); driver.pressKey('Tab'); expect(onSelect).toBeCalledWith(options[0]); }); it('should not call onSelect on space key press', () => { const onSelect = jest.fn(); const { driver } = createDriver( <InputWithOptions options={options} onSelect={onSelect} />, ); driver.focus(); driver.pressKey('ArrowDown'); driver.pressKey(' '); expect(onSelect).not.toHaveBeenCalled(); }); it('should call onSelect on space key press in readOnly mode', () => { const onSelect = jest.fn(); class ReadOnlyInput extends InputWithOptions { inputAdditionalProps = () => ({ readOnly: true }); } const { driver } = createDriver( <ReadOnlyInput options={options} onSelect={onSelect} />, ); driver.pressKey('ArrowDown'); driver.pressKey('ArrowDown'); driver.pressKey(' '); expect(onSelect).toBeCalledWith(options[0]); }); }); describe('testkit', () => { it('should exist', () => { const div = document.createElement('div'); const dataHook = 'myDataHook'; const wrapper = div.appendChild( ReactTestUtils.renderIntoDocument( <div> <InputWithOptions dataHook={dataHook} /> </div>, ), ); const inputWithOptionsTestkit = inputWithOptionsTestkitFactory({ wrapper, dataHook, }); expect(inputWithOptionsTestkit.driver.exists()).toBeTruthy(); expect(inputWithOptionsTestkit.inputDriver.exists()).toBeTruthy(); expect( inputWithOptionsTestkit.dropdownLayoutDriver.exists(), ).toBeTruthy(); }); }); describe('enzyme testkit', () => { it('should exist', () => { const dataHook = 'myDataHook'; const wrapper = mount(<InputWithOptions dataHook={dataHook} />); const inputWithOptionsTestkit = enzymeInputWithOptionsTestkitFactory({ wrapper, dataHook, }); expect(inputWithOptionsTestkit.driver.exists()).toBeTruthy(); expect(inputWithOptionsTestkit.inputDriver.exists()).toBeTruthy(); expect( inputWithOptionsTestkit.dropdownLayoutDriver.exists(), ).toBeTruthy(); }); }); describe('appearance', () => { it('should be possible to specify the theme of underlying elements', () => { const props = { theme: 'material', dataHook: 'myDataHook' }; const wrapper = mount(<InputWithOptions {...props} />); const testkit = enzymeInputWithOptionsTestkitFactory({ wrapper, dataHook: props.dataHook, }); expect(testkit.inputDriver.isOfStyle(props.theme)).toBe(true); expect(testkit.dropdownLayoutDriver.hasTheme(props.theme)).toBe(true); }); }); });