Ctrl K
ComunidadGuías…

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

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

Archivo original ↗Preguntar a KiBot
import { assertStringIncludes } from "@std/assert";
import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import { makeTestEntry as makeEntry } from "#test-utils/factories.ts";
import {
  buildTestData,
  describeEmailRenderer,
  TICKET_URL,
} from "./test-helpers.ts";

describeEmailRenderer(() => {
  describe("buildTemplateData", () => {
    test("builds correct data shape from single entry", async () => {
      const data = await buildTestData([makeEntry()]);

      expect(data.listing_names).toBe("Test Listing");
      expect(data.ticket_url).toBe(TICKET_URL);
      expect(data.currency).toBe("GBP");
      expect(data.entries.length).toBe(1);
      expect(data.entries[0]!.listing.name).toBe("Test Listing");
      expect(data.entries[0]!.listing.slug).toBe("test-listing");
      expect(data.entries[0]!.listing.is_paid).toBe(false);
      expect(data.attendee.name).toBe("Jane Doe");
      expect(data.attendee.email).toBe("jane@example.com");
    });

    test("carries each entry's answer lines into the attendee shape", async () => {
      const data = await buildTestData([makeEntry()], {
        answerLines: new Map([
          [42, new Map([[1, [{ question: "Any allergies?", text: "None" }]]])],
        ]),
      });

      expect(data.entries[0]!.attendee.answers).toEqual([
        { question: "Any allergies?", text: "None" },
      ]);
      expect(data.attendee.answers).toEqual(data.entries[0]!.attendee.answers);
    });

    test("lists each member's answers once on a collapsed hidden package's row", async () => {
      const diet = { question: "Diet?", text: "Vegan" };
      const data = await buildTestData(
        [
          makeEntry({}, { package_group_id: 5 }),
          makeEntry({ id: 2, name: "Member" }, { package_group_id: 5 }),
        ],
        {
          answerLines: new Map([
            [
              42,
              new Map([
                [1, [diet]],
                [2, [diet, { question: "Shoe size?", text: "9" }]],
              ]),
            ],
          ]),
          hidePackageMembers: true,
          packageDisplays: new Map([
            [5, { hideListings: true, name: "Hidden bundle" }],
          ]),
        },
      );

      expect(data.entries).toHaveLength(1);
      expect(data.entries[0]!.listing.name).toBe("Hidden bundle");
      expect(data.entries[0]!.attendee.answers).toEqual([
        diet,
        { question: "Shoe size?", text: "9" },
      ]);
    });

    test("builds correct data shape from multiple entries", async () => {
      const data = await buildTestData([
        makeEntry({ name: "Listing A" }),
        makeEntry({ name: "Listing B" }),
      ]);

      expect(data.listing_names).toBe("Listing A and Listing B");
      expect(data.entries.length).toBe(2);
      expect(data.attendee.name).toBe("Jane Doe");
    });

    test("formats three or more listing names with commas and 'and'", async () => {
      const data = await buildTestData([
        makeEntry({ name: "Listing A" }),
        makeEntry({ name: "Listing B" }),
        makeEntry({ name: "Listing C" }),
      ]);

      expect(data.listing_names).toBe("Listing A, Listing B, and Listing C");
    });

    test("marks paid listings correctly", async () => {
      const data = await buildTestData([makeEntry({ unit_price: 1000 })]);

      expect(data.entries[0]!.listing.is_paid).toBe(true);
    });

    test("marks can_pay_more listings as paid", async () => {
      const data = await buildTestData([
        makeEntry({ can_pay_more: true, unit_price: 0 }),
      ]);

      expect(data.entries[0]!.listing.is_paid).toBe(true);
    });

    test("marks a free-base entry as paid from its booking price", async () => {
      // A package override can charge a member whose base listing is free.
      const data = await buildTestData([
        makeEntry({ unit_price: 0 }, { price_paid: "1" }),
      ]);

      expect(data.entries[0]!.listing.is_paid).toBe(true);
    });

    test("includes attendee date when present", async () => {
      const data = await buildTestData([makeEntry({}, { date: "2026-04-15" })]);

      expect(data.entries[0]!.attendee.date).toBe("2026-04-15");
    });

    const dateRangeLabelFor = async (
      listing: Partial<Parameters<typeof makeEntry>[0]>,
      attendee: Partial<Parameters<typeof makeEntry>[1]>,
    ): Promise<string> =>
      (await buildTestData([makeEntry(listing, attendee)])).entries[0]!.attendee
        .date_range_label;

    test("date_range_label: single-day daily booking formats as a date", async () => {
      assertStringIncludes(
        await dateRangeLabelFor(
          { duration_days: 1, listing_type: "daily" },
          { date: "2026-04-15" },
        ),
        "15 April",
      );
    });

    test("date_range_label: multi-day booking uses en dash", async () => {
      expect(
        await dateRangeLabelFor(
          { duration_days: 3, listing_type: "daily" },
          { date: "2026-04-15", end_date: "2026-04-18" },
        ),
      ).toBe("15\u201317 April 2026");
    });

    test("date_range_label: empty when no booking date", async () => {
      expect(await dateRangeLabelFor({}, { date: null })).toBe("");
    });
  });
});