diff --git a/src/Form/FormAutosuggest.jsx b/src/Form/FormAutosuggest.jsx index ba4c0a975ce..85a5a12f158 100644 --- a/src/Form/FormAutosuggest.jsx +++ b/src/Form/FormAutosuggest.jsx @@ -227,6 +227,7 @@ function FormAutosuggest({ onChange={handleOnChange} onClick={handleClick} trailingElement={iconToggle} + data-testid="autosuggest_textbox_input" {...props} /> diff --git a/src/Form/FormAutosuggestOption.jsx b/src/Form/FormAutosuggestOption.jsx index c8792acc041..63c2b2d41d2 100644 --- a/src/Form/FormAutosuggestOption.jsx +++ b/src/Form/FormAutosuggestOption.jsx @@ -13,7 +13,7 @@ function FormAutosuggestOption({ { - const listeners = {}; - const handler = (domEl, e) => listeners?.[e]?.({ target: domEl }); - - document.addEventListener = jest.fn((e, fn) => { listeners[e] = fn; }); - document.removeEventListener = jest.fn(e => { delete listeners[e]; }); - - return { - click: domEl => handler(domEl, 'click'), - }; -}; - function FormAutosuggestWrapper(props) { return ( @@ -25,141 +14,179 @@ function FormAutosuggestWrapper(props) { ); } -describe('FormAutosuggest', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - const onSelected = jest.fn(); - const onClick = jest.fn(); - - const container = mount( +function FormAutosuggestTestComponent(props) { + return ( Option 1 - Option 2 + Option 2 Learn from more than 160 member universities - , + ); +} - describe('render behavior', () => { - it('renders component without error', () => { - mount(); - }); +FormAutosuggestTestComponent.defaultProps = { + onSelected: jest.fn(), + onClick: jest.fn(), +}; - it('render without loading state', () => { - expect(container.exists('.pgn__form-autosuggest__dropdown-loading')).toBe(false); - expect(container.props().isLoading).toBeUndefined(); - }); +FormAutosuggestTestComponent.propTypes = { + /** Specifies onSelected event handler. */ + onSelected: PropTypes.func, + /** Specifies onClick event handler. */ + onClick: PropTypes.func, +}; - it('render with loading state', () => { - const wrapper = mount(); +describe('render behavior', () => { + it('renders component without error', () => { + render(); + }); - expect(wrapper.exists('.pgn__form-autosuggest__dropdown-loading')).toBe(true); - expect(wrapper.props().isLoading).toBe(true); - }); + it('renders without loading state', () => { + const { container } = render(); + expect(container.querySelector('.pgn__form-autosuggest__dropdown-loading')).toBeNull(); + }); - it('renders the auto-populated value if it exists', () => { - const wrapper = mount(); + it('render with loading state', () => { + const { container } = render(); + expect(container.querySelector('.pgn__form-autosuggest__dropdown-loading')).toBeTruthy(); + }); - expect(wrapper.find('input').instance().value).toEqual('Test Value'); - expect(wrapper.props().value).toEqual('Test Value'); - }); + it('renders the auto-populated value if it exists', () => { + render(); + expect(screen.getByDisplayValue('Test Value')).toBeInTheDocument(); + }); - it('renders component with options', () => { - container.find('input').simulate('click'); - const optionsList = container.find('.pgn__form-autosuggest__dropdown').find('li'); + it('renders component with options', () => { + const { getByTestId, container } = render(); + const input = getByTestId('autosuggest_textbox_input'); + fireEvent.click(input); + const list = container.querySelectorAll('li'); + expect(list.length).toBe(3); + }); - expect(optionsList.length).toEqual(3); + it('renders with error msg', () => { + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); + + // if you click into the input and hit escape, you should see the error message + fireEvent.click(input); + fireEvent.keyDown(input, { + key: 'Escape', + code: 'Escape', + keyCode: 27, + charCode: 27, }); - it('renders with error msg', () => { - container.find('input').simulate('click'); - act(() => { - const event = new Event('click', { bubbles: true }); - document.dispatchEvent(event); - }); - container.update(); - const formControlFeedback = container.find('FormControlFeedback'); + const formControlFeedback = getByText('Example error message'); - expect(formControlFeedback.text()).toEqual('Example error message'); - }); + expect(formControlFeedback).toBeInTheDocument(); }); +}); - describe('controlled behavior', () => { - it('selects option', () => { - container.find('input').simulate('click'); - container.find('.pgn__form-autosuggest__dropdown').find('li') - .at(0).simulate('click'); +describe('controlled behavior', () => { + it('sets input value based on clicked option', () => { + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - expect(container.find('input').instance().value).toEqual('Option 1'); - expect(onSelected).toHaveBeenCalledWith('Option 1'); - expect(onSelected).toHaveBeenCalledTimes(1); - }); + fireEvent.click(input); + const menuItem = getByText('Option 1'); + fireEvent.click(menuItem); - it('when a function is passed to onClick, it is called', () => { - container.find('input').simulate('change', { target: { value: 'Option 2' } }); - container.find('.pgn__form-autosuggest__dropdown').find('li') - .at(0).simulate('click'); + expect(input.value).toEqual('Option 1'); + }); - expect(onClick).toHaveBeenCalledTimes(1); - }); + it('calls onSelected based on clicked option', () => { + const onSelected = jest.fn(); + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - it('when a function is not passed to onClick, it is not called', () => { - container.find('input').simulate('change', { target: { value: 'Option 1' } }); - container.find('.pgn__form-autosuggest__dropdown').find('li') - .at(0).simulate('click'); + fireEvent.click(input); + const menuItem = getByText('Option 1'); + fireEvent.click(menuItem); - expect(onClick).toHaveBeenCalledTimes(0); - }); + expect(onSelected).toHaveBeenCalledWith('Option 1'); + expect(onSelected).toHaveBeenCalledTimes(1); + }); - it('options list depends on empty field value', () => { - container.find('input').simulate('change', { target: { value: '' } }); + it('calls the function passed to onClick when an option with it is selected', () => { + const onClick = jest.fn(); + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - expect(container.find('input').instance().value).toEqual(''); - }); + fireEvent.click(input); + const menuItem = getByText('Option 2'); + fireEvent.click(menuItem); - it('options list depends on filled field value', () => { - container.find('input').simulate('change', { target: { value: 'option 1' } }); + expect(onClick).toHaveBeenCalledTimes(1); + }); - expect(container.find('.pgn__form-autosuggest__dropdown').find('li').length).toEqual(1); - expect(onSelected).toHaveBeenCalledTimes(0); - }); + it('does not call onClick when an option without it is selected', () => { + const onClick = jest.fn(); + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - it('toggles options list', () => { - const dropdownContainer = '.pgn__form-autosuggest__dropdown'; + fireEvent.click(input); + const menuItem = getByText('Option 1'); + fireEvent.click(menuItem); - expect(container.find(dropdownContainer).find('li').length).toEqual(1); + expect(onClick).toHaveBeenCalledTimes(0); + }); - container.find('button.pgn__form-autosuggest__icon-button').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(0); + it('filters dropdown based on typed field value with one match', () => { + const { getByTestId, container } = render(); + const input = getByTestId('autosuggest_textbox_input'); - container.find('button.pgn__form-autosuggest__icon-button').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(1); - }); + fireEvent.click(input); + fireEvent.change(input, { target: { value: 'Option 1' } }); - it('shows options list depends on field value', () => { - container.find('input').simulate('change', { target: { value: '1' } }); + const list = container.querySelectorAll('li'); + expect(list.length).toBe(1); + }); - expect(container.find('.pgn__form-autosuggest__dropdown').find('li').length).toEqual(2); - }); + it('toggles options list', () => { + const { container } = render(); + const dropdownBtn = container.querySelector('button.pgn__form-autosuggest__icon-button'); - it('closes options list on click outside', () => { - const fireEvent = createDocumentListenersMock(); - const dropdownContainer = '.pgn__form-autosuggest__dropdown'; + fireEvent.click(dropdownBtn); + const list = container.querySelectorAll('li'); + expect(list.length).toBe(3); - container.find('input').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(2); + fireEvent.click(dropdownBtn); + const updatedList = container.querySelectorAll('li'); + expect(updatedList.length).toBe(0); - act(() => { fireEvent.click(document.body); }); - container.update(); + fireEvent.click(dropdownBtn); + const reopenedList = container.querySelectorAll('li'); + expect(reopenedList.length).toBe(3); + }); - expect(container.find(dropdownContainer).find('li').length).toEqual(0); - }); + it('filters dropdown based on typed field value with multiple matches', () => { + const { getByTestId, container } = render(); + const input = getByTestId('autosuggest_textbox_input'); + + fireEvent.click(input); + fireEvent.change(input, { target: { value: '1' } }); + + const list = container.querySelectorAll('li'); + expect(list.length).toBe(2); + }); + + it('closes options list on click outside', () => { + const { getByTestId, container } = render(); + const input = getByTestId('autosuggest_textbox_input'); + + fireEvent.click(input); + const list = container.querySelectorAll('li'); + expect(list.length).toBe(3); + + fireEvent.click(document.body); + const updatedList = container.querySelectorAll('li'); + expect(updatedList.length).toBe(0); }); });