136 lines
5.7 KiB
TypeScript
136 lines
5.7 KiB
TypeScript
// This program has been developed by students from the bachelor Computer Science at Utrecht
|
|
// University within the Software Project course.
|
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
|
import { useState } from 'react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { renderWithProviders, screen } from '../../../../test-utils/test-utils.tsx';
|
|
import GestureValueEditor from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/GestureValueEditor';
|
|
|
|
function TestHarness({ initialValue = '', initialType=true, placeholder = 'Gesture name' } : { initialValue?: string, initialType?: boolean, placeholder?: string }) {
|
|
const [value, setValue] = useState(initialValue);
|
|
const [_, setType] = useState(initialType)
|
|
return (
|
|
<GestureValueEditor value={value} setValue={setValue} setType={setType} placeholder={placeholder} />
|
|
);
|
|
}
|
|
|
|
describe('GestureValueEditor', () => {
|
|
let user: ReturnType<typeof userEvent.setup>;
|
|
|
|
beforeEach(() => {
|
|
user = userEvent.setup();
|
|
});
|
|
|
|
test('renders in tag mode by default and allows selecting a tag via button and select', async () => {
|
|
renderWithProviders(<TestHarness />);
|
|
|
|
// Tag selector should be present
|
|
const select = screen.getByTestId('tagSelectorTestID') as HTMLSelectElement;
|
|
expect(select).toBeInTheDocument();
|
|
expect(select.value).toBe('');
|
|
|
|
// Choose a tag via select
|
|
await user.selectOptions(select, 'happy');
|
|
expect(select.value).toBe('happy');
|
|
|
|
// The corresponding tag button should reflect the selection (have the selected class)
|
|
const happyButton = screen.getByRole('button', { name: /happy/i });
|
|
expect(happyButton).toBeInTheDocument();
|
|
expect(happyButton.className).toMatch(/selected/);
|
|
});
|
|
|
|
test('switches to single mode and shows suggestions list', async () => {
|
|
renderWithProviders(<TestHarness initialValue={'happy'} />);
|
|
|
|
const singleButton = screen.getByRole('button', { name: /^single$/i });
|
|
await user.click(singleButton);
|
|
|
|
// Input should be present with placeholder
|
|
const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
|
|
expect(input).toBeInTheDocument();
|
|
|
|
// Because switching to single populates suggestions, we expect at least one suggestion item
|
|
const suggestion = await screen.findByText(/Listening_1/);
|
|
expect(suggestion).toBeInTheDocument();
|
|
});
|
|
|
|
test('typing filters suggestions and selecting a suggestion commits the value and hides the list', async () => {
|
|
renderWithProviders(<TestHarness />);
|
|
|
|
// Switch to single mode
|
|
await user.click(screen.getByRole('button', { name: /^single$/i }));
|
|
const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
|
|
|
|
// Type a substring that matches some suggestions
|
|
await user.type(input, 'Listening_2');
|
|
|
|
// The suggestion should appear and include the text we typed
|
|
const matching = await screen.findByText(/Listening_2/);
|
|
expect(matching).toBeInTheDocument();
|
|
|
|
// Click the suggestion
|
|
await user.click(matching);
|
|
|
|
// After selecting, input should contain that suggestion and suggestions should be hidden
|
|
expect(input.value).toContain('Listening_2');
|
|
expect(screen.queryByText(/Listening_1/)).toBeNull();
|
|
});
|
|
|
|
test('typing a non-matching string hides the suggestions list', async () => {
|
|
renderWithProviders(<TestHarness />);
|
|
await user.click(screen.getByRole('button', { name: /^single$/i }));
|
|
const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
|
|
|
|
await user.type(input, 'no-match-zzz');
|
|
|
|
// There should be no suggestion that includes that gibberish
|
|
expect(screen.queryByText(/no-match-zzz/)).toBeNull();
|
|
});
|
|
|
|
test('switching back to tag mode clears value when it is not a valid tag and preserves it when it is', async () => {
|
|
renderWithProviders(<TestHarness />);
|
|
|
|
// Switch to single mode and pick a suggestion (which is not a semantic tag)
|
|
await user.click(screen.getByRole('button', { name: /^single$/i }));
|
|
const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
|
|
await user.type(input, 'Listening_3');
|
|
const suggestion = await screen.findByText(/Listening_3/);
|
|
await user.click(suggestion);
|
|
|
|
// Switch back to tag mode -> value should be cleared (not in tag list)
|
|
await user.click(screen.getByRole('button', { name: /^tag$/i }));
|
|
const select = screen.getByTestId('tagSelectorTestID') as HTMLSelectElement;
|
|
expect(select.value).toBe('');
|
|
|
|
// Now pick a valid tag and switch to single then back to tag
|
|
await user.selectOptions(select, 'happy');
|
|
expect(select.value).toBe('happy');
|
|
|
|
// Switch to single and then back to tag; since 'happy' is a valid tag, it should remain
|
|
await user.click(screen.getByRole('button', { name: /^single$/i }));
|
|
await user.click(screen.getByRole('button', { name: /^tag$/i }));
|
|
expect(select.value).toBe('happy');
|
|
});
|
|
|
|
test('focus on input re-shows filtered suggestions when customValue is present', async () => {
|
|
renderWithProviders(<TestHarness />);
|
|
|
|
// Switch to single mode and type to filter
|
|
await user.click(screen.getByRole('button', { name: /^single$/i }));
|
|
const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
|
|
|
|
await user.type(input, 'Listening_4');
|
|
const found = await screen.findByText(/Listening_4/);
|
|
expect(found).toBeInTheDocument();
|
|
|
|
// Blur the input
|
|
input.blur();
|
|
expect(found).toBeInTheDocument();
|
|
|
|
// Focus the input again and ensure the suggestions remain or reappear
|
|
await user.click(input);
|
|
const foundAgain = await screen.findByText(/Listening_4/);
|
|
expect(foundAgain).toBeInTheDocument();
|
|
});
|
|
});
|