Files
familienarchiv/frontend/src/lib/geschichte/GeschichtenCard.svelte.test.ts
Marcel df5d880e09
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m17s
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 3m42s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m4s
fix(review): GeschichtenCard uses GeschichteSummary type; focus-visible on journey links; fix stale tests
- GeschichtenCard.svelte: use GeschichteSummary instead of Geschichte
  (list endpoint returns summaries; no items/createdAt/updatedAt needed)
- GeschichtenCard.svelte.test.ts: factory returns GeschichteSummary with
  lean author shape; drop Geschichte-only fields (createdAt, groups, etc.)
- geschichten/[id]/+page.svelte: add focus:outline-none focus-visible:ring-2
  focus-visible:ring-focus-ring to journey item document links (WCAG 2.4.7)
- page.svelte.test.ts ([id]): replace stale documents[] factory field with
  items[]; test now checks placeholder text + note caption
- page.svelte.test.ts (new): remove removed initialDocuments from baseData;
  rename test to reflect that only initialPersons is passed through

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:01:50 +02:00

120 lines
3.8 KiB
TypeScript

import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import GeschichtenCard from './GeschichtenCard.svelte';
import type { components } from '$lib/generated/api';
type GeschichteSummary = components['schemas']['GeschichteSummary'];
afterEach(cleanup);
const makeGeschichte = (overrides: Record<string, unknown> = {}): GeschichteSummary =>
({
id: 'g1',
title: 'Reise nach Berlin',
body: '<p>Brief text</p>',
status: 'PUBLISHED' as const,
type: 'STORY' as const,
publishedAt: '2026-04-15T10:00:00Z',
author: {
email: 'a@b',
firstName: 'Anna',
lastName: 'Schmidt'
},
...overrides
}) as GeschichteSummary;
const baseProps = (overrides: Record<string, unknown> = {}) => ({
geschichten: [] as GeschichteSummary[],
personId: 'p-1',
personName: 'Anna Schmidt',
canWrite: false,
...overrides
});
describe('GeschichtenCard', () => {
it('renders nothing when geschichten is empty', async () => {
render(GeschichtenCard, { props: baseProps() });
expect(document.querySelector('section')).toBeNull();
});
it('renders the section when at least one geschichte is present', async () => {
render(GeschichtenCard, { props: baseProps({ geschichten: [makeGeschichte()] }) });
await expect.element(page.getByRole('heading', { name: /geschichten/i })).toBeVisible();
});
it('shows the write-action link when canWrite is true', async () => {
render(GeschichtenCard, {
props: baseProps({ geschichten: [makeGeschichte()], canWrite: true })
});
await expect
.element(page.getByRole('link', { name: /geschichte schreiben/i }))
.toHaveAttribute('href', '/geschichten/new?personId=p-1');
});
it('hides the write-action link when canWrite is false', async () => {
render(GeschichtenCard, { props: baseProps({ geschichten: [makeGeschichte()] }) });
await expect
.element(page.getByRole('link', { name: /geschichte schreiben/i }))
.not.toBeInTheDocument();
});
it('limits visible geschichten to 3', async () => {
const geschichten = Array.from({ length: 5 }, (_, i) =>
makeGeschichte({ id: `g${i}`, title: `Geschichte ${i + 1}` })
);
render(GeschichtenCard, { props: baseProps({ geschichten }) });
await expect.element(page.getByText('Geschichte 1')).toBeVisible();
await expect.element(page.getByText('Geschichte 3')).toBeVisible();
await expect.element(page.getByText('Geschichte 4')).not.toBeInTheDocument();
});
it('renders the show-all link in the footer when there are 3 or more', async () => {
const geschichten = Array.from({ length: 3 }, (_, i) =>
makeGeschichte({ id: `g${i}`, title: `g${i}` })
);
render(GeschichtenCard, { props: baseProps({ geschichten }) });
await expect
.element(page.getByRole('link', { name: /alle geschichten zu anna schmidt/i }))
.toHaveAttribute('href', '/geschichten?personId=p-1');
});
it('hides the show-all footer when fewer than 3 geschichten', async () => {
render(GeschichtenCard, {
props: baseProps({
geschichten: [makeGeschichte({ id: 'g1' }), makeGeschichte({ id: 'g2', title: 'Two' })]
})
});
await expect
.element(page.getByRole('link', { name: /alle geschichten zu/i }))
.not.toBeInTheDocument();
});
it('renders the author full name when both first and last names are set', async () => {
render(GeschichtenCard, { props: baseProps({ geschichten: [makeGeschichte()] }) });
await expect.element(page.getByText(/Anna Schmidt/)).toBeVisible();
});
it('falls back to author email when no name', async () => {
render(GeschichtenCard, {
props: baseProps({
geschichten: [
makeGeschichte({
author: { firstName: undefined, lastName: undefined, email: 'fallback@x' }
})
]
})
});
await expect.element(page.getByText(/fallback@x/)).toBeVisible();
});
});