test(notification-bell): cover handleMarkRead annotationId and commentId-only paths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
82
frontend/src/lib/components/NotificationBell.svelte.spec.ts
Normal file
82
frontend/src/lib/components/NotificationBell.svelte.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user