demarches-normaliennes/app/javascript/shared/combobox.test.ts

295 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { suite, test, beforeEach, expect } from 'vitest';
import { matchSorter } from 'match-sorter';
import { Combobox, Option, State } from './combobox';
suite('Combobox', () => {
const options: Option[] =
'Fraises,Myrtilles,Framboises,Mûres,Canneberges,Groseilles,Baies de sureau,Mûres blanches,Baies de genièvre,Baies daçaï'
.split(',')
.map((label) => ({ label, value: label }));
let combobox: Combobox;
let currentState: State;
suite('single select without custom value', () => {
suite('with default selection', () => {
beforeEach(() => {
combobox = new Combobox({
options,
selected: options.at(0) ?? null,
render: (state) => {
currentState = state;
}
});
combobox.init();
});
test('open select box and select option with click', () => {
expect(currentState.open).toBeFalsy();
expect(currentState.loading).toBe(null);
expect(currentState.selection?.label).toBe('Fraises');
combobox.open();
expect(currentState.open).toBeTruthy();
combobox.select('Mûres');
expect(currentState.selection?.label).toBe('Mûres');
expect(currentState.open).toBeFalsy();
});
test('open select box and select option with enter', () => {
expect(currentState.open).toBeFalsy();
expect(currentState.selection?.label).toBe('Fraises');
combobox.keyboard('ArrowDown');
expect(currentState.open).toBeTruthy();
expect(currentState.selection?.label).toBe('Fraises');
expect(currentState.focused?.label).toBe('Fraises');
combobox.keyboard('ArrowDown');
expect(currentState.selection?.label).toBe('Fraises');
expect(currentState.focused?.label).toBe('Myrtilles');
combobox.keyboard('Enter');
expect(currentState.selection?.label).toBe('Myrtilles');
expect(currentState.open).toBeFalsy();
combobox.keyboard('Enter');
expect(currentState.selection?.label).toBe('Myrtilles');
expect(currentState.open).toBeFalsy();
});
test('open select box and select option with tab', () => {
combobox.keyboard('ArrowDown');
combobox.keyboard('ArrowDown');
combobox.keyboard('Tab');
expect(currentState.selection?.label).toBe('Myrtilles');
expect(currentState.open).toBeFalsy();
expect(currentState.hint).toEqual({
type: 'selected',
label: 'Myrtilles'
});
});
test('do not open select box on focus', () => {
combobox.focus();
expect(currentState.open).toBeFalsy();
});
});
suite('empty', () => {
beforeEach(() => {
combobox = new Combobox({
options,
selected: null,
render: (state) => {
currentState = state;
}
});
combobox.init();
});
test('open select box on focus', () => {
combobox.focus();
expect(currentState.open).toBeTruthy();
});
suite('open', () => {
beforeEach(() => {
combobox.open();
});
test('if tab on empty input nothing is selected', () => {
expect(currentState.open).toBeTruthy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Tab');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
});
test('if enter on empty input nothing is selected', () => {
expect(currentState.open).toBeTruthy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Enter');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
});
});
suite('closed', () => {
test('if tab on empty input nothing is selected', () => {
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Tab');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
});
test('if enter on empty input nothing is selected', () => {
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Enter');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
});
});
test('type exact match and press enter', () => {
combobox.input('Baies');
expect(currentState.open).toBeTruthy();
expect(currentState.selection).toBeNull();
expect(currentState.options.length).toEqual(3);
combobox.keyboard('Enter');
expect(currentState.open).toBeFalsy();
expect(currentState.selection?.label).toBe('Baies daçaï');
});
test('type exact match and press tab', () => {
combobox.input('Baies');
expect(currentState.open).toBeTruthy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Tab');
expect(currentState.open).toBeFalsy();
expect(currentState.selection?.label).toBe('Baies daçaï');
expect(currentState.inputValue).toEqual('Baies daçaï');
});
test('type non matching input and press enter', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Enter');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('');
});
test('type non matching input and press tab', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Tab');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('');
});
test('type non matching input and close', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.close();
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('');
});
test('focus should circle', () => {
combobox.input('Baie');
expect(currentState.open).toBeTruthy();
expect(currentState.options.map(({ label }) => label)).toEqual([
'Baies daçaï',
'Baies de genièvre',
'Baies de sureau'
]);
expect(currentState.focused).toBeNull();
combobox.keyboard('ArrowDown');
expect(currentState.focused?.label).toBe('Baies daçaï');
combobox.keyboard('ArrowDown');
expect(currentState.focused?.label).toBe('Baies de genièvre');
combobox.keyboard('ArrowDown');
expect(currentState.focused?.label).toBe('Baies de sureau');
combobox.keyboard('ArrowDown');
expect(currentState.focused?.label).toBe('Baies daçaï');
combobox.keyboard('ArrowUp');
expect(currentState.focused?.label).toBe('Baies de sureau');
});
});
});
suite('single select with custom value', () => {
beforeEach(() => {
combobox = new Combobox({
options,
selected: null,
allowsCustomValue: true,
render: (state) => {
currentState = state;
}
});
combobox.init();
});
test('type non matching input and press enter', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Enter');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('toto');
});
test('type non matching input and press tab', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.keyboard('Tab');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('toto');
});
test('type non matching input and close', () => {
combobox.input('toto');
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
combobox.close();
expect(currentState.open).toBeFalsy();
expect(currentState.selection).toBeNull();
expect(currentState.inputValue).toEqual('toto');
});
});
suite('single select with fetcher', () => {
beforeEach(() => {
combobox = new Combobox({
options: (term: string) =>
Promise.resolve(matchSorter(options, term, { keys: ['value'] })),
selected: null,
render: (state) => {
currentState = state;
}
});
combobox.init();
});
test('type and get options from fetcher', async () => {
expect(currentState.open).toBeFalsy();
expect(currentState.loading).toBe(false);
const result = combobox.input('Baies');
expect(currentState.loading).toBe(true);
await result;
expect(currentState.loading).toBe(false);
expect(currentState.open).toBeTruthy();
expect(currentState.selection).toBeNull();
expect(currentState.options.length).toEqual(3);
});
});
});