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);
});
});
});