import { describe, it, expect } from "vitest"; import { encodeSelectedCustomFields, parseSelectedCustomFields, } from "../utils/custom-fields"; import { afterAll } from "vitest"; import { createOffer, getOffer } from "../services/offers.service"; import { renderOfferHtml } from "../routes/admin/offers-pdf"; import type { OfferForPdf } from "../routes/admin/offers-pdf"; import type { company_settings } from "@prisma/client"; import prisma from "../config/database"; describe("selected custom fields encode/decode", () => { it("encodes a non-empty index array to a JSON string", () => { expect(encodeSelectedCustomFields([0, 2])).toBe("[0,2]"); }); it("encodes empty / non-array to null", () => { expect(encodeSelectedCustomFields([])).toBeNull(); expect(encodeSelectedCustomFields(undefined)).toBeNull(); expect(encodeSelectedCustomFields("nope")).toBeNull(); }); it("dedupes, sorts, and drops invalid entries before encoding", () => { expect(encodeSelectedCustomFields([2, 0, 2, -1, 1.5, 3])).toBe("[0,2,3]"); }); it("parses a stored string back to a number array", () => { expect(parseSelectedCustomFields("[0,2]")).toEqual([0, 2]); }); it("parses null / malformed to an empty array", () => { expect(parseSelectedCustomFields(null)).toEqual([]); expect(parseSelectedCustomFields("")).toEqual([]); expect(parseSelectedCustomFields("{garbage")).toEqual([]); expect(parseSelectedCustomFields('"x"')).toEqual([]); }); it("parses already-array input (defensive) and filters invalid", () => { expect(parseSelectedCustomFields([0, "1", 2, -3] as unknown)).toEqual([ 0, 2, ]); }); }); describe("offers selected_custom_fields round-trip", () => { const created: number[] = []; afterAll(async () => { if (created.length) await prisma.quotations.deleteMany({ where: { id: { in: created } } }); }); it("persists selection on create and decodes it on detail", async () => { const res = (await createOffer({ status: "draft", selected_custom_fields: [2, 0, 0], })) as { id: number }; created.push(res.id); const row = await prisma.quotations.findUnique({ where: { id: res.id } }); expect(row?.selected_custom_fields).toBe("[0,2]"); const detail = await getOffer(res.id); expect(detail?.selected_custom_fields).toEqual([0, 2]); }); it("stores null when selection is empty", async () => { const res = (await createOffer({ status: "draft", selected_custom_fields: [], })) as { id: number }; created.push(res.id); const row = await prisma.quotations.findUnique({ where: { id: res.id } }); expect(row?.selected_custom_fields).toBeNull(); }); }); describe("offer PDF prints only selected company custom fields", () => { // Two company custom fields; the document selects only index 0. const settings = { company_name: "Test s.r.o.", custom_fields: JSON.stringify({ fields: [ { name: "Email", value: "selected@example.com", showLabel: false }, { name: "Web", value: "notselected.example.com", showLabel: false }, ], field_order: [], }), logo_data: null, } as unknown as company_settings; const baseQuotation = { quotation_number: "TEST-2098-001", language: "EN", currency: "EUR", valid_until: new Date("2098-01-01"), project_code: null, created_at: new Date("2098-01-01"), customers: { name: "Cust" }, quotation_items: [], scope_sections: [], }; it("renders the selected custom field and omits the non-selected one", () => { const html = renderOfferHtml( { ...baseQuotation, selected_custom_fields: "[0]", } as unknown as OfferForPdf, settings, ); expect(html).toContain("selected@example.com"); expect(html).not.toContain("notselected.example.com"); }); it("prints NO company custom fields when the document selects none", () => { // The company call site always passes a parsed array; a null column // decodes to [] → empty selection → no custom fields on the document. const html = renderOfferHtml( { ...baseQuotation, selected_custom_fields: null, } as unknown as OfferForPdf, settings, ); expect(html).not.toContain("selected@example.com"); expect(html).not.toContain("notselected.example.com"); }); });