import { afterEach, describe, expect, it } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import GeschichtenCard from './GeschichtenCard.svelte'; const makeStory = (id: string, title: string, body: string | null = '

Body

') => ({ id, title, body, status: 'PUBLISHED' as const, publishedAt: '2024-04-01T12:00:00', createdAt: '2024-03-01T12:00:00', updatedAt: '2024-04-01T12:00:00', persons: [], documents: [], author: { id: 'u1', email: 'marcel@example.com', firstName: 'Marcel', lastName: 'Raddatz', enabled: true, notifyOnReply: false, notifyOnMention: false, groups: [], createdAt: '2024-01-01T00:00:00', color: '#000' } }); afterEach(() => cleanup()); describe('GeschichtenCard', () => { it('renders nothing when geschichten is empty', async () => { render(GeschichtenCard, { geschichten: [], personId: 'p1', personName: 'Franz', canWrite: true }); // No heading, no list — the entire
should not exist expect( document.querySelector('section[aria-labelledby="geschichten-card-heading"]') ).toBeNull(); }); it('renders the section heading and stories when geschichten is non-empty', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'Erinnerung an Franz')], personId: 'p1', personName: 'Franz', canWrite: false }); await expect.element(page.getByText('Geschichten')).toBeInTheDocument(); // The whole row is one link to the story; matching on the title text via // a partial regex tolerates trailing author/date metadata in the // accessible name. const link = await page .getByRole('link', { name: /Erinnerung an Franz/ }) .first() .element(); expect(link.getAttribute('href')).toBe('/geschichten/g1'); }); it('makes the entire story row a single clickable link', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'A title', '

Some body excerpt text

')], personId: 'p1', personName: 'Franz', canWrite: false }); // The body-excerpt text is inside the same as the title. const links = await page.getByRole('link', { name: /A title/ }).all(); expect(links.length).toBeGreaterThan(0); const linkEl = await links[0].element(); expect(linkEl.tagName).toBe('A'); expect(linkEl.textContent).toContain('Some body excerpt text'); }); it('hides the "+ Geschichte schreiben" link when canWrite is false', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'A story')], personId: 'p1', personName: 'Franz', canWrite: false }); const writeLinks = await page.getByText(/Geschichte schreiben/).all(); expect(writeLinks).toHaveLength(0); }); it('shows the write-action link only when canWrite is true', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'A story')], personId: 'p1', personName: 'Franz', canWrite: true }); const link = await page.getByRole('link', { name: /Geschichte schreiben/ }).element(); expect(link.getAttribute('href')).toBe('/geschichten/new?personId=p1'); }); it('hides the "Alle Geschichten zu …" footer link below the 3-story threshold', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'A'), makeStory('g2', 'B')], personId: 'p1', personName: 'Franz', canWrite: false }); const overflow = await page.getByText(/Alle Geschichten zu/).all(); expect(overflow).toHaveLength(0); }); it('shows the footer link at the 3-story threshold (>= 3)', async () => { render(GeschichtenCard, { geschichten: [makeStory('g1', 'A'), makeStory('g2', 'B'), makeStory('g3', 'C')], personId: 'p1', personName: 'Franz', canWrite: false }); const link = await page.getByRole('link', { name: /Alle Geschichten zu Franz/ }).element(); expect(link.getAttribute('href')).toBe('/geschichten?personId=p1'); }); it('renders a plain-text excerpt without HTML markup', async () => { render(GeschichtenCard, { geschichten: [ makeStory( 'g1', 'Mit HTML', '

Plain bold story

' ) ], personId: 'p1', personName: 'Franz', canWrite: false }); // Body excerpt appears once as plain text — no rendered, no script await expect.element(page.getByText(/Plain bold story/)).toBeInTheDocument(); expect(document.body.innerHTML).not.toContain('