Files
familienarchiv/frontend/src/lib/person/genealogy/StammbaumCard.svelte.test.ts
Marcel 78c01d4561 test(genealogy): expand StammbaumCard coverage
Adds direct-relationship sorting, yearRange formatting (both years,
only fromYear), inferred-relationships disclosure rendering, 5-item
cap on derived relationships.

5 new tests targeting ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:50:28 +02:00

183 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, unknown> = {}) => ({
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 "fromto" 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);
});
});