test(persons): replace fragile CSS class tests with aria-checked behavior tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-26 12:27:29 +02:00
committed by marcel
parent 1d5f99a2c8
commit 826283afcb

View File

@@ -25,32 +25,47 @@ describe('PersonTypeSelector', () => {
expect(hiddenInput.value).toBe('INSTITUTION'); 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 { container } = render(PersonTypeSelector, { value: 'PERSON' });
const buttons = container.querySelectorAll('[role="radio"]'); const hiddenInput = container.querySelector('input[type="hidden"]') as HTMLInputElement;
const selected = Array.from(buttons).find((b) => b.getAttribute('aria-checked') === 'true'); expect(hiddenInput.value).toBe('PERSON');
expect(selected).not.toBeNull();
expect(selected!.className).toContain('bg-primary'); const personButton = container.querySelector('[aria-checked="true"]') as HTMLElement;
expect(selected!.className).toContain('text-primary-fg'); 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 { container } = render(PersonTypeSelector, { value: 'PERSON' });
const buttons = container.querySelectorAll('[role="radio"]'); const buttons = Array.from(container.querySelectorAll('[role="radio"]'));
const unselected = Array.from(buttons).filter((b) => b.getAttribute('aria-checked') !== 'true'); const groupButton = buttons.find((b) => b.getAttribute('value') === 'GROUP') as HTMLElement;
expect(unselected.length).toBeGreaterThan(0); await userEvent.click(groupButton);
for (const btn of unselected) { expect(groupButton.getAttribute('aria-checked')).toBe('true');
expect(btn.className).toContain('bg-surface'); const others = buttons.filter((b) => b !== groupButton);
expect(btn.className).toContain('text-ink'); for (const btn of others) {
expect(btn.className).toContain('border-line'); 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 { container } = render(PersonTypeSelector, { value: 'PERSON' });
const buttons = container.querySelectorAll('[role="radio"]'); const buttons = Array.from(container.querySelectorAll('[role="radio"]'));
for (const btn of buttons) { const selected = buttons.find((b) => b.getAttribute('aria-checked') === 'true');
expect(btn.className).toContain('ring-focus-ring'); 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');
} }
}); });
}); });