Files
familienarchiv/frontend/src/lib/activity/DashboardActivityFeed.svelte.test.ts
Marcel 23bae62248 test(activity): cover DashboardActivityFeed branches
Caption + show-all link, empty/non-empty conditional, actor avatar
vs question-mark fallback, rollup count badge presence, youMentioned
badge, verbMap entries (TEXT_SAVED, FILE_UPLOADED), unknown-kind
fallback, rollup time range, actor name vs initials fallback,
document href construction.

13 tests, ~50 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:50:28 +02:00

162 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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> = {}): 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:0214: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();
});
});