Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/emails/utils/EmailTemplates.test.ts
T

133 lines
5.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { defaultBrandTokens } from "@toolctrl/brand";
import EmailTemplates from "./EmailTemplates";
import type { AppConfiguration } from "~/utils/db/appConfiguration.db.server";
const appConfiguration = {
app: { name: "Acme Portal" },
email: { fromName: "Acme Support", supportEmail: "help@acme.test" },
branding: { logo: "https://cdn.acme.test/logo.png", favicon: "https://cdn.acme.test/favicon.ico" },
} as AppConfiguration;
const actionUrl = "https://app.acme.test/action";
describe("EmailTemplates", () => {
it("keeps the exported template registry stable", () => {
expect(EmailTemplates.allTemplates.map((template) => template.name)).toEqual([
"welcome",
"password-reset",
"user-invitation",
"email-verification",
"account-setup",
]);
});
it("renders welcome emails through the React-Email pipeline", () => {
const rendered = EmailTemplates.WELCOME_EMAIL.parse({
appConfiguration,
data: { name: "Ada", action_url: actionUrl },
});
expect(rendered.subject).toBe("Welcome to Acme Portal, Ada!");
expect(rendered.body).toContain(actionUrl);
expect(rendered.body).toContain("Acme Portal");
expect(rendered.body.startsWith("<!DOCTYPE html>")).toBe(true);
expect(rendered.data).toEqual({ name: "Ada", action_url: actionUrl });
});
it("renders password reset emails with brand-aware subject", () => {
const rendered = EmailTemplates.PASSWORD_RESET_EMAIL.parse({
appConfiguration,
data: { name: "Ada", action_url: actionUrl },
});
expect(rendered.subject).toBe("Reset your Acme Portal password");
expect(rendered.body).toContain(actionUrl);
expect(rendered.body).toContain("Acme Portal");
});
it("renders user invitation emails with sender and organization data", () => {
const rendered = EmailTemplates.USER_INVITATION_EMAIL.parse({
appConfiguration,
data: {
name: "Ada",
invite_sender_name: "Grace",
invite_sender_organization: "Example Org",
action_url: actionUrl,
},
});
expect(rendered.subject).toBe("Grace invited you to Example Org on Acme Portal");
expect(rendered.body).toContain("Grace");
expect(rendered.body).toContain("Example Org");
expect(rendered.body).toContain(actionUrl);
});
it("renders email verification emails via the magic-link template", () => {
const rendered = EmailTemplates.VERIFICATION_EMAIL.parse({
appConfiguration,
data: { name: "Ada", action_url: actionUrl },
});
expect(rendered.subject).toBe("Verify your Acme Portal email address");
expect(rendered.body).toContain(actionUrl);
expect(rendered.body).toMatch(/expires? in 30 minutes/i);
});
it("renders account setup emails via the welcome template with plan-aware copy", () => {
const rendered = EmailTemplates.ACCOUNT_SETUP_EMAIL.parse({
appConfiguration,
data: { email: "ada@example.com", plan: "Pro", action_url: actionUrl },
});
expect(rendered.subject).toBe("Finish setting up your Acme Portal account");
expect(rendered.body).toContain("Pro plan");
expect(rendered.body).toContain("ada@example.com");
expect(rendered.body).toContain(actionUrl);
});
it("falls back to default brand tokens when app configuration omits brand values", () => {
const rendered = EmailTemplates.WELCOME_EMAIL.parse({
appConfiguration: {} as AppConfiguration,
data: { action_url: actionUrl },
});
expect(rendered.subject).toBe(`Welcome to ${defaultBrandTokens.productName}!`);
expect(rendered.body).toContain(defaultBrandTokens.productName);
});
it("uses the optional translator for subject and body chrome", () => {
const baseline = EmailTemplates.WELCOME_EMAIL.parse({
appConfiguration,
data: { name: "Ada", action_url: actionUrl },
});
const withTranslator = EmailTemplates.WELCOME_EMAIL.parse({
appConfiguration,
data: { name: "Ada", action_url: actionUrl },
t: (key, vars) => {
const values: Record<string, string> = {
"welcome.subject_named": `Willkommen bei ${vars?.productName}, ${vars?.name}!`,
"welcome.eyebrow": "Willkommen",
"welcome.title_named": `Hallo ${vars?.name}!`,
"welcome.intro": `Danke fuer ${vars?.productName}.`,
"welcome.actionLabel": "Einloggen",
"common.signoff.thanks": "Danke,",
};
return values[key] ?? key;
},
locale: "de",
});
expect(withTranslator.subject).toBe("Willkommen bei Acme Portal, Ada!");
expect(withTranslator.body).toContain("Hallo Ada!");
expect(withTranslator.body).toContain("Einloggen");
expect(withTranslator.body).toContain("Danke,");
expect(withTranslator.body).not.toEqual(baseline.body);
});
it("localizes registry descriptions when a mail translator is available", () => {
const t = (key: string) => (key === "registry.welcomeDescription" ? "Lokalisierte Willkommensbeschreibung" : key);
expect(EmailTemplates.WELCOME_EMAIL.describe(t)).toBe("Lokalisierte Willkommensbeschreibung");
expect(EmailTemplates.WELCOME_EMAIL.description).toBe("Welcome email with login link");
});
});