import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import PersonCard from './PersonCard.svelte'; import type { components } from '$lib/generated/api'; type Person = components['schemas']['PersonSummaryDTO']; const makePerson = (overrides: Partial = {}): Person => ({ id: 'p-1', firstName: 'Anna', lastName: 'Schmidt', displayName: 'Anna Schmidt', personType: 'PERSON', familyMember: false, provisional: false, documentCount: 0, ...overrides }); afterEach(cleanup); describe('PersonCard — confirmed person', () => { it('renders the display name', async () => { render(PersonCard, { props: { person: makePerson() } }); await expect.element(page.getByText('Anna Schmidt')).toBeVisible(); }); it('does not show an unconfirmed badge for a confirmed person', async () => { render(PersonCard, { props: { person: makePerson() } }); await expect.element(page.getByText('unbestätigt')).not.toBeInTheDocument(); }); }); describe('PersonCard — unconfirmed badge keys off provisional only (badge ⇔ count ⇔ triage parity)', () => { it('renders without throwing when lastName is null', async () => { // Before the fix, `lastName[0]` threw at render for a null lastName. Empty-name // crash-safety is a SEPARATE concern from the badge: the placeholder glyph renders // regardless, but the "unbestätigt" badge only fires when provisional is true. const person = makePerson({ lastName: null as unknown as string, displayName: '?', provisional: true }); render(PersonCard, { props: { person } }); // No throw + provisional → the badge is shown. await expect.element(page.getByText('unbestätigt')).toBeVisible(); }); it('shows an unbestätigt badge for a provisional person', async () => { render(PersonCard, { props: { person: makePerson({ provisional: true }) } }); await expect.element(page.getByText('unbestätigt')).toBeVisible(); }); it('does NOT show the badge for a "?" name when not provisional', async () => { // Empty/"?" name alone is no longer treated as unconfirmed — only `provisional` is. // This keeps the badge in lockstep with needsReviewCount and the /persons/review list. render(PersonCard, { props: { person: makePerson({ firstName: undefined, lastName: '?', displayName: '?' }) } }); await expect.element(page.getByText('unbestätigt')).not.toBeInTheDocument(); }); it('does NOT show the badge for an UNKNOWN type when not provisional', async () => { render(PersonCard, { props: { person: makePerson({ personType: 'UNKNOWN', displayName: 'Unklar' }) } }); await expect.element(page.getByText('unbestätigt')).not.toBeInTheDocument(); }); it('renders the placeholder glyph (never a "?" initial) for an empty name even without provisional', async () => { // Crash-safety branch: a null/empty lastName must not throw and must not show "?". render(PersonCard, { props: { person: makePerson({ firstName: undefined, lastName: null as unknown as string, displayName: 'Unbekannt' }) } }); await expect.element(page.getByText('Unbekannt')).toBeVisible(); await expect.element(page.getByText('unbestätigt')).not.toBeInTheDocument(); }); });