import React from 'react'; import { fireEvent, render, screen, within, act } from '@testing-library/react'; import { SilkeSelectField } from './silke-select-field'; import userEvent, { UserEvent } from '@testing-library/user-event'; import { SilkeForm } from '../silke-form'; import { ARROW_DOWN, ARROW_UP, ENTER } from '../../jest/keys'; describe('silke-select-field', () => { beforeAll(() => { window.HTMLElement.prototype.scrollIntoView = function () {}; }); const makeItem = (value: string) => ({ label: value, value }); const pressKey = (key: string, user: UserEvent) => act(async () => await user.keyboard(key)); const items = [ makeItem('rat'), makeItem('lion'), makeItem('bear'), makeItem('deer'), makeItem('cat'), ]; it('Has no value when no value passed', () => { render(); expect(screen.getByLabelText('Animal').getAttribute('value')).toBeNull(); }); it('Has value when value passed', () => { render(); expect(screen.getByLabelText('Animal').getAttribute('value')).toEqual('rat'); }); it('Displays no optionslist', () => { render(); const optionsList = screen.queryAllByRole('listbox'); expect(optionsList).toHaveLength(0); }); it('Displays options when clicked', async () => { const user = userEvent.setup(); const onChange = jest.fn(); render(); const input = screen.getByLabelText('Animal'); await act(async () => await user.click(input)); const optionsList = screen.queryAllByRole('listbox'); expect(optionsList).toHaveLength(1); const { getByText } = within(optionsList[0]); const lionOption = getByText('lion'); const ratOption = getByText('rat'); expect(lionOption).toBeDefined(); expect(ratOption).toBeDefined(); }); it('Calls onChange with value when option is clicked', async () => { const user = userEvent.setup(); const onChange = jest.fn(); render(); const input = screen.getByLabelText('Animal'); await act(async () => await user.click(input)); const option = screen.getByText('lion'); await act(async () => await user.click(option)); expect(onChange).toHaveBeenCalledWith('lion'); }); it('Highlights 1st item in list when opened', async () => { const onChange = jest.fn(); const user = userEvent.setup(); render(); const input = screen.getByLabelText('Animal'); act(() => input.focus()); await pressKey(ARROW_DOWN, user); await pressKey(ENTER, user); expect(onChange).toHaveBeenCalledWith('lion'); }); it('Can be selected with keyboard only', async () => { const onChange = jest.fn(); const user = userEvent.setup(); render(); const input = screen.getByLabelText('Animal'); act(() => input.focus()); await pressKey(ARROW_DOWN, user); await pressKey(ARROW_DOWN, user); await pressKey(ARROW_DOWN, user); await pressKey(ENTER, user); expect(onChange).toHaveBeenCalledWith('deer'); }); it('When it has value already, pressing arrow key selects item next to value', async () => { const onChange = jest.fn(); const user = userEvent.setup(); render(); const input = screen.getByLabelText('Animal'); act(() => input.focus()); await pressKey(ARROW_UP, user); await pressKey(ARROW_UP, user); await pressKey(ARROW_UP, user); await pressKey(ENTER, user); expect(onChange).toHaveBeenCalledWith('cat'); }); it('Does not hide dropdown when item is selected on a multiselect select', async () => { const onChange = jest.fn(); const user = userEvent.setup(); render(); const input = screen.getByLabelText('Animal'); act(() => input.focus()); await act(async () => await user.keyboard(ARROW_UP)); const lionOption = screen.getByText('lion'); const dropdown = screen.getByRole('listbox'); expect(dropdown).toBeDefined(); await act(async () => await user.click(lionOption)); expect(onChange).toHaveBeenCalledWith(['lion']); expect(dropdown).toBeDefined(); }); describe('When Silke Select field is within a Silke form', () => { it('Gets value from form', async () => { const onChange = jest.fn(); const currentIndex = 2; render( , ); const input = screen.getByLabelText('Animal'); act(() => input.focus()); expect(input.getAttribute('value')).toEqual('bear'); const dropdown = screen.getByRole('listbox'); expect(dropdown).toBeDefined(); const searchField = dropdown.querySelector('input') as HTMLInputElement; expect(searchField).toBeDefined(); act(() => fireEvent.keyUp(searchField, { key: 'ArrowDown', code: 'ArrowDown' })); act(() => fireEvent.keyUp(searchField, { key: 'ArrowDown', code: 'ArrowDown' })); act(() => fireEvent.keyUp(searchField, { key: 'Enter', code: 'Enter' })); expect(onChange).toHaveBeenCalledWith({ animal: items[currentIndex + 2].value }, true); }); }); });