import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import StammbaumCard from './StammbaumCard.svelte'; afterEach(cleanup); const baseProps = (overrides: Record = {}) => ({ personId: 'p-1', familyMember: false, relationships: [] as unknown[], inferredRelationships: [] as unknown[], canWrite: false, relationshipError: null as string | null, ...overrides }); describe('StammbaumCard', () => { it('renders the heading', async () => { render(StammbaumCard, { props: baseProps() }); await expect .element(page.getByRole('heading', { name: /stammbaum & beziehungen/i })) .toBeVisible(); }); it('renders the family-member toggle when canWrite is true', async () => { render(StammbaumCard, { props: baseProps({ canWrite: true }) }); await expect.element(page.getByRole('switch')).toBeVisible(); }); it('omits the family-member toggle when canWrite is false', async () => { render(StammbaumCard, { props: baseProps() }); await expect.element(page.getByRole('switch')).not.toBeInTheDocument(); }); it('marks the toggle as aria-checked=true when familyMember is true', async () => { render(StammbaumCard, { props: baseProps({ canWrite: true, familyMember: true }) }); await expect.element(page.getByRole('switch')).toHaveAttribute('aria-checked', 'true'); }); it('renders the in-tree banner when familyMember is true', async () => { render(StammbaumCard, { props: baseProps({ familyMember: true }) }); await expect.element(page.getByText('Erscheint im Stammbaum')).toBeVisible(); await expect .element(page.getByRole('link', { name: /ansehen/i })) .toHaveAttribute('href', '/stammbaum?focus=p-1'); }); it('hides the in-tree banner when familyMember is false', async () => { render(StammbaumCard, { props: baseProps() }); await expect.element(page.getByText('Erscheint im Stammbaum')).not.toBeInTheDocument(); }); it('shows the relationshipError alert when set', async () => { render(StammbaumCard, { props: baseProps({ relationshipError: 'Beziehung konnte nicht gespeichert werden.' }) }); await expect .element(page.getByText('Beziehung konnte nicht gespeichert werden.')) .toBeVisible(); }); it('renders the empty placeholder for direct relationships when none exist', async () => { render(StammbaumCard, { props: baseProps() }); await expect.element(page.getByText('Noch keine Beziehungen bekannt.')).toBeVisible(); }); it('hides the inferred-relationships disclosure when there are none', async () => { render(StammbaumCard, { props: baseProps() }); await expect.element(page.getByText('Abgeleitete Beziehungen')).not.toBeInTheDocument(); }); it('renders the AddRelationshipForm when canWrite is true', async () => { render(StammbaumCard, { props: baseProps({ canWrite: true }) }); // AddRelationshipForm renders interactive elements const buttons = document.querySelectorAll('button'); expect(buttons.length).toBeGreaterThan(1); }); it('renders direct relationships sorted by relationType order', async () => { render(StammbaumCard, { props: baseProps({ relationships: [ { id: 'r-friend', relationType: 'FRIEND', personA: { id: 'p-1', displayName: 'Anna' }, personB: { id: 'p-friend', displayName: 'Carlos' } }, { id: 'r-parent', relationType: 'PARENT_OF', personA: { id: 'p-1', displayName: 'Anna' }, personB: { id: 'p-child', displayName: 'Daniel' } } ] }) }); const items = document.querySelectorAll('ul.divide-y > li, ul.divide-y > *'); expect(items.length).toBeGreaterThanOrEqual(2); }); it('renders the year range "from–to" for a relationship with both years', async () => { render(StammbaumCard, { props: baseProps({ relationships: [ { id: 'r-1', relationType: 'COLLEAGUE', fromYear: 1940, toYear: 1945, personA: { id: 'p-1', displayName: 'Anna' }, personB: { id: 'p-x', displayName: 'Xavier' } } ] }) }); expect(document.body.textContent).toContain('1940'); expect(document.body.textContent).toContain('1945'); }); it('renders only "fromYear" for a relationship with no end year', async () => { render(StammbaumCard, { props: baseProps({ relationships: [ { id: 'r-2', relationType: 'NEIGHBOR', fromYear: 1935, personA: { id: 'p-1', displayName: 'Anna' }, personB: { id: 'p-y', displayName: 'Yvonne' } } ] }) }); expect(document.body.textContent).toContain('1935'); expect(document.body.textContent).not.toContain('1935–'); }); it('renders the inferred-relationships disclosure when topDerived has items', async () => { render(StammbaumCard, { props: baseProps({ inferredRelationships: [ { label: 'GRANDPARENT_OF', person: { id: 'p-grand', displayName: 'Grandma' } } ] }) }); await expect.element(page.getByText('Abgeleitete Beziehungen')).toBeVisible(); expect(document.body.textContent).toContain('Grandma'); }); it('caps the inferred relationships at 5 items', async () => { const inferred = Array.from({ length: 8 }, (_, i) => ({ label: 'COUSIN_OF', person: { id: `p-cousin-${i}`, displayName: `Cousin ${i}` } })); render(StammbaumCard, { props: baseProps({ inferredRelationships: inferred }) }); const items = document.querySelectorAll('details ul > li'); expect(items.length).toBe(5); }); });