Files
TREK/client/tests/unit/stores/inAppNotificationStore.test.ts
T
2026-04-07 12:31:09 +02:00

135 lines
4.8 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { http, HttpResponse } from 'msw';
import { server } from '../../helpers/msw/server';
import { useInAppNotificationStore } from '../../../src/store/inAppNotificationStore';
import { resetAllStores } from '../../helpers/store';
// Raw notification factory matching the server shape (is_read as 0/1, params as strings)
function buildRawNotif(overrides: Record<string, unknown> = {}) {
const id = Math.floor(Math.random() * 100000);
return {
id,
type: 'simple',
scope: 'trip',
target: 1,
sender_id: 2,
sender_username: 'alice',
sender_avatar: null,
recipient_id: 1,
title_key: 'notif.title',
title_params: '{}',
text_key: 'notif.text',
text_params: '{}',
positive_text_key: null,
negative_text_key: null,
response: null,
navigate_text_key: null,
navigate_target: null,
is_read: 0,
created_at: '2025-01-01T00:00:00.000Z',
...overrides,
};
}
beforeEach(() => {
resetAllStores();
});
describe('inAppNotificationStore', () => {
describe('FE-NOTIF-001: fetchNotifications() loads first page', () => {
it('populates notifications, total, and unreadCount', async () => {
await useInAppNotificationStore.getState().fetchNotifications();
const state = useInAppNotificationStore.getState();
expect(state.notifications.length).toBeGreaterThan(0);
expect(state.total).toBeGreaterThan(0);
expect(state.unreadCount).toBe(5);
expect(state.isLoading).toBe(false);
});
});
describe('FE-NOTIF-002: Pagination — loading more appends to list', () => {
it('appends additional notifications when fetchNotifications is called again', async () => {
// First page
await useInAppNotificationStore.getState().fetchNotifications(true);
const firstPageCount = useInAppNotificationStore.getState().notifications.length;
const total = useInAppNotificationStore.getState().total;
// Only test pagination if there are more items
if (firstPageCount < total) {
await useInAppNotificationStore.getState().fetchNotifications();
const state = useInAppNotificationStore.getState();
expect(state.notifications.length).toBeGreaterThan(firstPageCount);
} else {
// All notifications fit in one page
expect(firstPageCount).toBe(total);
}
});
});
describe('FE-NOTIF-003: markRead(id)', () => {
it('updates is_read to true for the notification', async () => {
// Seed with an unread notification
const unread = buildRawNotif({ id: 42, is_read: 0 });
useInAppNotificationStore.setState({
notifications: [{ ...unread, title_params: {}, text_params: {}, is_read: false }] as never,
unreadCount: 1,
});
await useInAppNotificationStore.getState().markRead(42);
const state = useInAppNotificationStore.getState();
const notif = state.notifications.find((n) => n.id === 42);
expect(notif?.is_read).toBe(true);
expect(state.unreadCount).toBe(0);
});
});
describe('FE-NOTIF-004: handleNewNotification() prepends to list', () => {
it('adds a new notification at the start of the list', () => {
// Seed existing notifications
useInAppNotificationStore.setState({
notifications: [{ ...buildRawNotif({ id: 1 }), title_params: {}, text_params: {}, is_read: false }] as never,
total: 1,
unreadCount: 1,
});
const newRaw = buildRawNotif({ id: 99 });
useInAppNotificationStore.getState().handleNewNotification(newRaw as never);
const state = useInAppNotificationStore.getState();
expect(state.notifications[0].id).toBe(99);
expect(state.notifications.length).toBe(2);
expect(state.total).toBe(2);
expect(state.unreadCount).toBe(2);
});
});
describe('FE-NOTIF-005: handleUpdatedNotification() updates existing notification', () => {
it('replaces the notification in the list', () => {
useInAppNotificationStore.setState({
notifications: [{ ...buildRawNotif({ id: 7, is_read: 0 }), title_params: {}, text_params: {}, is_read: false }] as never,
total: 1,
unreadCount: 1,
});
const updated = buildRawNotif({ id: 7, is_read: 1 });
useInAppNotificationStore.getState().handleUpdatedNotification(updated as never);
const state = useInAppNotificationStore.getState();
const notif = state.notifications.find((n) => n.id === 7);
expect(notif?.is_read).toBe(true);
});
});
describe('FE-NOTIF-006: Unread count is correct', () => {
it('unreadCount matches the number of unread notifications', async () => {
await useInAppNotificationStore.getState().fetchNotifications(true);
const state = useInAppNotificationStore.getState();
// The mock returns 5 unread from the server
expect(state.unreadCount).toBe(5);
});
});
});