test/shared/bulk-email/core.test.ts
Rama main · réplica de lectura del repositorio original.
import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import {
type BulkEmailDraft,
contactFrequencySummary,
parseDraft,
serializeDraft,
validateDraftInput,
} from "#shared/bulk-email.ts";
import {
targetComposeControl,
targetQuery,
} from "#shared/bulk-email-targets/registry.ts";
import {
AUDIENCES,
audienceById,
DEFAULT_AUDIENCE_ID,
isAudienceId,
} from "#shared/bulk-email-targets/types.ts";
import { MAX_INPUT_LENGTH, MAX_TEXTAREA_LENGTH } from "#shared/limits.ts";
const audienceTarget = (audience: BulkEmailDraft["target"]) => audience;
describe("bulk-email audiences and targets", () => {
test("AUDIENCES has a stable default that exists in the registry", () => {
expect(isAudienceId(DEFAULT_AUDIENCE_ID)).toBe(true);
expect(
AUDIENCES.some((audience) => audience.id === DEFAULT_AUDIENCE_ID),
).toBe(true);
});
test("isAudienceId rejects unknown ids", () => {
expect(isAudienceId("active")).toBe(true);
expect(isAudienceId("nonsense")).toBe(false);
});
test("audienceById returns the matching definition", () => {
expect(audienceById("all").label).toBe("All attendees");
});
test("targetQuery round-trips audience and listing targets", () => {
expect(targetQuery({ audience: "upcoming", kind: "audience" })).toBe(
"?audience=upcoming",
);
expect(targetQuery({ kind: "listing", listingId: 7 })).toBe("?listing=7");
});
test("targetQuery URL-encodes an attendee token", () => {
expect(targetQuery({ kind: "attendee", token: "abc/def+ghi" })).toBe(
"?attendee=abc%2Fdef%2Bghi",
);
});
test("targetComposeControl drives the recipient control per kind", () => {
const audience = targetComposeControl({
audience: "active",
kind: "audience",
});
expect(audience.mode).toBe("select");
if (audience.mode === "select") {
expect(audience.name).toBe("audience");
expect(audience.selected).toBe("active");
expect(audience.options.map((option) => option.value)).toEqual(
AUDIENCES.map((entry) => entry.id),
);
}
expect(targetComposeControl({ kind: "listing", listingId: 7 })).toEqual({
fields: [["listing_id", "7"]],
mode: "fixed",
});
expect(targetComposeControl({ kind: "attendee", token: "tok" })).toEqual({
fields: [["attendee", "tok"]],
mode: "fixed",
});
});
});
describe("bulk-email draft validation and serialization", () => {
const target = audienceTarget({ audience: "active", kind: "audience" });
test("accepts a valid draft", () => {
const result = validateDraftInput({
body: "Hello there",
marketing: true,
subject: "Subject",
target,
});
expect(result.valid).toBe(true);
if (result.valid) {
expect(result.draft.subject).toBe("Subject");
expect(result.draft.marketing).toBe(true);
}
});
test("rejects an empty subject", () => {
const result = validateDraftInput({
body: "Body",
marketing: false,
subject: " ",
target,
});
expect(result).toEqual({ error: "Subject is required", valid: false });
});
test("rejects an over-length subject", () => {
const result = validateDraftInput({
body: "Body",
marketing: false,
subject: "x".repeat(MAX_INPUT_LENGTH + 1),
target,
});
expect(result.valid).toBe(false);
});
test("accepts a subject at the shared single-line limit", () => {
const draft = {
body: "Body",
marketing: false,
subject: "x".repeat(MAX_INPUT_LENGTH),
target,
};
expect(validateDraftInput(draft)).toEqual({ draft, valid: true });
});
test("rejects an empty body", () => {
const result = validateDraftInput({
body: " ",
marketing: false,
subject: "Subject",
target,
});
expect(result).toEqual({ error: "Message body is required", valid: false });
});
test("rejects an over-length body", () => {
const result = validateDraftInput({
body: "x".repeat(MAX_TEXTAREA_LENGTH + 1),
marketing: false,
subject: "Subject",
target,
});
expect(result.valid).toBe(false);
});
test("serialize/parse round-trips", () => {
const draft: BulkEmailDraft = {
body: "Body",
marketing: true,
subject: "Subject",
target,
};
expect(parseDraft(serializeDraft(draft))).toEqual(draft);
});
test("parseDraft returns null for empty, malformed, or invalid drafts", () => {
expect(parseDraft("")).toBe(null);
expect(parseDraft("{not json")).toBe(null);
expect(parseDraft(JSON.stringify({ subject: 1 }))).toBe(null);
expect(
parseDraft(
JSON.stringify({
body: "b",
marketing: false,
subject: "s",
target: { audience: "bogus", kind: "audience" },
}),
),
).toBe(null);
});
});
describe("contactFrequencySummary", () => {
test("is empty with no recipients", () => {
expect(contactFrequencySummary([])).toBe("");
});
test("reports never-contacted when all counts are zero", () => {
expect(contactFrequencySummary([0, 0, 0])).toBe(
"These attendees have never been contacted through this page.",
);
});
test("reports a whole number when the average is an integer", () => {
expect(contactFrequencySummary([2, 2, 2])).toBe(
"These attendees have been contacted through this page 2 times each.",
);
});
test("reports a one-decimal average otherwise", () => {
expect(contactFrequencySummary([1, 2])).toBe(
"These attendees have been contacted through this page an average of 1.5 times each.",
);
});
});