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>
183 lines
5.4 KiB
TypeScript
183 lines
5.4 KiB
TypeScript
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 "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);
|
||
});
|
||
});
|