diff --git a/frontend/src/lib/components/PersonTypeSelector.svelte.spec.ts b/frontend/src/lib/components/PersonTypeSelector.svelte.spec.ts index 58738aa1..29eb21d0 100644 --- a/frontend/src/lib/components/PersonTypeSelector.svelte.spec.ts +++ b/frontend/src/lib/components/PersonTypeSelector.svelte.spec.ts @@ -25,32 +25,47 @@ describe('PersonTypeSelector', () => { expect(hiddenInput.value).toBe('INSTITUTION'); }); - it('selected button uses semantic bg-primary and text-primary-fg classes', () => { + + it('hidden input value updates when user navigates with ArrowLeft (wraps around)', async () => { const { container } = render(PersonTypeSelector, { value: 'PERSON' }); - const buttons = container.querySelectorAll('[role="radio"]'); - const selected = Array.from(buttons).find((b) => b.getAttribute('aria-checked') === 'true'); - expect(selected).not.toBeNull(); - expect(selected!.className).toContain('bg-primary'); - expect(selected!.className).toContain('text-primary-fg'); + const hiddenInput = container.querySelector('input[type="hidden"]') as HTMLInputElement; + expect(hiddenInput.value).toBe('PERSON'); + + const personButton = container.querySelector('[aria-checked="true"]') as HTMLElement; + personButton.focus(); + await userEvent.keyboard('{ArrowLeft}'); + + expect(hiddenInput.value).toBe('UNKNOWN'); + }); + it('exactly one button is aria-checked=true for the initial value', () => { + const { container } = render(PersonTypeSelector, { value: 'INSTITUTION' }); + const buttons = Array.from(container.querySelectorAll('[role="radio"]')); + const checked = buttons.filter((b) => b.getAttribute('aria-checked') === 'true'); + const unchecked = buttons.filter((b) => b.getAttribute('aria-checked') === 'false'); + expect(checked).toHaveLength(1); + expect(unchecked).toHaveLength(3); }); - it('unselected buttons use semantic bg-surface, text-ink, border-line classes', () => { + it('aria-checked=true moves to clicked button on click', async () => { const { container } = render(PersonTypeSelector, { value: 'PERSON' }); - const buttons = container.querySelectorAll('[role="radio"]'); - const unselected = Array.from(buttons).filter((b) => b.getAttribute('aria-checked') !== 'true'); - expect(unselected.length).toBeGreaterThan(0); - for (const btn of unselected) { - expect(btn.className).toContain('bg-surface'); - expect(btn.className).toContain('text-ink'); - expect(btn.className).toContain('border-line'); + const buttons = Array.from(container.querySelectorAll('[role="radio"]')); + const groupButton = buttons.find((b) => b.getAttribute('value') === 'GROUP') as HTMLElement; + await userEvent.click(groupButton); + expect(groupButton.getAttribute('aria-checked')).toBe('true'); + const others = buttons.filter((b) => b !== groupButton); + for (const btn of others) { + expect(btn.getAttribute('aria-checked')).toBe('false'); } }); - it('focus ring uses semantic ring-focus-ring class', () => { + it('selected button has tabindex=0, unselected buttons have tabindex=-1', () => { const { container } = render(PersonTypeSelector, { value: 'PERSON' }); - const buttons = container.querySelectorAll('[role="radio"]'); - for (const btn of buttons) { - expect(btn.className).toContain('ring-focus-ring'); + const buttons = Array.from(container.querySelectorAll('[role="radio"]')); + const selected = buttons.find((b) => b.getAttribute('aria-checked') === 'true'); + const unselected = buttons.filter((b) => b.getAttribute('aria-checked') !== 'true'); + expect(selected!.getAttribute('tabindex')).toBe('0'); + for (const btn of unselected) { + expect(btn.getAttribute('tabindex')).toBe('-1'); } }); });