187 lines
5.6 KiB
TypeScript
187 lines
5.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
buildTopBarNotifications,
|
|
countUnreadTopBarNotifications,
|
|
getTopBarCampusLabel,
|
|
getTopBarInitials,
|
|
getTopBarRoleLabel,
|
|
} from '@/business/top-bar/selectors';
|
|
import { APP_ROUTE_PATHS } from '@/shared/constants/routes';
|
|
import type { CommunicationEventDto } from '@/shared/types/communications';
|
|
import type { PolicyAcknowledgmentDto } from '@/shared/types/policyDocuments';
|
|
|
|
function createCommunicationEvent(overrides: Partial<CommunicationEventDto> = {}): CommunicationEventDto {
|
|
return {
|
|
id: 'event-1',
|
|
title: 'Campus meeting',
|
|
date: '2026-06-15',
|
|
type: 'meeting',
|
|
targetLevel: 'campus',
|
|
roles: ['director'],
|
|
organizationId: 'org-1',
|
|
campusId: 'campus-1',
|
|
schoolId: null,
|
|
classId: null,
|
|
canceledEventId: null,
|
|
createdById: 'user-1',
|
|
updatedById: null,
|
|
createdAt: '2026-06-15T00:00:00Z',
|
|
updatedAt: '2026-06-15T00:00:00Z',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function createPolicyAcknowledgment(
|
|
overrides: Partial<PolicyAcknowledgmentDto> = {},
|
|
): PolicyAcknowledgmentDto {
|
|
return {
|
|
id: 'ack-1',
|
|
policyDocumentId: 'handbook-1',
|
|
version: 1,
|
|
userId: 'user-1',
|
|
acknowledgedAt: '2026-06-15T00:00:00Z',
|
|
organizationId: 'org-1',
|
|
campusId: null,
|
|
createdAt: '2026-06-15T00:00:00Z',
|
|
updatedAt: '2026-06-15T00:00:00Z',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('top bar selectors', () => {
|
|
it('surfaces an unread zone check-in nudge (linking to the zones page) only when needed', () => {
|
|
expect(buildTopBarNotifications({ needsZoneCheckIn: false })).toEqual([]);
|
|
|
|
const withNudge = buildTopBarNotifications({ needsZoneCheckIn: true });
|
|
expect(withNudge).toHaveLength(1);
|
|
expect(withNudge[0]).toMatchObject({ unread: true, href: APP_ROUTE_PATHS.zones });
|
|
expect(countUnreadTopBarNotifications(withNudge)).toBe(1);
|
|
});
|
|
|
|
it('surfaces an unread QBS quiz reminder when the weekly quiz is incomplete', () => {
|
|
expect(buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
needsSafetyQuiz: false,
|
|
})).toEqual([]);
|
|
|
|
const withReminder = buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
needsSafetyQuiz: true,
|
|
});
|
|
|
|
expect(withReminder).toEqual([{
|
|
id: 'safety-quiz-weekly',
|
|
text: "You haven't completed this week's QBS safety quiz",
|
|
time: 'This week',
|
|
unread: true,
|
|
href: APP_ROUTE_PATHS.qbs,
|
|
}]);
|
|
});
|
|
|
|
it('surfaces EI self-assessment and personality quiz completion reminders', () => {
|
|
const reminders = buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
needsEiSelfAssessment: true,
|
|
needsPersonalityQuiz: true,
|
|
});
|
|
|
|
expect(reminders).toEqual([
|
|
{
|
|
id: 'ei-self-assessment',
|
|
text: "You haven't completed your EI self-assessment",
|
|
time: 'This week',
|
|
unread: true,
|
|
href: APP_ROUTE_PATHS.ei,
|
|
},
|
|
{
|
|
id: 'ei-personality-quiz',
|
|
text: "You haven't completed your personality type quiz",
|
|
time: 'Once',
|
|
unread: true,
|
|
href: APP_ROUTE_PATHS.ei,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('builds initials from display names', () => {
|
|
expect(getTopBarInitials('Guest')).toBe('G');
|
|
expect(getTopBarInitials('Ada Lovelace')).toBe('AL');
|
|
expect(getTopBarInitials(' Grace Hopper ')).toBe('GH');
|
|
});
|
|
|
|
it('surfaces unacknowledged internal alerts in the notifications menu', () => {
|
|
const unread = buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
communicationEvents: [createCommunicationEvent()],
|
|
acknowledgedCommunicationEventIds: new Set(),
|
|
});
|
|
|
|
expect(unread).toHaveLength(1);
|
|
expect(unread[0]).toMatchObject({
|
|
text: 'Internal alert: Campus meeting',
|
|
href: APP_ROUTE_PATHS.internalComm,
|
|
unread: true,
|
|
});
|
|
|
|
expect(buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
communicationEvents: [createCommunicationEvent()],
|
|
acknowledgedCommunicationEventIds: new Set(['event-1']),
|
|
})).toEqual([]);
|
|
});
|
|
|
|
it('surfaces unread handbook policies and safety protocols by document version', () => {
|
|
const unread = buildTopBarNotifications({
|
|
needsZoneCheckIn: false,
|
|
handbookPolicies: [{
|
|
id: 'handbook-1',
|
|
title: 'Parent Communication',
|
|
category: 'Communication',
|
|
content: 'Document families contact.',
|
|
version: 2,
|
|
lastUpdated: '2026-06-15',
|
|
updatedBy: 'Director',
|
|
}],
|
|
safetyProtocols: [{
|
|
id: 'safety-1',
|
|
title: 'Fire Drill',
|
|
tag: 'fire',
|
|
steps: ['Line up'],
|
|
autismConsiderations: [],
|
|
version: 1,
|
|
lastUpdated: '2026-06-15',
|
|
author: 'Director',
|
|
}],
|
|
policyAcknowledgments: [
|
|
createPolicyAcknowledgment({ policyDocumentId: 'handbook-1', version: 1 }),
|
|
createPolicyAcknowledgment({ id: 'ack-2', policyDocumentId: 'safety-1', version: 1 }),
|
|
],
|
|
});
|
|
|
|
expect(unread).toEqual([{
|
|
id: 'handbook-policy-handbook-1-v2',
|
|
text: 'Unread handbook policy: Parent Communication',
|
|
time: 'Version 2',
|
|
unread: true,
|
|
href: APP_ROUTE_PATHS.handbook,
|
|
}]);
|
|
});
|
|
|
|
it('falls back to default campus label', () => {
|
|
expect(getTopBarCampusLabel()).toBe('Current Campus');
|
|
});
|
|
|
|
it('uses shared auth role labels', () => {
|
|
expect(getTopBarRoleLabel('support_staff')).toBe('Support Staff');
|
|
expect(getTopBarRoleLabel('office_manager')).toBe('Office Manager');
|
|
});
|
|
|
|
it('counts unread notifications', () => {
|
|
expect(countUnreadTopBarNotifications([
|
|
{ id: '1', text: 'One', time: 'Now', unread: true },
|
|
{ id: '2', text: 'Two', time: 'Earlier', unread: false },
|
|
])).toBe(1);
|
|
});
|
|
});
|