Test Cases: Email Preferences
/account/notifications (My Account → Notifications) → NotificationsTab.tsx rendering EmailPreferencesSection.tsx. The member 403 on GET/PUT /api/email-preferences is fixed (user?.memberId). ⚠️ Known field-name mismatch: the backend persists paymentReminders, eventReminders, membershipUpdates, broadcasts, unsubscribedAll, but the UI's 5 toggles are Payment Reminders / Event Reminders / Membership Alerts (membershipAlerts) / Newsletter (newsletterEmails) / Marketing Emails (marketingEmails) — only the first two map to backend columns, so the latter three likely won't persist. Test it: toggle all 5, save, reload, and flag any that don't stick. The steps below predate the tab — the navigation path is now the Notifications tab, not Profile; treat exact selectors loosely.
Module: Communications - Email Preferences
Priority: P2
Phase: 4
Backend Endpoints:
GET /api/email-preferencesPUT /api/email-preferencesPOST /api/unsubscribePOST /api/resubscribe
Frontend Page: Member Portal - Email Preferences Settings
Prerequisites
- User logged in as member with active account.
- Email preferences record exists for member (created on registration).
- Broadcast system operational.
- Test broadcasts available for sending.
Test Data
| Member | Marketing | Events | Payments | Newsletter | Membership | |
|---|---|---|---|---|---|---|
| MEM-001 | john@example.com | true | true | true | true | true |
| MEM-002 | jane@example.com | false | true | true | false | true |
| MEM-003 | bob@example.com | false | false | true | false | false |
Test Cases
TC-COMM-PREF-001: View Current Email Preferences
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member MEM-001. | Dashboard displayed. |
| 2 | Navigate to Profile > Email Preferences. | Email preferences page displayed. |
| 3 | Verify all preference categories visible. | Marketing, Events, Payments, Newsletter, Membership Alerts. |
| 4 | Verify current values. | All toggles reflect database values (all enabled for MEM-001). |
| 5 | Verify API call. | GET /api/email-preferences returns current preferences. |
TC-COMM-PREF-002: Toggle Marketing Emails Off
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member MEM-001. | Dashboard displayed. |
| 2 | Navigate to Profile > Email Preferences. | Preferences page displayed. |
| 3 | Toggle "Marketing Emails" to OFF. | Toggle switches to off state. |
| 4 | Click "Save Preferences". | API PUT /api/email-preferences called. |
| 5 | Verify success message. | "Preferences updated" message displayed. |
| 6 | Refresh page. | marketingEmails shows as false. |
| 7 | Verify database. | marketingEmails field is false. |
TC-COMM-PREF-003: Toggle Event Reminders Off
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Navigate to Email Preferences. | - |
| 3 | Toggle "Event Reminders" to OFF. | Toggle switches to off state. |
| 4 | Save preferences. | API PUT /api/email-preferences called. |
| 5 | Verify update persisted. | eventReminders shows as false. |
TC-COMM-PREF-004: Toggle Newsletter Emails Off
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Navigate to Email Preferences. | - |
| 3 | Toggle "Newsletter Emails" to OFF. | Toggle switches to off state. |
| 4 | Save preferences. | API PUT /api/email-preferences called. |
| 5 | Verify update persisted. | newsletterEmails shows as false. |
TC-COMM-PREF-005: Toggle Membership Alerts Off
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Navigate to Email Preferences. | - |
| 3 | Toggle "Membership Alerts" to OFF. | Toggle switches to off state. |
| 4 | Save preferences. | API PUT /api/email-preferences called. |
| 5 | Verify update persisted. | membershipAlerts shows as false. |
TC-COMM-PREF-006: Toggle All Preferences Off
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member with all preferences ON. | - |
| 2 | Navigate to Email Preferences. | All toggles in ON state. |
| 3 | Toggle all categories to OFF. | All toggles switch to off state. |
| 4 | Save preferences. | All preferences updated except paymentReminders if protected. |
| 5 | Verify each category. | All optional categories are false. |
TC-COMM-PREF-007: Toggle All Preferences On
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member MEM-003 with preferences OFF. | - |
| 2 | Navigate to Email Preferences. | Most toggles in OFF state. |
| 3 | Toggle all categories to ON. | All toggles switch to on state. |
| 4 | Save preferences. | All preferences updated. |
| 5 | Verify each category. | All categories are true. |
TC-COMM-PREF-008: Payment Reminders Cannot Be Disabled (Transactional)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Navigate to Email Preferences. | Preferences page displayed. |
| 3 | Locate "Payment Reminders" toggle. | Toggle visible. |
| 4 | Verify toggle is disabled/locked. | Cannot toggle off OR warning displayed. |
| 5 | Attempt via API to set paymentReminders: false. | Request ignored or returns warning. |
| 6 | Verify database. | paymentReminders remains true. |
TC-COMM-PREF-009: Transactional Email Always Sent (Payment Reminder)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Setup: Member with all email preferences OFF except payments. | - |
| 2 | Create overdue invoice for member. | Invoice in OVERDUE status. |
| 3 | Trigger payment reminder email. | System sends payment reminder. |
| 4 | Check member's inbox. | Payment reminder email received. |
| 5 | Verify email category. | Email marked as transactional. |
TC-COMM-PREF-010: Marketing Email Respects Preference (Opted Out)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member MEM-002 (marketingEmails: false). | - |
| 2 | Admin creates marketing broadcast. | Broadcast targeting all members. |
| 3 | Admin sends broadcast. | Broadcast sent. |
| 4 | Check MEM-002 inbox. | No marketing email received. |
| 5 | Verify send log. | MEM-002 excluded due to preference. |
TC-COMM-PREF-011: Marketing Email Respects Preference (Opted In)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Ensure member MEM-001 has marketingEmails: true. | - |
| 2 | Admin creates marketing broadcast. | Broadcast targeting all members. |
| 3 | Admin sends broadcast. | Broadcast sent. |
| 4 | Check MEM-001 inbox. | Marketing email received. |
| 5 | Verify content. | Email content matches broadcast. |
TC-COMM-PREF-012: Event Reminder Respects Preference
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member MEM-003 has eventReminders: false. | - |
| 2 | Register MEM-003 for upcoming event. | Registration confirmed. |
| 3 | Trigger event reminder (day before). | System processes reminders. |
| 4 | Check MEM-003 inbox. | No event reminder received. |
| 5 | Verify other registrants. | Members with preference ON receive reminder. |
TC-COMM-PREF-013: Newsletter Respects Preference
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member MEM-002 has newsletterEmails: false. | - |
| 2 | Admin sends newsletter broadcast. | Newsletter sent. |
| 3 | Check MEM-002 inbox. | No newsletter received. |
| 4 | Check MEM-001 inbox (newsletter: true). | Newsletter received. |
TC-COMM-PREF-014: Membership Alert Respects Preference
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member MEM-003 has membershipAlerts: false. | - |
| 2 | Change MEM-003 membership tier. | Membership updated. |
| 3 | Check MEM-003 inbox. | No membership alert received. |
| 4 | Perform same for member with alerts ON. | Membership alert received. |
TC-COMM-PREF-015: Preference Persistence - Session Logout
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Set marketingEmails to false. | Preference saved. |
| 3 | Logout. | Session ended. |
| 4 | Login again. | New session started. |
| 5 | Navigate to Email Preferences. | marketingEmails still false. |
| 6 | Verify all other preferences. | All settings persist. |
TC-COMM-PREF-016: Preference Persistence - Different Device
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login on Device A. | - |
| 2 | Update preferences (turn off newsletters). | Preference saved. |
| 3 | Login on Device B (different browser/device). | New session. |
| 4 | Navigate to Email Preferences. | Shows same preferences as Device A. |
| 5 | Verify newsletters is OFF. | Preference persisted across devices. |
TC-COMM-PREF-017: Preference Persistence - Clear Browser Data
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as member. | - |
| 2 | Update email preferences. | Changes saved. |
| 3 | Clear browser cache and cookies. | Local data cleared. |
| 4 | Login again. | Fresh session. |
| 5 | Check preferences. | Server-stored preferences intact. |
TC-COMM-PREF-018: Unsubscribe via Email Link
| Step | Action | Expected Result |
|---|---|---|
| 1 | Send broadcast to member. | Email received. |
| 2 | Click "Unsubscribe" link in email footer. | Unsubscribe page displayed. |
| 3 | Select category to unsubscribe from. | Category selection available. |
| 4 | Confirm unsubscribe. | API POST /api/unsubscribe called. |
| 5 | Verify success message. | "You have been unsubscribed" displayed. |
| 6 | Login and check preferences. | Selected category now OFF. |
| 7 | Verify unsubscribedAt. | Timestamp recorded in database. |
TC-COMM-PREF-019: Unsubscribe from All (One-Click)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Click unsubscribe link in email. | Unsubscribe page displayed. |
| 2 | Select "Unsubscribe from all". | Option selected. |
| 3 | Confirm. | All optional categories disabled. |
| 4 | Verify payment reminders. | Payment reminders still enabled (transactional). |
| 5 | Verify unsubscribeReason. | Recorded in database. |
TC-COMM-PREF-020: Resubscribe After Unsubscribe
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member previously unsubscribed from marketing. | marketingEmails: false. |
| 2 | Navigate to Email Preferences. | Preferences page displayed. |
| 3 | Toggle marketing back to ON. | Toggle switches to on state. |
| 4 | Save preferences. | Preferences updated. |
| 5 | Verify resubscription. | marketingEmails: true. |
| 6 | Receive next marketing email. | Email received successfully. |
TC-COMM-PREF-021: Resubscribe via Token Link
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member clicks resubscribe link (if sent). | Resubscribe page displayed. |
| 2 | Confirm resubscription. | API POST /api/resubscribe called. |
| 3 | Verify success message. | "You have been resubscribed" displayed. |
| 4 | Check preferences. | Selected category now ON. |
TC-COMM-PREF-022: Invalid Unsubscribe Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Access unsubscribe URL with invalid token. | Page loads. |
| 2 | Attempt to unsubscribe. | Error message displayed. |
| 3 | Verify no changes. | Preferences unchanged. |
| 4 | Verify API response. | 400 Bad Request - "Invalid token". |
TC-COMM-PREF-023: Expired Unsubscribe Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Use unsubscribe link from old email (>30 days). | - |
| 2 | Attempt to unsubscribe. | Token expired message. |
| 3 | Prompt to login. | Login link provided. |
| 4 | Login and manage preferences. | Full preferences page accessible. |
TC-COMM-PREF-024: Concurrent Preference Updates
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open preferences in two browser tabs. | Same preferences displayed. |
| 2 | Tab 1: Toggle marketing OFF, save. | Saved successfully. |
| 3 | Tab 2: Toggle newsletter OFF, save (without refresh). | Saved successfully. |
| 4 | Refresh both tabs. | Both changes persisted. |
| 5 | Verify final state. | marketing: false, newsletter: false. |
TC-COMM-PREF-025: Preferences UI Accessibility
| Step | Action | Expected Result |
|---|---|---|
| 1 | Navigate to Email Preferences. | Page displayed. |
| 2 | Verify keyboard navigation. | Can tab through all toggles. |
| 3 | Verify ARIA labels. | Toggle descriptions read by screen reader. |
| 4 | Verify contrast. | Meets WCAG 2.1 AA standards. |
| 5 | Verify save with keyboard. | Enter/Space activates save button. |
API Test Cases
TC-COMM-PREF-API-001: GET /api/email-preferences
Expected Response: 200 OK
{
"id": "pref_abc123",
"memberId": "MEM-001",
"marketingEmails": true,
"eventReminders": true,
"paymentReminders": true,
"newsletterEmails": true,
"membershipAlerts": true,
"unsubscribedAt": null,
"unsubscribeReason": null
}
TC-COMM-PREF-API-002: GET /api/email-preferences (Unauthenticated)
Expected Response: 401 Unauthorized
{
"error": "Authentication required"
}
TC-COMM-PREF-API-003: PUT /api/email-preferences (Update Single)
Request:
{
"marketingEmails": false
}
Expected Response: 200 OK
{
"id": "pref_abc123",
"memberId": "MEM-001",
"marketingEmails": false,
"eventReminders": true,
"paymentReminders": true,
"newsletterEmails": true,
"membershipAlerts": true
}
TC-COMM-PREF-API-004: PUT /api/email-preferences (Update Multiple)
Request:
{
"marketingEmails": false,
"eventReminders": false,
"newsletterEmails": false
}
Expected Response: 200 OK
{
"id": "pref_abc123",
"memberId": "MEM-001",
"marketingEmails": false,
"eventReminders": false,
"paymentReminders": true,
"newsletterEmails": false,
"membershipAlerts": true
}
TC-COMM-PREF-API-005: PUT /api/email-preferences (Attempt to Disable Payment Reminders)
Request:
{
"paymentReminders": false
}
Expected Response: 200 OK (value ignored)
{
"id": "pref_abc123",
"memberId": "MEM-001",
"paymentReminders": true,
"warning": "Payment reminders cannot be disabled"
}
TC-COMM-PREF-API-006: POST /api/unsubscribe
Request:
{
"token": "valid_unsubscribe_token",
"category": "broadcasts"
}
Expected Response: 200 OK
{
"success": true,
"category": "broadcasts",
"message": "Successfully unsubscribed from broadcasts"
}
TC-COMM-PREF-API-007: POST /api/unsubscribe (Invalid Token)
Request:
{
"token": "invalid_token",
"category": "broadcasts"
}
Expected Response: 400 Bad Request
{
"error": "Invalid or expired unsubscribe token"
}
TC-COMM-PREF-API-008: POST /api/resubscribe
Request:
{
"token": "valid_resubscribe_token",
"category": "broadcasts"
}
Expected Response: 200 OK
{
"success": true,
"category": "broadcasts",
"message": "Successfully resubscribed to broadcasts"
}
TC-COMM-PREF-API-009: PUT /api/email-preferences (Invalid Field)
Request:
{
"unknownField": true
}
Expected Response: 400 Bad Request
{
"error": "Invalid preference field: unknownField"
}
TC-COMM-PREF-API-010: PUT /api/email-preferences (Invalid Value Type)
Request:
{
"marketingEmails": "yes"
}
Expected Response: 400 Bad Request
{
"error": "marketingEmails must be a boolean"
}
Edge Cases
TC-COMM-PREF-EDGE-001: New Member Default Preferences
| Step | Action | Expected Result |
|---|---|---|
| 1 | Register new member. | Account created. |
| 2 | Check email preferences record. | Record created automatically. |
| 3 | Verify default values. | All preferences true by default. |
TC-COMM-PREF-EDGE-002: Member Without Preferences Record
| Step | Action | Expected Result |
|---|---|---|
| 1 | Database: Delete preferences record for test member. | Record removed. |
| 2 | Login as that member. | - |
| 3 | Navigate to Email Preferences. | Page loads without error. |
| 4 | Verify behavior. | New record created with defaults OR error handled gracefully. |
TC-COMM-PREF-EDGE-003: Broadcast to Segment with All Opted-Out
| Step | Action | Expected Result |
|---|---|---|
| 1 | Create segment where all members opted out of marketing. | - |
| 2 | Create marketing broadcast for this segment. | Broadcast created. |
| 3 | Attempt to send. | Warning: "0 eligible recipients". |
| 4 | Confirm send anyway. | No emails sent, broadcast marked as sent. |
TC-COMM-PREF-EDGE-004: Rapid Toggle Changes
| Step | Action | Expected Result |
|---|---|---|
| 1 | Navigate to preferences. | - |
| 2 | Rapidly toggle same preference ON/OFF multiple times. | UI remains responsive. |
| 3 | Click save. | Final state saved. |
| 4 | Verify no race conditions. | Database reflects last save action. |
Security Test Cases
TC-COMM-PREF-SEC-001: Cannot View Other Member's Preferences
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as MEM-001. | - |
| 2 | Attempt to GET /api/email-preferences?memberId=MEM-002. | Parameter ignored OR access denied. |
| 3 | Verify response. | Returns MEM-001's preferences only. |
TC-COMM-PREF-SEC-002: Cannot Update Other Member's Preferences
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as MEM-001. | - |
| 2 | PUT /api/email-preferences with memberId=MEM-002 in body. | Parameter ignored OR access denied. |
| 3 | Verify MEM-002 preferences. | Unchanged. |
TC-COMM-PREF-SEC-003: Admin Can View Member Preferences
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as ADMIN. | - |
| 2 | Navigate to member details for MEM-001. | Member profile displayed. |
| 3 | View email preferences section. | Preferences visible (read-only for admin). |
Notes
- Transactional Exemption: Payment reminders are classified as transactional emails required for service operation and cannot be disabled per CAN-SPAM/GDPR guidelines.
- Preference Granularity: The system supports category-level preferences rather than individual email opt-outs.
- Audit Trail: All preference changes should be logged for compliance purposes.
- Token Expiration: Unsubscribe tokens typically expire after 30 days for security.