diff --git a/frontend/src/lib/shared/dashboard/ReaderHeaderBar.svelte.test.ts b/frontend/src/lib/shared/dashboard/ReaderHeaderBar.svelte.test.ts new file mode 100644 index 00000000..84342013 --- /dev/null +++ b/frontend/src/lib/shared/dashboard/ReaderHeaderBar.svelte.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import ReaderHeaderBar from './ReaderHeaderBar.svelte'; + +afterEach(cleanup); + +const baseProps = (overrides: Record = {}) => ({ + name: 'Anna', + documents: 50, + persons: 12, + stories: 5, + ...overrides +}); + +describe('ReaderHeaderBar', () => { + it('renders the welcome greeting with name', async () => { + render(ReaderHeaderBar, { props: baseProps() }); + + await expect.element(page.getByText(/anna/i)).toBeVisible(); + }); + + it('renders Morgen label for hours before noon', async () => { + render(ReaderHeaderBar, { props: baseProps({ hour: 9 }) }); + + await expect.element(page.getByText('Morgen')).toBeVisible(); + }); + + it('renders Mittag label for hours 12-17', async () => { + render(ReaderHeaderBar, { props: baseProps({ hour: 14 }) }); + + await expect.element(page.getByText('Mittag')).toBeVisible(); + }); + + it('renders Abend label for hours 18+', async () => { + render(ReaderHeaderBar, { props: baseProps({ hour: 20 }) }); + + await expect.element(page.getByText('Abend')).toBeVisible(); + }); + + it('renders the stats counts and links', async () => { + render(ReaderHeaderBar, { props: baseProps() }); + + // Counts visible somewhere + expect(document.body.textContent).toContain('50'); + expect(document.body.textContent).toContain('12'); + + const docsLink = document.querySelector('a[href="/documents"]'); + const personsLink = document.querySelector('a[href="/persons"]'); + const storiesLink = document.querySelector('a[href="/geschichten"]'); + expect(docsLink).not.toBeNull(); + expect(personsLink).not.toBeNull(); + expect(storiesLink).not.toBeNull(); + }); + + it('renders em-dash when stats are null', async () => { + render(ReaderHeaderBar, { + props: baseProps({ documents: null, persons: null, stories: null }) + }); + + const dashes = Array.from(document.querySelectorAll('span.text-2xl')); + const dashCount = dashes.filter((el) => el.textContent?.trim() === '—').length; + expect(dashCount).toBe(3); + }); +}); diff --git a/frontend/src/lib/shared/dashboard/ReaderRecentStories.svelte.test.ts b/frontend/src/lib/shared/dashboard/ReaderRecentStories.svelte.test.ts new file mode 100644 index 00000000..868bf660 --- /dev/null +++ b/frontend/src/lib/shared/dashboard/ReaderRecentStories.svelte.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import ReaderRecentStories from './ReaderRecentStories.svelte'; + +afterEach(cleanup); + +const makeStory = (overrides: Record = {}) => ({ + id: 'g1', + title: 'Reise nach Berlin', + body: '

Brief text content

', + publishedAt: '2026-04-15T10:00:00Z', + updatedAt: '2026-04-15T10:00:00Z', + ...overrides +}); + +describe('ReaderRecentStories', () => { + it('renders nothing when stories is empty', async () => { + render(ReaderRecentStories, { props: { stories: [] } }); + + expect(document.querySelector('h3')).toBeNull(); + }); + + it('renders the heading and one row per story', async () => { + render(ReaderRecentStories, { + props: { + stories: [ + makeStory({ id: 'g1', title: 'Story 1' }), + makeStory({ id: 'g2', title: 'Story 2' }) + ] + } + }); + + await expect.element(page.getByRole('heading', { name: /neue geschichten/i })).toBeVisible(); + await expect.element(page.getByText('Story 1')).toBeVisible(); + await expect.element(page.getByText('Story 2')).toBeVisible(); + }); + + it('renders the link to /geschichten in the header', async () => { + render(ReaderRecentStories, { props: { stories: [makeStory()] } }); + + await expect + .element(page.getByRole('link', { name: /alle geschichten/i })) + .toHaveAttribute('href', '/geschichten'); + }); + + it('renders the story link to /geschichten/{id}', async () => { + render(ReaderRecentStories, { props: { stories: [makeStory({ id: 'g-42' })] } }); + + const links = document.querySelectorAll('a[href^="/geschichten/"]'); + const detailLinks = Array.from(links).filter((a) => + (a as HTMLAnchorElement).href.includes('/geschichten/g-42') + ); + expect(detailLinks.length).toBe(1); + }); + + it('renders the body excerpt when present', async () => { + render(ReaderRecentStories, { + props: { stories: [makeStory({ body: '

Once upon a time in 1923

' })] } + }); + + await expect.element(page.getByText(/Once upon a time in 1923/)).toBeVisible(); + }); + + it('omits the excerpt paragraph when body is empty', async () => { + render(ReaderRecentStories, { + props: { stories: [makeStory({ body: '' })] } + }); + + const paragraphs = document.querySelectorAll('p'); + expect(paragraphs.length).toBe(0); + }); +});