feat(notifications): renderNotificationEmail + previewEmailTemplate + logistics sample data
- Export `renderNotificationEmail` from email.ts (pure template resolver, no send) - Refactor `sendStyledNotificationEmail` to delegate to `renderNotificationEmail` - Hoist sampleData to module-level `NOTIFICATION_SAMPLE_DATA` in notification router - Add 8 logistics sample entries (FINALIST_*/TRAVEL_CONFIRMED/VISA_STATUS_UPDATE) - Add `notification.previewEmailTemplate` adminProcedure query (returns subject/html/hasStyledTemplate) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
62
tests/unit/notification-preview.test.ts
Normal file
62
tests/unit/notification-preview.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Task 1: renderNotificationEmail + previewEmailTemplate
|
||||
*
|
||||
* Tests that:
|
||||
* 1. previewEmailTemplate returns rendered HTML and a non-empty subject for a
|
||||
* type that has a styled template (VISA_STATUS_UPDATE).
|
||||
* 2. previewEmailTemplate returns non-empty HTML for a fallback type
|
||||
* (FINALIST_EXPIRED — no registered template, uses generic fallback).
|
||||
*/
|
||||
import { afterAll, describe, expect, it } from 'vitest'
|
||||
import { createCaller, prisma } from '../setup'
|
||||
import { createTestUser, cleanupTestData, uid } from '../helpers'
|
||||
import { notificationRouter } from '../../src/server/routers/notification'
|
||||
|
||||
describe('notification.previewEmailTemplate', () => {
|
||||
let adminId: string
|
||||
let adminEmail: string
|
||||
|
||||
afterAll(async () => {
|
||||
if (adminId) {
|
||||
await prisma.user.deleteMany({ where: { id: adminId } })
|
||||
}
|
||||
})
|
||||
|
||||
async function getAdminCaller() {
|
||||
if (!adminId) {
|
||||
const admin = await createTestUser('SUPER_ADMIN', {
|
||||
name: 'Preview Admin',
|
||||
email: `preview-admin-${uid()}@test.local`,
|
||||
})
|
||||
adminId = admin.id
|
||||
adminEmail = admin.email
|
||||
}
|
||||
return createCaller(notificationRouter, {
|
||||
id: adminId,
|
||||
email: adminEmail,
|
||||
name: 'Preview Admin',
|
||||
role: 'SUPER_ADMIN',
|
||||
})
|
||||
}
|
||||
|
||||
it('returns HTML containing a visa-related string and a non-empty subject for VISA_STATUS_UPDATE', async () => {
|
||||
const caller = await getAdminCaller()
|
||||
const result = await caller.previewEmailTemplate({ notificationType: 'VISA_STATUS_UPDATE' })
|
||||
|
||||
expect(result.subject).toBeTruthy()
|
||||
expect(result.html).toBeTruthy()
|
||||
// The visa template includes 'visa' or 'Grand' in its content
|
||||
const lower = result.html.toLowerCase()
|
||||
expect(lower.includes('visa') || lower.includes('grand')).toBe(true)
|
||||
expect(result.hasStyledTemplate).toBe(true)
|
||||
})
|
||||
|
||||
it('returns non-empty HTML for FINALIST_EXPIRED (fallback — no styled template)', async () => {
|
||||
const caller = await getAdminCaller()
|
||||
const result = await caller.previewEmailTemplate({ notificationType: 'FINALIST_EXPIRED' })
|
||||
|
||||
expect(result.html).toBeTruthy()
|
||||
expect(result.subject).toBeTruthy()
|
||||
expect(result.hasStyledTemplate).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user