130 lines
4.3 KiB
TypeScript
130 lines
4.3 KiB
TypeScript
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");
|
|
});
|
|
});
|