Ctrl K
ComunidadGuías…

test/shared/email-renderer/render-template.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * The Liquid filters and tags a custom email template can use.
 *
 * Driven through `renderEmailContent`, which is how the site really renders a
 * template the owner wrote: it reads their wording out of the settings, so
 * each test saves a template and reads back what would be sent. Rendering it
 * any other way would prove the engine works without proving the owner's
 * wording ever reaches it.
 */

import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import { ALL_SETTINGS_KEYS, settings } from "#db/settings.ts";
import {
  renderEmailContent,
  type TemplateData,
} from "#shared/email-renderer.ts";
import {
  describeEmailRenderer,
  sampleData,
  TICKET_URL,
} from "./test-helpers.ts";

/** Save one template as the owner's own wording, then read back the text the
 * site would send. The plain-text body, because it renders what it is given
 * without any markup of its own around it. */
const sendsWith = async (
  template: string,
  data: TemplateData = sampleData,
): Promise<string> => {
  await settings.update.email.template("confirmation", "text", template);
  // Dropping the cache is not enough: the renderer reads the loaded snapshot,
  // and an empty one falls back to the site's own wording.
  settings.invalidateCache();
  await settings.loadKeys(ALL_SETTINGS_KEYS);
  return (await renderEmailContent("confirmation", data)).text;
};

describeEmailRenderer(() => {
  describe("what a custom template can say", () => {
    test("renders simple variable interpolation", async () => {
      expect(await sendsWith("Hello {{ attendee.name }}")).toBe("Hello Jane");
    });

    test("renders listing_names variable", async () => {
      expect(await sendsWith("For {{ listing_names }}")).toBe("For Concert");
    });

    test("renders ticket_url variable", async () => {
      expect(await sendsWith("{{ ticket_url }}")).toBe(TICKET_URL);
    });

    test("renders currency filter", async () => {
      expect(await sendsWith("{{ 2000 | currency }}")).toBe("£20");
    });

    test("renders currency filter with string value", async () => {
      expect(
        await sendsWith(
          "{% for entry in entries %}{{ entry.attendee.price_paid | currency }}{% endfor %}",
        ),
      ).toBe("£20");
    });

    test("renders pluralize filter for singular", async () => {
      const data = {
        ...sampleData,
        entries: [
          {
            ...sampleData.entries[0]!,
            attendee: { ...sampleData.entries[0]!.attendee, quantity: 1 },
          },
        ],
      };
      expect(
        await sendsWith(
          '{% for entry in entries %}{{ entry.attendee.quantity }} {{ entry.attendee.quantity | pluralize: "ticket", "tickets" }}{% endfor %}',
          data,
        ),
      ).toBe("1 ticket");
    });

    test("renders pluralize filter for plural", async () => {
      expect(
        await sendsWith(
          '{% for entry in entries %}{{ entry.attendee.quantity }} {{ entry.attendee.quantity | pluralize: "ticket", "tickets" }}{% endfor %}',
        ),
      ).toBe("2 tickets");
    });

    test("renders for loop over entries", async () => {
      const data: TemplateData = {
        ...sampleData,
        entries: [
          {
            ...sampleData.entries[0]!,
            listing: { is_paid: false, name: "Listing A", slug: "a" },
          },
          {
            ...sampleData.entries[0]!,
            listing: { is_paid: false, name: "Listing B", slug: "b" },
          },
        ],
      };
      expect(
        await sendsWith(
          "{% for entry in entries %}{{ entry.listing.name }} {% endfor %}",
          data,
        ),
      ).toBe("Listing A Listing B");
    });

    test("renders conditional on is_paid", async () => {
      expect(
        await sendsWith(
          "{% for entry in entries %}{% if entry.listing.is_paid %}paid{% else %}free{% endif %}{% endfor %}",
        ),
      ).toBe("paid");
    });

    test("renders conditional on attendee date", async () => {
      const data: TemplateData = {
        ...sampleData,
        entries: [
          {
            ...sampleData.entries[0]!,
            attendee: {
              ...sampleData.entries[0]!.attendee,
              date: "2026-04-15",
            },
          },
        ],
      };
      expect(
        await sendsWith(
          "{% for entry in entries %}{% if entry.attendee.date %}{{ entry.attendee.date }}{% endif %}{% endfor %}",
          data,
        ),
      ).toBe("2026-04-15");
    });

    test("trims whitespace from rendered output", async () => {
      expect(await sendsWith("  hello  ")).toBe("hello");
    });
  });
});