import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import ReaderRecentStories from './ReaderRecentStories.svelte'; import type { components } from '$lib/generated/api'; type Geschichte = components['schemas']['Geschichte']; afterEach(() => { cleanup(); }); const story1: Geschichte = { id: 'g1', title: 'Die Familie Müller', body: '

Dies ist eine sehr lange Geschichte über die Familie Müller. Sie lebten in Bayern und hatten viele Kinder. Das war früher so üblich in diesen Gebieten.

', status: 'PUBLISHED', createdAt: '2025-01-01T00:00:00Z', updatedAt: '2025-01-01T00:00:00Z', publishedAt: '2025-01-01T00:00:00Z' }; const longBodyStory: Geschichte = { id: 'g2', title: 'Sehr lange Geschichte', body: '

' + 'A'.repeat(200) + '

', status: 'PUBLISHED', createdAt: '2025-02-01T00:00:00Z', updatedAt: '2025-02-01T00:00:00Z', publishedAt: '2025-02-01T00:00:00Z' }; describe('ReaderRecentStories', () => { it('renders a link to /geschichten/{id} for each story', async () => { render(ReaderRecentStories, { stories: [story1] }); const link = page.getByRole('link', { name: /Die Familie Müller/ }); await expect.element(link).toHaveAttribute('href', '/geschichten/g1'); }); it('truncates body excerpt to 150 characters and strips HTML', async () => { render(ReaderRecentStories, { stories: [longBodyStory] }); const excerpt = page.getByText(/A{100,150}/); await expect.element(excerpt).toBeInTheDocument(); const text = ((await excerpt.element()) as HTMLElement).textContent; expect(text!.replace(/…$/, '').length).toBeLessThanOrEqual(150); }); it('shows empty state when stories array is empty', async () => { render(ReaderRecentStories, { stories: [] }); const links = page.getByRole('link'); await expect.element(links).not.toBeInTheDocument(); }); it('renders "Alle Geschichten" link pointing to /geschichten', async () => { render(ReaderRecentStories, { stories: [story1] }); const allLink = page.getByRole('link', { name: /Alle Geschichten/i }); await expect.element(allLink).toHaveAttribute('href', '/geschichten'); }); it('exposes a focus-visible ring on the "Alle Geschichten" link', async () => { render(ReaderRecentStories, { stories: [story1] }); const allLink = page.getByRole('link', { name: /Alle Geschichten/i }); const cls = ((await allLink.element()) as HTMLElement).className; expect(cls).toMatch(/focus-visible:ring-2/); expect(cls).toMatch(/focus-visible:ring-brand-navy/); }); it('meets the 44px touch target on the "Alle Geschichten" link', async () => { render(ReaderRecentStories, { stories: [story1] }); const allLink = page.getByRole('link', { name: /Alle Geschichten/i }); const cls = ((await allLink.element()) as HTMLElement).className; expect(cls).toMatch(/min-h-\[44px\]/); }); it('card-head contains an h3 (not h2)', async () => { render(ReaderRecentStories, { stories: [story1] }); const h3 = page.getByRole('heading', { level: 3 }); await expect.element(h3).toBeInTheDocument(); const h2 = page.getByRole('heading', { level: 2 }); await expect.element(h2).not.toBeInTheDocument(); }); it('card-head div has border-b and border-line classes', async () => { render(ReaderRecentStories, { stories: [story1] }); const h3 = page.getByRole('heading', { level: 3 }); const cardHead = ((await h3.element()) as HTMLElement).parentElement; expect(cardHead?.className).toMatch(/border-b/); expect(cardHead?.className).toMatch(/border-line/); }); it('"Alle Geschichten" link is inside the card-head (sibling of h3)', async () => { render(ReaderRecentStories, { stories: [story1] }); const h3 = page.getByRole('heading', { level: 3 }); const cardHead = ((await h3.element()) as HTMLElement).parentElement; const allLink = cardHead?.querySelector('a'); expect(allLink).not.toBeNull(); expect(allLink?.textContent?.trim()).toMatch(/Alle Geschichten/i); }); it('story-row link has min-h-[44px] touch target', async () => { render(ReaderRecentStories, { stories: [story1] }); const link = page.getByRole('link', { name: /Die Familie Müller/ }); const cls = ((await link.element()) as HTMLElement).className; expect(cls).toMatch(/min-h-\[44px\]/); }); it('excerpt has text-ink-2 class', async () => { render(ReaderRecentStories, { stories: [story1] }); const link = page.getByRole('link', { name: /Die Familie Müller/ }); const el = (await link.element()) as HTMLElement; const excerptEl = el.querySelector('p'); expect(excerptEl?.className).toMatch(/text-ink-2/); }); });