// 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/PlanEditor/GestureValueEditor.tsx';
function TestHarness({ initialValue = '', initialType=true, placeholder = 'Gesture name' } : { initialValue?: string, initialType?: boolean, placeholder?: string }) {
const [value, setValue] = useState(initialValue);
const [_, setType] = useState(initialType)
return (
);
}
describe('GestureValueEditor', () => {
let user: ReturnType;
beforeEach(() => {
user = userEvent.setup();
});
test('renders in tag mode by default and allows selecting a tag via button and select', async () => {
renderWithProviders();
// 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();
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();
// 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();
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();
// 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();
// 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();
});
});