mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-12 23:31:02 +00:00
b9d0d3397e
They are outside scripts/format-baseline.json, so new code there must be formatted; Prettier only, no change in behaviour.
113 lines
4.5 KiB
TypeScript
113 lines
4.5 KiB
TypeScript
/**
|
|
* FE-NOTIF-MODEL-001 to FE-NOTIF-MODEL-009
|
|
*
|
|
* What a notification's tile shows, how the server's time is read, and how the
|
|
* list is cut into days under headings in the reader's language.
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { render, screen } from '../../../tests/helpers/render';
|
|
import type { InAppNotification } from '../../store/inAppNotificationStore';
|
|
import NotificationDayList from './NotificationDayList';
|
|
import { KIND_LOOK, createdAt, dayLabel, groupByDay, kindOf, localizedParams } from './notificationModel';
|
|
|
|
const note = (id: number, created_at: string, title_key = 'notif.trip_invite.title'): InAppNotification => ({
|
|
id,
|
|
type: 'simple',
|
|
scope: 'trip',
|
|
target: 1,
|
|
sender_id: null,
|
|
sender_username: null,
|
|
sender_avatar: null,
|
|
recipient_id: 1,
|
|
title_key,
|
|
title_params: {},
|
|
text_key: 'notifications.empty',
|
|
text_params: {},
|
|
positive_text_key: null,
|
|
negative_text_key: null,
|
|
response: null,
|
|
navigate_text_key: null,
|
|
navigate_target: null,
|
|
is_read: false,
|
|
created_at,
|
|
});
|
|
|
|
const NOW = new Date(2026, 9, 24, 15, 0);
|
|
const at = (days: number, hour = 10) => new Date(2026, 9, 24 - days, hour, 0);
|
|
|
|
describe('notificationModel', () => {
|
|
it('FE-NOTIF-MODEL-001: every known title has its kind, anything else is the plain bell', () => {
|
|
expect(kindOf('notif.collab_message.title')).toBe('message');
|
|
expect(kindOf('notif.replica_failure.title')).toBe('alert');
|
|
expect(kindOf('plugin.something.title')).toBe('other');
|
|
for (const look of Object.values(KIND_LOOK)) expect(look.tone).toMatch(/^bg-/);
|
|
});
|
|
|
|
it("FE-NOTIF-MODEL-002: the server's UTC text is read as UTC, an ISO string as it is", () => {
|
|
expect(createdAt('2026-10-24 08:30:00').toISOString()).toBe('2026-10-24T08:30:00.000Z');
|
|
expect(createdAt('2026-10-24T08:30:00.000Z').toISOString()).toBe('2026-10-24T08:30:00.000Z');
|
|
});
|
|
|
|
it('FE-NOTIF-MODEL-003: today and yesterday by name, the week by weekday, older by date', () => {
|
|
expect(dayLabel(at(0), 'en', NOW)).toBe('Today');
|
|
expect(dayLabel(at(1), 'en', NOW)).toBe('Yesterday');
|
|
expect(dayLabel(at(3), 'en', NOW)).toBe('Wednesday');
|
|
expect(dayLabel(at(20), 'en', NOW)).toBe('October 4');
|
|
expect(dayLabel(new Date(2025, 11, 31), 'en', NOW)).toBe('December 31, 2025');
|
|
});
|
|
|
|
it('FE-NOTIF-MODEL-004: the headings follow the language', () => {
|
|
expect(dayLabel(at(0), 'de', NOW)).toBe('Heute');
|
|
expect(dayLabel(at(1), 'de', NOW)).toBe('Gestern');
|
|
});
|
|
|
|
it('FE-NOTIF-MODEL-005: the list is cut into days in the order it came', () => {
|
|
const groups = groupByDay(
|
|
[note(1, at(0, 12).toISOString()), note(2, at(0, 9).toISOString()), note(3, at(2).toISOString())],
|
|
'en',
|
|
NOW
|
|
);
|
|
expect(groups.map((g) => [g.label, g.items.map((n) => n.id)])).toEqual([
|
|
['Today', [1, 2]],
|
|
['Thursday', [3]],
|
|
]);
|
|
});
|
|
|
|
it('FE-NOTIF-MODEL-006: an empty list has no days', () => {
|
|
expect(groupByDay([], 'en', NOW)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('NotificationDayList', () => {
|
|
it('FE-NOTIF-MODEL-007: each day is a section under its heading', () => {
|
|
const now = new Date();
|
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 12);
|
|
render(<NotificationDayList notifications={[note(1, now.toISOString()), note(2, yesterday.toISOString())]} />);
|
|
expect(screen.getByRole('heading', { name: 'Today' })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: 'Yesterday' })).toBeInTheDocument();
|
|
expect(screen.getAllByRole('region')).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe('localizedParams', () => {
|
|
const NOW = new Date(2026, 10, 7, 12);
|
|
|
|
it('FE-NOTIF-MODEL-008: a due day sent as YYYY-MM-DD reads as the short date of the language', () => {
|
|
const params = { todo: 'Exchange the JR Pass', trip: 'Kyoto', due: '2026-11-05' };
|
|
expect(localizedParams(params, 'de', NOW)).toEqual({ todo: 'Exchange the JR Pass', trip: 'Kyoto', due: '5. Nov.' });
|
|
expect(localizedParams(params, 'en-US', NOW).due).toBe('Nov 5');
|
|
expect(localizedParams(params, 'ru', NOW).due).toBe('5 нояб.');
|
|
// Another year names it.
|
|
expect(localizedParams({ due: '2027-01-02' }, 'en-US', NOW).due).toBe('Jan 2, 2027');
|
|
});
|
|
|
|
it('FE-NOTIF-MODEL-009: anything that is not a bare date passes through, and no params stay none', () => {
|
|
expect(localizedParams({ trip: '2026-11-05 at noon', n: '3' }, 'de', NOW)).toEqual({
|
|
trip: '2026-11-05 at noon',
|
|
n: '3',
|
|
});
|
|
expect(localizedParams(undefined, 'de', NOW)).toEqual({});
|
|
});
|
|
});
|