diff --git a/bun.lock b/bun.lock
index f99312f88..62a8a0d77 100644
--- a/bun.lock
+++ b/bun.lock
@@ -534,6 +534,7 @@
"react-dom": "^19.1.0",
"tsup": "^8.5.0",
"typescript": "^5.8.3",
+ "vitest": "^3.2.4",
},
"peerDependencies": {
"react": "^19.1.1",
diff --git a/packages/email/emails/all-policy-notification.tsx b/packages/email/emails/all-policy-notification.tsx
index d794bc901..cc2852ddf 100644
--- a/packages/email/emails/all-policy-notification.tsx
+++ b/packages/email/emails/all-policy-notification.tsx
@@ -34,7 +34,8 @@ export const AllPolicyNotificationEmail = ({
return (
-{subjectText}
+
+ {subjectText}
-You've been invited to the Comp AI Portal
+
+ You've been invited to the Comp AI Portal
{
return (
-You've been invited to join Comp AI
+
+ You've been invited to join Comp AI
{
return (
-Login Link for Comp AI
+
+ Login Link for Comp AI
{
return (
-Get started with Comp AI
+
+ Get started with Comp AI
{
return (
-One-Time Password for Comp AI
+
+ One-Time Password for Comp AI
-{subjectText}
+
+ {subjectText}
return (
-Comp AI - Task Reminder
+
+ Comp AI - Task Reminder
-
+
+
Task "{taskName}" {statusLabel} - {organizationName}
diff --git a/packages/email/emails/reminders/weekly-task-digest.tsx b/packages/email/emails/reminders/weekly-task-digest.tsx
index 7f1f18f94..fd06f0246 100644
--- a/packages/email/emails/reminders/weekly-task-digest.tsx
+++ b/packages/email/emails/reminders/weekly-task-digest.tsx
@@ -45,7 +45,8 @@ export const WeeklyTaskDigestEmail = ({
return (
-{taskCountMessage}
+
+ {taskCountMessage}
from every template, which broke
+// @react-email/tailwind's media-query injection (md:* classes) and caused
+// every email to render to an empty Suspense fallback in production.
+// These tests fail if any template renders to that fallback or otherwise
+// produces broken output, so the same mistake can't ship again.
+
+const SUSPENSE_ERROR_MARKER = '';
+
+const cases = [
+ {
+ name: 'weekly-task-digest',
+ el: (
+
+ ),
+ },
+ {
+ name: 'task-reminder',
+ el: (
+
+ ),
+ },
+ {
+ name: 'task-status-notification',
+ el: (
+
+ ),
+ },
+ {
+ name: 'invite-portal',
+ el: (
+
+ ),
+ },
+ {
+ name: 'invite',
+ el: (
+
+ ),
+ },
+ { name: 'welcome', el: },
+ {
+ name: 'policy-notification',
+ el: (
+
+ ),
+ },
+ {
+ name: 'magic-link',
+ el: (
+
+ ),
+ },
+ {
+ name: 'all-policy-notification',
+ el: (
+
+ ),
+ },
+ { name: 'otp', el: },
+ {
+ name: 'training-completed',
+ el: (
+
+ ),
+ },
+ {
+ name: 'unassigned-items-notification',
+ el: (
+
+ ),
+ },
+];
+
+describe('email templates render to non-empty HTML', () => {
+ for (const { name, el } of cases) {
+ it(name, async () => {
+ const html = await render(el);
+ expect(html).not.toContain(SUSPENSE_ERROR_MARKER);
+ expect(html).toContain('
-Congratulations! You've completed your Security Awareness Training
+
+ Congratulations! You've completed your Security Awareness Training
-Member removed - items require reassignment
+
+ Member removed - items require reassignment