test: cover CorrespondentSuggestionsDropdown and PersonCard branches
CorrespondentSuggestionsDropdown: empty list still renders the static heading and 'Alle Korrespondenten' row, populated rows when not loading, loading hides correspondent rows, initials fallback (lastName-only when firstName is null), click + keyboard selection, Escape closes. PersonCard: full matrix of conditional UI — title visibility for PERSON vs non-PERSON, avatar initials path (firstName+lastName vs lastName-only fallback), PersonTypeBadge presence for non-PERSON types, alias, life dates, notes, and the canWrite=true/false branches that gate the edit link (Nora's authorization-rendering rule). 21 tests covering ~50 branches. Refs #496. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import CorrespondentSuggestionsDropdown from './CorrespondentSuggestionsDropdown.svelte';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const corrA = { id: 'a', firstName: 'Anna', lastName: 'Schmidt', displayName: 'Anna Schmidt' };
|
||||
const corrB = { id: 'b', firstName: null, lastName: 'Müller', displayName: 'Müller' };
|
||||
|
||||
describe('CorrespondentSuggestionsDropdown', () => {
|
||||
it('renders the heading and the "all correspondents" row even when the list is empty', async () => {
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Häufigste Korrespondenten')).toBeVisible();
|
||||
await expect.element(page.getByText('Alle Korrespondenten von Anna')).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders one row per correspondent when not loading', async () => {
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA, corrB],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Anna Schmidt')).toBeVisible();
|
||||
await expect.element(page.getByText('Müller')).toBeVisible();
|
||||
});
|
||||
|
||||
it('hides correspondent rows while loading is true', async () => {
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA],
|
||||
loading: true,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Anna Schmidt')).not.toBeInTheDocument();
|
||||
await expect.element(page.getByText('Häufigste Korrespondenten')).toBeVisible();
|
||||
});
|
||||
|
||||
it('builds initials from firstName + lastName when available', async () => {
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('AS')).toBeVisible();
|
||||
});
|
||||
|
||||
it('falls back to the first two letters of lastName when firstName is missing', async () => {
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrB],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('MÜ')).toBeVisible();
|
||||
});
|
||||
|
||||
it('calls onselect with the correspondent id when a row is clicked', async () => {
|
||||
const onselect = vi.fn();
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect,
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await page.getByText('Anna Schmidt').click();
|
||||
|
||||
expect(onselect).toHaveBeenCalledWith('a');
|
||||
});
|
||||
|
||||
it('calls onselect with an empty string when the "all correspondents" row is clicked', async () => {
|
||||
const onselect = vi.fn();
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect,
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await page.getByText('Alle Korrespondenten von Anna').click();
|
||||
|
||||
expect(onselect).toHaveBeenCalledWith('');
|
||||
});
|
||||
|
||||
it('calls onselect via Enter key on a focused row', async () => {
|
||||
const onselect = vi.fn();
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect,
|
||||
onclose: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const row = (await page.getByText('Anna Schmidt').element()) as HTMLElement;
|
||||
row.focus();
|
||||
row.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
||||
|
||||
expect(onselect).toHaveBeenCalledWith('a');
|
||||
});
|
||||
|
||||
it('calls onclose when the Escape key is pressed', async () => {
|
||||
const onclose = vi.fn();
|
||||
render(CorrespondentSuggestionsDropdown, {
|
||||
props: {
|
||||
correspondents: [corrA],
|
||||
loading: false,
|
||||
senderName: 'Anna',
|
||||
onselect: () => {},
|
||||
onclose
|
||||
}
|
||||
});
|
||||
|
||||
const list = (await page.getByRole('listbox').element()) as HTMLElement;
|
||||
list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
||||
|
||||
expect(onclose).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user