feat(chronik): deep-link mentions and comments to the specific comment #301

Merged
marcel merged 11 commits from feat/issue-300-chronik-mention-deep-link into main 2026-04-21 19:06:19 +02:00
Showing only changes of commit a76af739e5 - Show all commits

View File

@@ -0,0 +1,82 @@
import { afterEach, describe, it, expect, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import type { NotificationItem } from '$lib/utils/notifications';
import NotificationBell from './NotificationBell.svelte';
const gotoMock = vi.hoisted(() => vi.fn());
vi.mock('$app/navigation', () => ({ goto: gotoMock, beforeNavigate: vi.fn() }));
const mockMarkRead = vi.hoisted(() => vi.fn().mockResolvedValue(undefined));
const mockNotificationList = vi.hoisted((): { value: NotificationItem[] } => ({ value: [] }));
vi.mock('$lib/stores/notifications.svelte', () => ({
notificationStore: {
get notifications() {
return mockNotificationList.value;
},
get unreadCount() {
return mockNotificationList.value.length;
},
markRead: mockMarkRead,
fetchNotifications: vi.fn().mockResolvedValue(undefined),
init: vi.fn(),
destroy: vi.fn(),
markAllRead: vi.fn()
}
}));
afterEach(() => {
cleanup();
gotoMock.mockClear();
mockMarkRead.mockClear();
mockNotificationList.value = [];
});
const makeNotification = (overrides: Partial<NotificationItem> = {}): NotificationItem => ({
id: 'n1',
type: 'REPLY',
documentId: 'doc-1',
referenceId: 'ref-1',
annotationId: null,
read: false,
createdAt: '2026-04-21T10:00:00Z',
actorName: 'Anna',
documentTitle: 'Test Doc',
...overrides
});
async function openDropdownAndClickFirstNotification() {
const bellButton = document.querySelector<HTMLButtonElement>('button[aria-haspopup="true"]')!;
bellButton.click();
await vi.waitFor(() => {
expect(document.querySelector('[role="dialog"]')).not.toBeNull();
});
const notifButton = document.querySelector<HTMLButtonElement>('[role="list"] button')!;
notifButton.click();
}
describe('NotificationBell', () => {
it('handleMarkRead navigates to URL including annotationId when notification has annotationId', async () => {
mockNotificationList.value = [makeNotification({ annotationId: 'annot-1' })];
render(NotificationBell);
await openDropdownAndClickFirstNotification();
await vi.waitFor(() => {
expect(gotoMock).toHaveBeenCalledWith(
'/documents/doc-1?commentId=ref-1&annotationId=annot-1'
);
});
});
it('handleMarkRead navigates to commentId-only URL when annotationId is absent', async () => {
mockNotificationList.value = [makeNotification({ annotationId: null })];
render(NotificationBell);
await openDropdownAndClickFirstNotification();
await vi.waitFor(() => {
expect(gotoMock).toHaveBeenCalledWith('/documents/doc-1?commentId=ref-1');
});
});
});