Files
familienarchiv/frontend/src/routes/aktivitaeten/page.server.spec.ts
Marcel 567612761d refactor: move lib-root files to lib/shared/ and finalize domain structure
- Move api.server.ts, errors.ts, types.ts, utils.ts, relativeTime.ts to lib/shared/
- Move person relationship components to lib/person/relationship/
- Move Stammbaum components to lib/person/genealogy/
- Move HelpPopover to lib/shared/primitives/
- Update all import paths across routes, specs, and lib files
- Update vi.mock() paths in server-project test files
- Remove now-empty legacy directories (components/, hooks/, server/, etc.)
- Update vite.config.ts coverage include paths for new structure
- Update frontend/CLAUDE.md to reflect domain-based lib/ layout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:53:31 +02:00

136 lines
4.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { load } from './+page.server';
const mockApi = {
GET: vi.fn()
};
vi.mock('$lib/shared/api.server', () => ({
createApiClient: () => mockApi
}));
function buildUrl(search = ''): URL {
return new URL(`http://localhost/aktivitaeten${search}`);
}
function mockSuccess() {
mockApi.GET.mockImplementation((path: string) => {
if (path === '/api/dashboard/activity') {
return Promise.resolve({ response: { ok: true }, data: [] });
}
return Promise.resolve({ response: { ok: true }, data: { content: [] } });
});
}
beforeEach(() => {
vi.clearAllMocks();
});
describe('aktivitaeten/load — core', () => {
it('requests only unread notifications for Für-dich', async () => {
mockSuccess();
await load({ fetch, url: buildUrl() } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/notifications', {
params: { query: { read: false, page: 0, size: 20 } }
});
});
it('returns the activity feed and unread notifications on success', async () => {
const feed = [{ kind: 'FILE_UPLOADED', documentId: 'd1' }];
const unread = [{ id: 'n1', type: 'MENTION' }];
mockApi.GET.mockImplementation((path: string) => {
if (path === '/api/dashboard/activity') {
return Promise.resolve({ response: { ok: true }, data: feed });
}
return Promise.resolve({ response: { ok: true }, data: { content: unread } });
});
const result = await load({ fetch, url: buildUrl() } as never);
expect(result.activityFeed).toEqual(feed);
expect(result.unreadNotifications).toEqual(unread);
expect(result.filter).toBe('alle');
expect(result.loadError).toBeNull();
});
it('surfaces "activity" loadError when the dashboard endpoint returns non-ok', async () => {
mockApi.GET.mockImplementation((path: string) => {
if (path === '/api/dashboard/activity') {
return Promise.resolve({ response: { ok: false, status: 500 }, error: {} });
}
return Promise.resolve({ response: { ok: true }, data: { content: [] } });
});
const result = await load({ fetch, url: buildUrl() } as never);
expect(result.loadError).toBe('activity');
expect(result.activityFeed).toEqual([]);
});
it('parses the filter query param, falling back to "alle" for invalid values', async () => {
mockApi.GET.mockResolvedValue({ response: { ok: true }, data: [] });
const validResult = await load({ fetch, url: buildUrl('?filter=fuer-dich') } as never);
expect(validResult.filter).toBe('fuer-dich');
mockApi.GET.mockResolvedValue({ response: { ok: true }, data: [] });
const invalidResult = await load({ fetch, url: buildUrl('?filter=bogus') } as never);
expect(invalidResult.filter).toBe('alle');
});
});
describe('aktivitaeten/load — kinds param per filter', () => {
it('omits kinds for filter=alle (server defaults to ROLLUP_ELIGIBLE)', async () => {
mockSuccess();
await load({ fetch, url: buildUrl() } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/dashboard/activity', {
params: { query: { limit: 40 } }
});
});
it('omits kinds for filter=fuer-dich (client-side filtering on youMentioned/youParticipated)', async () => {
mockSuccess();
await load({ fetch, url: buildUrl('?filter=fuer-dich') } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/dashboard/activity', {
params: { query: { limit: 40 } }
});
});
it('sends kinds=FILE_UPLOADED for filter=hochgeladen', async () => {
mockSuccess();
await load({ fetch, url: buildUrl('?filter=hochgeladen') } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/dashboard/activity', {
params: { query: { limit: 40, kinds: ['FILE_UPLOADED'] } }
});
});
it('sends TEXT_SAVED, BLOCK_REVIEWED, ANNOTATION_CREATED for filter=transkription', async () => {
mockSuccess();
await load({ fetch, url: buildUrl('?filter=transkription') } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/dashboard/activity', {
params: {
query: {
limit: 40,
kinds: expect.arrayContaining(['TEXT_SAVED', 'BLOCK_REVIEWED', 'ANNOTATION_CREATED'])
}
}
});
const call = mockApi.GET.mock.calls.find(([p]) => p === '/api/dashboard/activity');
expect(call[1].params.query.kinds).toHaveLength(3);
});
it('sends COMMENT_ADDED, MENTION_CREATED for filter=kommentare', async () => {
mockSuccess();
await load({ fetch, url: buildUrl('?filter=kommentare') } as never);
expect(mockApi.GET).toHaveBeenCalledWith('/api/dashboard/activity', {
params: {
query: {
limit: 40,
kinds: expect.arrayContaining(['COMMENT_ADDED', 'MENTION_CREATED'])
}
}
});
const call = mockApi.GET.mock.calls.find(([p]) => p === '/api/dashboard/activity');
expect(call[1].params.query.kinds).toHaveLength(2);
});
});