diff --git a/src/Form/FormAutosuggest.jsx b/src/Form/FormAutosuggest.jsx index daee05ea98..d653d90b42 100644 --- a/src/Form/FormAutosuggest.jsx +++ b/src/Form/FormAutosuggest.jsx @@ -102,6 +102,7 @@ function FormAutosuggest({ const iconToggle = ( @@ -253,7 +255,7 @@ function FormAutosuggest({ > {isLoading ? (
- +
) : state.dropDownItems.length > 0 && state.dropDownItems} diff --git a/src/Form/FormAutosuggestOption.jsx b/src/Form/FormAutosuggestOption.jsx index c8792acc04..a8cad8d9ae 100644 --- a/src/Form/FormAutosuggestOption.jsx +++ b/src/Form/FormAutosuggestOption.jsx @@ -12,8 +12,9 @@ function FormAutosuggestOption({ return ( { - 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 +15,176 @@ 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 - , + ); +} + +FormAutosuggestTestComponent.defaultProps = { + onSelected: jest.fn(), + onClick: jest.fn(), +}; + +FormAutosuggestTestComponent.propTypes = { + /** Specifies onSelected event handler. */ + onSelected: PropTypes.func, + /** Specifies onClick event handler. */ + onClick: PropTypes.func, +}; + +describe('render behavior', () => { + it('renders component without error', () => { + render(); + }); - describe('render behavior', () => { - it('renders component without error', () => { - mount(); - }); + it('renders without loading state', () => { + const { queryByTestId } = render(); + const spinner = queryByTestId('autosuggest_loading_spinner'); + expect(spinner).not.toBeInTheDocument(); + }); + + it('render with loading state', () => { + const { queryByTestId } = render(); + const spinner = queryByTestId('autosuggest_loading_spinner'); + expect(spinner).toBeInTheDocument(); + }); - it('render without loading state', () => { - expect(container.exists('.pgn__form-autosuggest__dropdown-loading')).toBe(false); - expect(container.props().isLoading).toBeUndefined(); - }); + it('renders the auto-populated value if it exists', () => { + render(); + expect(screen.getByDisplayValue('Test Value')).toBeInTheDocument(); + }); - it('render with loading state', () => { - const wrapper = mount(); + it('renders component with options', () => { + const { getByTestId, queryAllByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); + userEvent.click(input); + const list = queryAllByTestId('autosuggest_optionitem'); + expect(list.length).toBe(3); + }); - expect(wrapper.exists('.pgn__form-autosuggest__dropdown-loading')).toBe(true); - expect(wrapper.props().isLoading).toBe(true); - }); + it('renders with error msg', () => { + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - it('renders the auto-populated value if it exists', () => { - const wrapper = mount(); + // if you click into the input and click outside, you should see the error message + userEvent.click(input); + userEvent.click(document.body); - expect(wrapper.find('input').instance().value).toEqual('Test Value'); - expect(wrapper.props().value).toEqual('Test Value'); - }); + const formControlFeedback = getByText('Example error message'); - it('renders component with options', () => { - container.find('input').simulate('click'); - const optionsList = container.find('.pgn__form-autosuggest__dropdown').find('li'); + expect(formControlFeedback).toBeInTheDocument(); + }); +}); - expect(optionsList.length).toEqual(3); - }); +describe('controlled behavior', () => { + it('sets input value based on clicked option', () => { + const { getByText, getByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - 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'); + userEvent.click(input); + const menuItem = getByText('Option 1'); + userEvent.click(menuItem); - expect(formControlFeedback.text()).toEqual('Example error message'); - }); + expect(input.value).toEqual('Option 1'); }); - describe('controlled behavior', () => { - it('selects option', () => { - container.find('input').simulate('click'); - container.find('.pgn__form-autosuggest__dropdown').find('li') - .at(0).simulate('click'); + it('calls onSelected based on clicked option', () => { + const onSelected = jest.fn(); + 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); - }); + userEvent.click(input); + const menuItem = getByText('Option 1'); + userEvent.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(onSelected).toHaveBeenCalledWith('Option 1'); + expect(onSelected).toHaveBeenCalledTimes(1); + }); - expect(onClick).toHaveBeenCalledTimes(1); - }); + 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'); - 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'); + userEvent.click(input); + const menuItem = getByText('Option 2'); + userEvent.click(menuItem); - expect(onClick).toHaveBeenCalledTimes(0); - }); + expect(onClick).toHaveBeenCalledTimes(1); + }); - it('options list depends on empty field value', () => { - container.find('input').simulate('change', { target: { value: '' } }); + 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'); - expect(container.find('input').instance().value).toEqual(''); - }); + userEvent.click(input); + const menuItem = getByText('Option 1'); + userEvent.click(menuItem); - it('options list depends on filled field value', () => { - container.find('input').simulate('change', { target: { value: 'option 1' } }); + expect(onClick).toHaveBeenCalledTimes(0); + }); - expect(container.find('.pgn__form-autosuggest__dropdown').find('li').length).toEqual(1); - expect(onSelected).toHaveBeenCalledTimes(0); - }); + it('filters dropdown based on typed field value with one match', () => { + const { getByTestId, queryAllByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - it('toggles options list', () => { - const dropdownContainer = '.pgn__form-autosuggest__dropdown'; + userEvent.click(input); + userEvent.type(input, 'Option 1'); - expect(container.find(dropdownContainer).find('li').length).toEqual(1); + const list = queryAllByTestId('autosuggest_optionitem'); + expect(list.length).toBe(1); + }); - container.find('button.pgn__form-autosuggest__icon-button').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(0); + it('toggles options list', () => { + const { getByTestId, queryAllByTestId } = render(); + const dropdownBtn = getByTestId('autosuggest_iconbutton'); - container.find('button.pgn__form-autosuggest__icon-button').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(1); - }); + userEvent.click(dropdownBtn); + const list = queryAllByTestId('autosuggest_optionitem'); + expect(list.length).toBe(3); - it('shows options list depends on field value', () => { - container.find('input').simulate('change', { target: { value: '1' } }); + userEvent.click(dropdownBtn); + const updatedList = queryAllByTestId('autosuggest_optionitem'); + expect(updatedList.length).toBe(0); - expect(container.find('.pgn__form-autosuggest__dropdown').find('li').length).toEqual(2); - }); + userEvent.click(dropdownBtn); + const reopenedList = queryAllByTestId('autosuggest_optionitem'); + expect(reopenedList.length).toBe(3); + }); - it('closes options list on click outside', () => { - const fireEvent = createDocumentListenersMock(); - const dropdownContainer = '.pgn__form-autosuggest__dropdown'; + it('filters dropdown based on typed field value with multiple matches', () => { + const { getByTestId, queryAllByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); + + userEvent.click(input); + userEvent.type(input, '1'); + + const list = queryAllByTestId('autosuggest_optionitem'); + expect(list.length).toBe(2); + }); - container.find('input').simulate('click'); - expect(container.find(dropdownContainer).find('li').length).toEqual(2); + it('closes options list on click outside', () => { + const { getByTestId, queryAllByTestId } = render(); + const input = getByTestId('autosuggest_textbox_input'); - act(() => { fireEvent.click(document.body); }); - container.update(); + userEvent.click(input); + const list = queryAllByTestId('autosuggest_optionitem'); + expect(list.length).toBe(3); - expect(container.find(dropdownContainer).find('li').length).toEqual(0); - }); + userEvent.click(document.body); + const updatedList = queryAllByTestId('autosuggest_optionitem'); + expect(updatedList.length).toBe(0); }); });