import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import DashboardActivityFeed from './DashboardActivityFeed.svelte'; import type { components } from '$lib/generated/api'; type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO']; afterEach(cleanup); const baseItem = (overrides: Partial = {}): ActivityFeedItemDTO => ({ kind: 'TEXT_SAVED', documentId: 'doc-1', documentTitle: 'Brief 1899', actor: { id: 'u-1', name: 'Anna Schmidt', initials: 'AS', color: '#336699' }, count: 1, happenedAt: '2026-04-14T14:02:00Z', happenedAtUntil: null, youMentioned: false, ...overrides }) as ActivityFeedItemDTO; describe('DashboardActivityFeed', () => { it('renders the feed caption and show-all link', async () => { render(DashboardActivityFeed, { props: { feed: [] } }); await expect.element(page.getByText('Kommentare & Aktivität')).toBeVisible(); const link = document.querySelector('a[href="/aktivitaeten"]'); expect(link).not.toBeNull(); }); it('renders nothing in the list when the feed is empty', async () => { render(DashboardActivityFeed, { props: { feed: [] } }); const lists = document.querySelectorAll('ul'); expect(lists.length).toBe(0); }); it('renders one row per feed item with the actor initials', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem(), baseItem({ documentId: 'doc-2', documentTitle: 'Brief 1900' })] } }); const items = document.querySelectorAll('li'); expect(items.length).toBe(2); expect(document.body.textContent).toContain('AS'); }); it('renders the question-mark badge when no actor is set', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ actor: null as unknown as undefined })] } }); const li = document.querySelector('li'); expect(li?.textContent).toContain('?'); }); it('renders the rollup count badge when count > 1', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ count: 5 })] } }); const badge = document.querySelector('[data-testid="feed-rollup-count"]'); expect(badge?.textContent?.trim()).toBe('5'); }); it('omits the rollup count badge when count is 1', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ count: 1 })] } }); const badge = document.querySelector('[data-testid="feed-rollup-count"]'); expect(badge).toBeNull(); }); it('renders the "für dich" badge when youMentioned is true', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ youMentioned: true })] } }); await expect.element(page.getByText(/für dich/i)).toBeVisible(); }); it('maps the kind enum to a localized verb (TEXT_SAVED)', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ kind: 'TEXT_SAVED' as ActivityFeedItemDTO['kind'] })] } }); expect(document.body.textContent).toContain('hat Text gespeichert in'); }); it('maps the kind enum to a localized verb (FILE_UPLOADED)', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ kind: 'FILE_UPLOADED' as ActivityFeedItemDTO['kind'] })] } }); expect(document.body.textContent).toContain('hat eine Datei hochgeladen'); }); it('falls back to the raw kind when no verb is mapped', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ kind: 'UNKNOWN_KIND' as unknown as ActivityFeedItemDTO['kind'] })] } }); expect(document.body.textContent).toContain('UNKNOWN_KIND'); }); it('renders a rollup time range when happenedAtUntil is set and count > 1', async () => { render(DashboardActivityFeed, { props: { feed: [ baseItem({ happenedAt: '2026-04-14T14:02:00Z', happenedAtUntil: '2026-04-14T14:32:00Z', count: 3 }) ] } }); // "14:02–14:32" appears (with the en-dash) expect(document.body.textContent).toMatch(/\d{2}:\d{2}–\d{2}:\d{2}/); }); it('uses the actor initials as the fallback name when name is null', async () => { render(DashboardActivityFeed, { props: { feed: [ baseItem({ actor: { id: 'u-2', name: null as unknown as undefined, initials: 'XR', color: '#000' } }) ] } }); const strong = document.querySelector('strong'); expect(strong?.textContent).toBe('XR'); }); it('builds the document detail href from documentId', async () => { render(DashboardActivityFeed, { props: { feed: [baseItem({ documentId: 'doc-xyz', documentTitle: 'Brief 1901' })] } }); const link = document.querySelector('a[href="/documents/doc-xyz"]'); expect(link).not.toBeNull(); }); });