The trigger hardcoded the multiple-people label for every count, so a single did-you-mean picker announced "Mehrere Personen gefunden" to screen readers while sighted users saw one name and a "Meintest du …?" heading. Derive the trigger's accessible name from persons.length: a single suggestion reuses the heading prop, two or more keep the multiple-people label. Visible truncated name span unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
import { describe, expect, it, vi, afterEach } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import { page } from 'vitest/browser';
|
|
import DisambiguationPicker from './DisambiguationPicker.svelte';
|
|
import type { components } from '$lib/generated/api';
|
|
|
|
type PersonHint = components['schemas']['PersonHint'];
|
|
|
|
afterEach(() => cleanup());
|
|
|
|
const persons: PersonHint[] = [
|
|
{ id: 'w1', displayName: 'Walter Raddatz' },
|
|
{ id: 'w2', displayName: 'Walter Müller' }
|
|
];
|
|
|
|
const multiProps = { persons, heading: 'Person auswählen', showCue: true };
|
|
|
|
function pressEscape() {
|
|
(document.activeElement as HTMLElement).dispatchEvent(
|
|
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })
|
|
);
|
|
}
|
|
|
|
describe('DisambiguationPicker', () => {
|
|
it('opens the picker and shows a select option per ambiguous person', async () => {
|
|
render(DisambiguationPicker, { ...multiProps, onSelect: vi.fn() });
|
|
await page.getByRole('button', { name: /Mehrere Personen gefunden/ }).click();
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Walter Raddatz auswählen' }))
|
|
.toBeInTheDocument();
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Walter Müller auswählen' }))
|
|
.toBeInTheDocument();
|
|
});
|
|
|
|
it('moves focus into the picker list on open', async () => {
|
|
render(DisambiguationPicker, { ...multiProps, onSelect: vi.fn() });
|
|
await page.getByRole('button', { name: /Mehrere Personen gefunden/ }).click();
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Walter Raddatz auswählen' }))
|
|
.toHaveFocus();
|
|
});
|
|
|
|
it('returns focus to the trigger when closed with Escape', async () => {
|
|
render(DisambiguationPicker, { ...multiProps, onSelect: vi.fn() });
|
|
const trigger = page.getByRole('button', { name: /Mehrere Personen gefunden/ });
|
|
await trigger.click();
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Walter Raddatz auswählen' }))
|
|
.toHaveFocus();
|
|
pressEscape();
|
|
await expect.element(trigger).toHaveFocus();
|
|
});
|
|
|
|
it('does not call onSelect when dismissed without choosing', async () => {
|
|
const onSelect = vi.fn();
|
|
render(DisambiguationPicker, { ...multiProps, onSelect });
|
|
await page.getByRole('button', { name: /Mehrere Personen gefunden/ }).click();
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Walter Raddatz auswählen' }))
|
|
.toHaveFocus();
|
|
pressEscape();
|
|
expect(onSelect).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('calls onSelect with the chosen person', async () => {
|
|
const onSelect = vi.fn();
|
|
render(DisambiguationPicker, { ...multiProps, onSelect });
|
|
await page.getByRole('button', { name: /Mehrere Personen gefunden/ }).click();
|
|
await page.getByRole('button', { name: 'Walter Müller auswählen' }).click();
|
|
expect(onSelect).toHaveBeenCalledWith(persons[1]);
|
|
});
|
|
|
|
it('renders the supplied heading as a visible panel heading', async () => {
|
|
render(DisambiguationPicker, {
|
|
persons: [{ id: 'c1', displayName: 'Clara Cramer' }],
|
|
heading: 'Meintest du Clara Cramer?',
|
|
showCue: false,
|
|
onSelect: vi.fn()
|
|
});
|
|
await page.getByRole('button', { name: 'Meintest du Clara Cramer?' }).click();
|
|
await expect.element(page.getByText('Meintest du Clara Cramer?')).toBeVisible();
|
|
});
|
|
|
|
it('suppresses the cue when showCue is false', async () => {
|
|
render(DisambiguationPicker, {
|
|
persons: [{ id: 'c1', displayName: 'Clara Cramer' }],
|
|
heading: 'Meintest du Clara Cramer?',
|
|
showCue: false,
|
|
onSelect: vi.fn()
|
|
});
|
|
await expect.element(page.getByText('(auswählen…)')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('shows the cue when showCue is true', async () => {
|
|
render(DisambiguationPicker, { ...multiProps, onSelect: vi.fn() });
|
|
await expect.element(page.getByText('(auswählen…)')).toBeVisible();
|
|
});
|
|
|
|
it('announces the did-you-mean heading as the trigger accessible name for a single suggestion', async () => {
|
|
render(DisambiguationPicker, {
|
|
persons: [{ id: 'c1', displayName: 'Clara Cramer' }],
|
|
heading: 'Meintest du Clara Cramer?',
|
|
showCue: false,
|
|
onSelect: vi.fn()
|
|
});
|
|
await expect
|
|
.element(page.getByRole('button', { name: 'Meintest du Clara Cramer?' }))
|
|
.toBeInTheDocument();
|
|
});
|
|
|
|
it('keeps the multiple-people trigger accessible name for two or more suggestions', async () => {
|
|
render(DisambiguationPicker, { ...multiProps, onSelect: vi.fn() });
|
|
await expect
|
|
.element(page.getByRole('button', { name: /Mehrere Personen gefunden/ }))
|
|
.toBeInTheDocument();
|
|
});
|
|
});
|