Notifications
Notification List & Filtering
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-001 | View notification bell with no unread notifications | Logged in as user with zero unread notifications | 1. Navigate to home/dashboard 2. Locate bell icon in top navigation | Bell icon displays zero badge or no badge; icon is visible | High |
| NOTIF-002 | View notification bell with unread notifications | Logged in as user with 3+ unread notifications (contact_reminder, lead_assigned, etc.) | 1. Navigate to home/dashboard 2. Locate bell icon in top navigation | Bell icon displays red badge with unread count (e.g., "3"); icon is prominent | High |
| NOTIF-003 | Open notification dropdown | Logged in as user with 5+ notifications | 1. Click bell icon 2. Observe dropdown opens | Dropdown slides/fades in showing notification list; loading spinner briefly visible if fetching | High |
| NOTIF-004 | View notifications grouped by date (Today, Yesterday, Earlier) | Logged in as user with notifications created at: today 2 hours ago, yesterday 8 hours ago, 5 days ago | 1. Click bell icon to open dropdown 2. Observe grouped sections | Notifications grouped in collapsible/visible sections: "Today" (1 item), "Yesterday" (1 item), "Earlier" (1 item); each with correct timestamps | High |
| NOTIF-005 | View relative timestamps in notification dropdown | Logged in as user with notifications | 1. Click bell icon 2. Read timestamps on notifications | Timestamps display as relative (e.g., "2m ago", "1h ago", "Yesterday", "5 days ago"); updates dynamically without refresh | High |
| NOTIF-006 | Filter notifications: All | Logged in as user with 3 read + 2 unread notifications | 1. Click bell icon 2. Click "All" filter tab 3. Observe list | List shows all 5 notifications; "All" tab is highlighted/active | High |
| NOTIF-007 | Filter notifications: Unread only | Logged in as user with 3 read + 2 unread notifications | 1. Click bell icon 2. Click "Unread" filter tab 3. Observe list | List shows only 2 unread notifications; "Unread" tab is highlighted/active | High |
| NOTIF-008 | Switch between All and Unread filters | Logged in as user with mixed read/unread notifications | 1. Click bell icon 2. Click "All" tab 3. See 5 items 4. Click "Unread" tab 5. See 2 items 6. Click "All" again | List updates smoothly when switching; no flickering; correct counts displayed each time | High |
| NOTIF-009 | Pagination: View first page of notifications | Logged in as user with 30+ notifications | 1. Click bell icon 2. Observe initial list loads | First 25 notifications visible by default; pagination controls visible if more than 25 exist | High |
| NOTIF-010 | Pagination: Navigate to next page | Logged in as user with 50+ notifications on page 1 | 1. Click bell icon 2. Scroll to bottom or click "Next" button 3. Observe page 2 loads | Page 2 notifications load; previous page items no longer visible; "Previous" button enabled if on page 2+ | Medium |
| NOTIF-011 | Empty state: No notifications | Logged in as user with zero notifications | 1. Click bell icon 2. Observe dropdown | Dropdown shows "No notifications" or empty state message; no items listed | Medium |
| NOTIF-012 | Empty state: No unread notifications | Logged in as user with 5 read notifications, zero unread | 1. Click bell icon 2. Click "Unread" filter | Dropdown shows "No unread notifications" message; "All" tab shows 5 items | Medium |
Notification Types & Metadata Display
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-013 | Display contact_reminder notification | Contact reminder due for logged-in user; notification created | 1. Click bell icon 2. Locate contact_reminder notification (type=contact_reminder) | Notification displays: š icon, amber/orange color (#d97706), light amber background (#fef3c7), title "Follow-up reminder", body with contact name | High |
| NOTIF-014 | Display lead_assigned notification | Lead assigned to logged-in user; notification created | 1. Click bell icon 2. Locate lead_assigned notification (type=lead_assigned) | Notification displays: š¤ icon, blue color (#2563eb), light blue background (#dbeafe), title and body with lead details | High |
| NOTIF-015 | Display lead_stage_changed notification | Lead's stage changed; notification created | 1. Click bell icon 2. Locate lead_stage_changed notification (type=lead_stage_changed) | Notification displays: š icon, purple color (#7c3aed), light purple background (#ede9fe), title and stage change info | High |
| NOTIF-016 | Display conversation_assigned notification | Conversation assigned to logged-in user; notification created | 1. Click bell icon 2. Locate conversation_assigned notification (type=conversation_assigned) | Notification displays: š¬ icon, cyan color (#0891b2), light cyan background (#e0f2fe), conversation details in body | High |
| NOTIF-017 | Display new_message notification | New WhatsApp message received; notification created | 1. Click bell icon 2. Locate new_message notification (type=new_message) | Notification displays: āļø icon, green color (#16a34a), light green background (#dcfce7), sender and message preview in body | High |
| NOTIF-018 | Display handoff_sla_breach notification | Pending conversation breaches SLA; notification created | 1. Click bell icon 2. Locate handoff_sla_breach notification | Notification displays: default icon (š¬), slate color (#475569), light slate background (#f1f5f9), title "Pending conversation needs attention", body with elapsed time | Medium |
| NOTIF-019 | Display unknown notification type | Notification with unrecognized type created | 1. Click bell icon 2. Locate unknown type notification | Notification displays: default š¬ icon, slate colors (#475569, #f1f5f9), title and body render correctly | Low |
Mark as Read & Read State
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-020 | Auto-mark unread as read after opening dropdown | User has 3 unread notifications; none are read yet | 1. Bell icon shows "3" badge 2. Click bell icon 3. Wait 2 seconds 4. Observe dropdown is still open | After ~2 seconds: all notifications' is_read becomes true; unread badge disappears or shows "0"; notification rows no longer appear highlighted/bold | High |
| NOTIF-021 | Mark single notification as read | User has 2 unread notifications in dropdown | 1. Click bell icon 2. Hover over/click a specific unread notification row 3. Click the read/check icon (if visible) or wait for auto-mark | Notification row updates: visual emphasis (bold/highlight) removed; if dropdown still open, unread count in badge decreases | High |
| NOTIF-022 | Mark all as read via button | User has 5 unread notifications in dropdown | 1. Click bell icon 2. Locate "Mark all as read" button (if exists) or observe auto-mark after 2s 3. Wait for action to complete | All notification rows update: none appear bold/highlighted; bell badge updates to "0" or disappears; success message (if manual action) appears | High |
| NOTIF-023 | Unread visual distinction | User has mixed read/unread notifications | 1. Click bell icon 2. Observe notification rows | Unread notifications are bold/highlighted/have different background; read notifications appear dimmed/normal weight | High |
| NOTIF-024 | Read state persists after closing dropdown | User marks notifications as read; closes dropdown | 1. Click bell icon 2. Auto-mark as read or manually mark 3. Close dropdown (click elsewhere or press Esc) 4. Re-open bell icon | Notifications still show is_read=true; no unread badge on bell; dropdown content unchanged | High |
Notification Navigation & Actions
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-025 | Click contact_reminder notification ā navigate to contact detail | Notification of type contact_reminder with resource_id=contact_123 exists | 1. Click bell icon 2. Click on contact_reminder notification row 3. Wait for page to load | Browser navigates to /contacts/contact_123 or contact detail page; notification is marked as read; dropdown closes | High |
| NOTIF-026 | Click lead_assigned notification ā navigate to lead detail | Notification of type lead_assigned with resource_id=lead_456 exists | 1. Click bell icon 2. Click on lead_assigned notification row | Browser navigates to /leads/lead_456 or lead detail page; dropdown closes; notification marked as read | High |
| NOTIF-027 | Click conversation_assigned notification ā navigate to conversation | Notification of type conversation_assigned with resource_type=conversation exists | 1. Click bell icon 2. Click on conversation_assigned notification row | Browser navigates to conversation view with resource_id; dropdown closes | High |
| NOTIF-028 | View resource label text | Notification dropdown open with mixed resource types | 1. Click bell icon 2. Read action text on notifications | contact_reminder/lead notifications show "View contact ā"; conversation notifications show "View conversation ā"; lead notifications show "View lead ā" | High |
| NOTIF-029 | Click "View Contact ā" link | Notification displayed in dropdown | 1. Click bell icon 2. Click "View contact ā" link on a contact-resource notification | Navigates to contact detail page; dropdown closes | High |
Delete Notifications
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-030 | Delete single notification | Notification exists in dropdown | 1. Click bell icon 2. Hover over notification row 3. Click delete/trash icon (if visible) or right-click menu | Notification row disappears from dropdown; success toast shows "Notification deleted"; unread count updates if it was unread | High |
| NOTIF-031 | Delete all notifications | Dropdown open with 5+ notifications | 1. Click bell icon 2. Click "Delete All" or similar action (if available) 3. Confirm if prompted | All notifications removed from dropdown; bell shows empty state or "No notifications"; success message appears | High |
| NOTIF-032 | Undo delete (if supported) | Just deleted a notification | 1. Delete notification 2. Click "Undo" in toast (if available) | Notification reappears in dropdown; deleted status reversed | Low |
Notification Preferences
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-033 | Access notification preferences | Logged in as any user | 1. Navigate to settings/account page 2. Find "Notifications" section or click settings gear icon next to bell 3. Click "Preferences" or similar | Preferences modal/page opens showing toggles for each notification type | High |
| NOTIF-034 | Disable contact_reminder notifications | In preferences page | 1. Locate contact_reminder toggle 2. Toggle OFF 3. Click Save | contact_reminder toggle shows OFF state; success toast "Preferences saved"; future contact_reminder notifications not received | High |
| NOTIF-035 | Enable lead_assigned notifications | In preferences page with lead_assigned disabled | 1. Locate lead_assigned toggle 2. Toggle ON 3. Click Save | lead_assigned toggle shows ON state; success toast appears; future lead_assigned notifications received | High |
| NOTIF-036 | Disable multiple notification types | In preferences page | 1. Toggle OFF: contact_reminder, conversation_assigned, new_message 2. Leave enabled: lead_assigned, lead_stage_changed 3. Click Save | All three toggles show OFF; success toast; save completes without errors | Medium |
| NOTIF-037 | Disable all notifications | In preferences page | 1. Toggle OFF all notification type toggles 2. Click Save | All toggles OFF; success toast "All notifications disabled"; system no longer generates notifications for user | Medium |
| NOTIF-038 | Re-enable notifications after disabling all | All notifications currently disabled | 1. Go to preferences 2. Toggle ON several types (contact_reminder, lead_assigned) 3. Click Save | Selected toggles show ON; others remain OFF; success toast; notifications resume for enabled types | Medium |
| NOTIF-039 | Preferences persist across sessions | Set preferences; disable contact_reminder; log out | 1. Log out 2. Log back in 3. Go to preferences | contact_reminder still disabled; preference persists | Medium |
Contact Reminder Notifications (Worker)
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-040 | Contact reminder fires at due time | Contact reminder created with due_at=2026-04-17T14:00:00Z; current time is 2026-04-17T14:01:00Z | 1. Wait for reminder_worker to run (polls every 60s) 2. Check notification in user's bell | Notification appears for assigned_user_id with type=contact_reminder; title="Follow-up reminder"; body contains contact name and note; reminder.status changes to "sent"; reminder.sent_at is set | High |
| NOTIF-041 | Contact reminder uses assigned_user_id if set | Contact reminder with assigned_user_id=user_123; contact also has assigned_user_id=user_456 | 1. Wait for worker to fire reminder | Notification sent to user_123, not user_456 | High |
| NOTIF-042 | Contact reminder falls back to contact's assigned_user_id | Contact reminder with assigned_user_id=null; contact.assigned_user_id=user_456; reminder.created_by_user_id=user_789 | 1. Wait for worker to fire reminder | Notification sent to user_456 (contact's assigned user), not user_789 | High |
| NOTIF-043 | Contact reminder falls back to created_by_user_id | Contact reminder with assigned_user_id=null; contact.assigned_user_id=null; reminder.created_by_user_id=user_789 | 1. Wait for worker to fire reminder | Notification sent to user_789 | High |
| NOTIF-044 | No notification sent if no user to notify | Contact reminder with assigned_user_id, contact.assigned_user_id, and created_by_user_id all null | 1. Wait for worker to fire reminder | reminder.status changes to "sent"; no notification created; audit log recorded with "notified_user": "" | Medium |
| NOTIF-045 | Contact reminder with no contact | Reminder with contact_id pointing to deleted contact | 1. Wait for worker to fire reminder | Notification sent with body containing "Unknown" instead of contact name; reminder.status="sent"; audit event logged | Medium |
| NOTIF-046 | Contact reminder uses contact phone if name null | Contact with name=null, phone="+1234567890"; reminder.note="Check status" | 1. Wait for worker to fire reminder | Notification body shows "+1234567890: Check status" | Medium |
| NOTIF-047 | Contact reminder audit log created | Contact reminder fires and notification sent | 1. Fire reminder via worker 2. Check contact audit logs in DB | Audit event logged with event_type="reminder_sent"; data includes reminder_id, note, notified_user full name | Medium |
| NOTIF-048 | Already-sent reminders not re-fired | Reminder with status="sent" and sent_at already set; due_at is in past | 1. Run reminder_worker 2. Check notification count | No new notification created; reminder status unchanged | High |
| NOTIF-049 | Pending reminders fired in batch | 3 reminders all due at same time | 1. Run worker 2. Check results | All 3 reminders status changed to "sent"; notifications sent to respective users; worker logs show sent=3 | Medium |
Handoff SLA Breach Notifications (Worker)
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-050 | Handoff SLA breach notification fires when threshold exceeded | Conversation status=PENDING, last_message_at=2026-04-17T12:00:00Z, current time=2026-04-17T12:31:00Z, org.handoff_sla_minutes=30, no agent activity since message | 1. Run handoff_worker 2. Check notifications for assigned team members | Notification created for all team members with type=handoff_sla_breach; title="Pending conversation needs attention"; body shows "31 minutes" (elapsed time); sla_notified_at set on conversation | High |
| NOTIF-051 | Handoff SLA not breached when within threshold | Conversation status=PENDING, last_message_at=2026-04-17T12:00:00Z, current time=2026-04-17T12:15:00Z, org.handoff_sla_minutes=30 | 1. Run handoff_worker 2. Check notifications | No notification created; conversation.sla_notified_at remains null | High |
| NOTIF-052 | SLA breach only fires once per conversation | Conversation breaches SLA; sla_notified_at already set to earlier time | 1. Run handoff_worker 2. Check new notifications | No new notification created; sla_notified_at unchanged | High |
| NOTIF-053 | Handoff SLA checks agent activity | Conversation status=PENDING, last_message_at=2026-04-17T12:00:00Z, last_agent_activity_at=2026-04-17T12:29:00Z, current time=2026-04-17T12:31:00Z, org.handoff_sla_minutes=30 | 1. Run handoff_worker | No notification created; agent activity is more recent than message, so no SLA breach | High |
| NOTIF-054 | Handoff SLA notifies all team members | Conversation assigned to team_A with 3 members (user_1, user_2, user_3); SLA breached | 1. Run handoff_worker 2. Check notifications for all 3 users | Each of 3 team members receives same notification; notified count=3 | High |
| NOTIF-055 | No SLA notification if conversation has no assigned team | Conversation status=PENDING, assigned_team_id=null, SLA breached | 1. Run handoff_worker 2. Check notifications 3. Check logs | No notification created; worker logs "handoff_sla_no_team_assigned" with conversation_id | Medium |
| NOTIF-056 | SLA check skips non-PENDING conversations | Conversation status=RESOLVED (not PENDING), SLA would breach | 1. Run handoff_worker | No notification; conversation not evaluated | High |
| NOTIF-057 | SLA check handles null last_message_at | Conversation status=PENDING, last_message_at=null, SLA check runs | 1. Run handoff_worker | Conversation skipped; no notification; no error | Medium |
| NOTIF-058 | Batch processing: multiple conversations breach SLA | 5 conversations all PENDING and breaching SLA simultaneously | 1. Run handoff_worker 2. Check results | Notifications sent for all breached conversations; worker logs notified=N where Nā„5 | Medium |
| NOTIF-059 | SLA elapsed time calculation accurate | Conversation last_message_at=2026-04-17T12:00:00Z, current=2026-04-17T12:47:00Z (47 mins elapsed) | 1. Run handoff_worker 2. Check notification body | Notification shows "47 minutes" elapsed time (within ±1 min tolerance) | Medium |
Role-Based Access & Isolation
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-060 | User sees only own notifications | User A logged in; notifications exist for User A and User B | 1. Click bell icon 2. List notifications | User A sees only own notifications; User B's notifications not visible; unread count only includes User A's | High |
| NOTIF-061 | Agent cannot view other users' notifications | Agent_1 logged in; Agent_2 has unread notifications | 1. Attempt to access Agent_2's notifications via browser dev tools or API 2. Observe response | Access denied or 403; Agent_1 sees no notifications from Agent_2 | High |
| NOTIF-062 | Admin sees own notifications only (not all org users) | Admin logged in; multiple users in org with notifications | 1. Click bell icon | Admin sees only own notifications, not all org users' notifications | High |
| NOTIF-063 | User cannot delete other user's notification | User A logged in; notification belongs to User B | 1. Attempt delete via API or UI manipulation | 403 Forbidden or notification remains; User B's notification unaffected | High |
| NOTIF-064 | Preferences scoped to current user | User A sets contact_reminder=OFF; logs out | 1. Log in as User B 2. Check User B's preferences | User B's contact_reminder still ON; User A's preference unaffected | High |
| NOTIF-065 | Mark all read is scoped to current user | User A marks all read; User B has 5 unread | 1. Log in as User A 2. Mark all read 3. Log in as User B | User A's bell shows 0 unread; User B's bell still shows 5 unread | High |
API Endpoint Validation & Error Handling
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-066 | GET /notifications: page parameter validation | Not logged in or invalid token | 1. Call GET /notifications?page=0 | 400 Bad Request; error message "page must be >= 1" | Medium |
| NOTIF-067 | GET /notifications: page_size exceeds max | Logged in | 1. Call GET /notifications?page_size=101 | 400 Bad Request; error "page_size must be <= 100" | Medium |
| NOTIF-068 | GET /notifications: invalid filter_by | Logged in | 1. Call GET /notifications?filter_by=invalid | 400 Bad Request; filter_by must match pattern "all" or "unread" | Medium |
| NOTIF-069 | GET /notifications: successful response structure | Logged in with 10 notifications | 1. Call GET /notifications?page=1&page_size=10 | 200 OK; response contains items[], total, page, page_size; each item has id, title, body, is_read, created_at, resource_type, resource_id | High |
| NOTIF-070 | GET /notifications/preferences: structure | Logged in | 1. Call GET /notifications/preferences | 200 OK; response contains preferences object with keys for each notification type (contact_reminder, lead_assigned, etc.) | High |
| NOTIF-071 | PUT /notifications/preferences: save preferences | Logged in | 1. Call PUT /notifications/preferences with body: { "preferences": { "contact_reminder": { "enabled": false }, ... } } | 204 No Content; preferences saved to DB; subsequent GET returns updated values | High |
| NOTIF-072 | PUT /notifications/preferences: invalid request body | Logged in | 1. Call PUT with malformed JSON or missing required fields | 400 Bad Request; validation error message | Medium |
| NOTIF-073 | POST /notifications/{notif_id}/read: mark single notification read | Logged in with unread notification id=notif_abc | 1. Call POST /notifications/notif_abc/read | 204 No Content; notification.is_read=true in DB; unread count decreases | High |
| NOTIF-074 | POST /notifications/{notif_id}/read: invalid notification id | Logged in | 1. Call POST /notifications/invalid-uuid/read | 400 Bad Request or 404 Not Found | Medium |
| NOTIF-075 | POST /notifications/{notif_id}/read: notification not found | Logged in | 1. Call POST /notifications/00000000-0000-0000-0000-000000000000/read | 404 Not Found | Medium |
| NOTIF-076 | POST /notifications/read-all: success | Logged in with 5 unread notifications | 1. Call POST /notifications/read-all | 204 No Content; all 5 notifications marked is_read=true; unread count becomes 0 | High |
| NOTIF-077 | DELETE /notifications/{notif_id}: delete single | Logged in with notification id=notif_xyz | 1. Call DELETE /notifications/notif_xyz | 204 No Content; notification removed from DB; GET /notifications no longer includes it | High |
| NOTIF-078 | DELETE /notifications/{notif_id}: not owned by user | Logged in as User A; notification belongs to User B | 1. Call DELETE /notifications/other_user_notif_id | 403 Forbidden or 404 Not Found; notification unchanged | High |
| NOTIF-079 | DELETE /notifications: delete all | Logged in with 10 notifications | 1. Call DELETE /notifications | 204 No Content; all notifications removed from DB; GET /notifications returns empty list | High |
| NOTIF-080 | Unauthenticated requests rejected | No auth token or expired token | 1. Call any GET/POST/DELETE endpoint without Authorization header | 401 Unauthorized; "Not authenticated" or similar message | High |
Multi-Tenant Isolation
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-081 | Notifications isolated by org_id | User in org_A and org_B with different notifications | 1. Switch to org_A context 2. Call GET /notifications 3. Switch to org_B context 4. Call GET /notifications | org_A notifications only visible in org_A context; org_B notifications only in org_B context; no cross-org leakage | High |
| NOTIF-082 | Preferences isolated by org_id | User in org_A sets contact_reminder=OFF; org_B contact_reminder still ON | 1. Switch org context to A; GET preferences (OFF) 2. Switch to B; GET preferences (ON) | org_A prefs show OFF; org_B shows ON; independent per org | Medium |
| NOTIF-083 | Reminders respect org_id | Reminder in org_A for user_X; user_X also in org_B | 1. Worker fires reminder in org_A context | Notification sent with org_id=org_A; org_B reminders unaffected | Medium |
Real-Time Updates (WebSocket)
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-084 | WebSocket notification received in real-time | Bell dropdown open; new contact_reminder fired by worker | 1. Keep dropdown open 2. Fire reminder in worker 3. Observe dropdown | New notification appears in dropdown without refresh; unread count updates; grouped by correct date | Medium |
| NOTIF-085 | WebSocket updates unread badge in real-time | Bell icon visible; new notification fired | 1. With dropdown closed, new unread notification created 2. Observe bell badge | Unread count on bell icon updates immediately without clicking | Medium |
| NOTIF-086 | WebSocket handles disconnection gracefully | WebSocket connection drops; new notifications created | 1. Simulate WebSocket disconnect 2. Create new notification 3. Reconnect WebSocket | Upon reconnect, notification appears; no console errors; UI remains functional | Low |
Performance & Load
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-087 | Large notification list loads without blocking | User with 500+ notifications | 1. Click bell icon 2. Observe load time | Dropdown opens within 2 seconds; pagination/virtualization prevents UI freeze | Medium |
| NOTIF-088 | Pagination handles large datasets | User with 1000 notifications across 40 pages | 1. Load page 1 (25 items) 2. Navigate to page 20 3. Verify correctness | Each page loads quickly; correct items displayed per page; no missing/duplicate items | Medium |
| NOTIF-089 | Worker batch processes many reminders efficiently | 100+ reminders due simultaneously | 1. Run reminder_worker with 100 due reminders | All 100 processed within worker timeout; no reminders missed; logs show sent=100; no performance degradation | Low |
Edge Cases & Boundary Conditions
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-090 | Notification with very long title/body | Notification with title >200 chars, body >500 chars | 1. Click bell icon 2. View notification in dropdown | Title/body truncated with ellipsis or wrapped; no UI layout breaking; text readable | Low |
| NOTIF-091 | Notification resource_id is invalid UUID | Notification with resource_id pointing to non-existent record | 1. Click notification to navigate 2. Observe behavior | Error message shown or graceful redirect; no 500 error page | Medium |
| NOTIF-092 | Timestamp in far future or past | Notification created_at is year 2100 or 1970 | 1. View notification timestamp | Timestamp displays correctly without error; relative time calculation handles edge case | Low |
| NOTIF-093 | User deletes own account, then notifications queried | User deleted; their notifications still in DB | 1. Query GET /notifications for deleted user | 401 Unauthorized or user not found; no orphaned notification display | Medium |
| NOTIF-094 | Notification preferences contain unknown type keys | DB has preference entry for non-existent notification type | 1. GET /notifications/preferences 2. Check response | Response includes all preference entries; unknown types handled gracefully without validation error | Low |
| NOTIF-095 | Rapid successive notification creation | Create 10 notifications for same user in <1 second | 1. Trigger 10 notifications rapidly 2. Query GET /notifications | All 10 notifications present; correct unread count; no duplicate IDs; correct ordering (newest first) | Medium |
Accessibility & UX
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| NOTIF-096 | Bell icon keyboard accessible | Using keyboard navigation (Tab, Enter) | 1. Tab to bell icon 2. Press Enter | Dropdown opens; focus moves into dropdown; can tab through notifications | Low |
| NOTIF-097 | Notification row keyboard selectable | Dropdown open with keyboard navigation | 1. Tab through notification rows 2. Press Enter on focused row | Notification clicked; navigates to resource or performs action | Low |
| NOTIF-098 | Close dropdown with Escape key | Dropdown open | 1. Press Escape | Dropdown closes; focus returns to bell icon or previous focus | Low |
| NOTIF-099 | Color contrast meets WCAG AA | Notification rows with colored backgrounds | 1. Inspect text color vs background color using contrast checker | All text/background pairs meet WCAG AA contrast ratio (4.5:1 for normal text) | Low |
| NOTIF-100 | Notification bell has aria-label | Bell icon in navigation | 1. Inspect element with screen reader or DevTools | aria-label="Notifications" or similar present; unread count announced to screen readers | Low |