Files
api/apps/api/src/__tests__/email.test.ts
T
Hugh Hackman 51f95e0fd6 Initial extraction: groombook/api from groombook/app monorepo
Part of GRO-802 monorepo breakdown.

Changes:
- Extract apps/api/ as the main API service
- Inline packages/db/ (database schema, migrations, utilities)
- Inline packages/types/ (shared TypeScript types)
- Add CI workflow for lint, typecheck, test, build, docker
- Port Dockerfile with 4 stages: runner, migrate, seed, reset

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-02 21:10:21 +00:00

107 lines
3.3 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
buildConfirmationEmail,
buildReminderEmail,
} from "../services/email.js";
const START = new Date("2026-03-25T15:00:00Z");
const BASE = {
clientName: "Jane Doe",
petName: "Biscuit",
serviceName: "Full Groom",
groomerName: "Alex",
startTime: START,
};
describe("buildConfirmationEmail", () => {
it("addresses the correct recipient", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.to).toBe("jane@example.com");
});
it("includes the pet name in the subject", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.subject).toContain("Biscuit");
});
it("includes confirmation wording in subject", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.subject).toMatch(/confirmed/i);
});
it("includes client name in the plain text body", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.text).toContain("Jane Doe");
});
it("includes service name in plain text body", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.text).toContain("Full Groom");
});
it("includes groomer name when provided", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.text).toContain("Alex");
});
it("omits groomer when groomerName is null", () => {
const mail = buildConfirmationEmail("jane@example.com", {
...BASE,
groomerName: null,
});
expect(mail.text).not.toContain("with ");
});
it("includes HTML body", () => {
const mail = buildConfirmationEmail("jane@example.com", BASE);
expect(mail.html).toBeTruthy();
expect(mail.html).toContain("Biscuit");
});
});
describe("buildReminderEmail", () => {
it("addresses the correct recipient", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.to).toBe("jane@example.com");
});
it("says 'tomorrow' for 24-hour reminder", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.subject).toContain("tomorrow");
expect(mail.text).toContain("tomorrow");
});
it("says 'in X hours' for sub-24-hour reminders", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 2);
expect(mail.subject).toContain("in 2 hours");
expect(mail.text).toContain("in 2 hours");
});
it("includes pet name in subject", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.subject).toContain("Biscuit");
});
it("includes service name in plain text body", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.text).toContain("Full Groom");
});
it("includes groomer name when provided", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.text).toContain("Alex");
});
it("omits groomer when groomerName is null", () => {
const mail = buildReminderEmail("jane@example.com", { ...BASE, groomerName: null }, 24);
expect(mail.text).not.toContain("with ");
});
it("includes HTML body", () => {
const mail = buildReminderEmail("jane@example.com", BASE, 24);
expect(mail.html).toBeTruthy();
expect(mail.html).toContain("Biscuit");
});
});