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