import { describe, expect, it } from "vitest"; import { displayRectToPdf, displaySize, displayToPdfPoint, encodeRFC5987, hexToRgb01, wrapStyledText, wrapTextByWidth, } from "@/utils/pdf-export"; // หน้า A4 portrait จริง (pt) — display ของ 90/270 จะสลับเป็น 792×612 const PAGE = { width: 612, height: 792 }; describe("displaySize", () => { it("/Rotate 0 และ 180 → ขนาดหน้าเดิม", () => { expect(displaySize(PAGE, 0)).toEqual(PAGE); expect(displaySize(PAGE, 180)).toEqual(PAGE); }); it("/Rotate 90 และ 270 → สลับกว้าง-สูง", () => { expect(displaySize(PAGE, 90)).toEqual({ width: 792, height: 612 }); expect(displaySize(PAGE, 270)).toEqual({ width: 792, height: 612 }); }); }); describe("displayToPdfPoint", () => { it("rot 0: พลิกแกน y (จอ y ลง → PDF y ขึ้น)", () => { expect(displayToPdfPoint({ x: 10, y: 20 }, PAGE, 0)).toEqual({ x: 10, y: 772 }); }); it("rot 90: จอมุมบนซ้าย (0,0) → PDF (0,0)", () => { expect(displayToPdfPoint({ x: 0, y: 0 }, PAGE, 90)).toEqual({ x: 0, y: 0 }); expect(displayToPdfPoint({ x: 10, y: 20 }, PAGE, 90)).toEqual({ x: 20, y: 10 }); }); it("rot 180: มุมบนซ้ายจอ → มุมขวาล่างของ PDF", () => { expect(displayToPdfPoint({ x: 10, y: 20 }, PAGE, 180)).toEqual({ x: 602, y: 20 }); }); it("rot 270: มุมบนซ้ายจอ → มุมขวาบนของ PDF", () => { expect(displayToPdfPoint({ x: 10, y: 20 }, PAGE, 270)).toEqual({ x: 592, y: 782 }); }); }); describe("displayRectToPdf", () => { it("rot 0: y พลิก (y เดิม = ขอบบนจอ → กลายเป็น y บนของ rect PDF)", () => { expect(displayRectToPdf({ x: 10, y: 20, width: 30, height: 40 }, PAGE, 0)).toEqual({ x: 10, y: 732, width: 30, height: 40, }); }); it("rot 90: กว้าง-สูงสลับกัน และ normalized เป็นบวกเสมอ", () => { expect(displayRectToPdf({ x: 10, y: 20, width: 30, height: 40 }, PAGE, 90)).toEqual({ x: 20, y: 10, width: 40, height: 30, }); }); }); describe("hexToRgb01", () => { it("แปลง #rrggbb เป็นสัดส่วน 0–1", () => { expect(hexToRgb01("#ff0000")).toEqual({ r: 1, g: 0, b: 0 }); expect(hexToRgb01("#00ff00")).toEqual({ g: 1, r: 0, b: 0 }); expect(hexToRgb01("#0000ff")).toEqual({ r: 0, g: 0, b: 1 }); }); it("ไม่มี # นำหน้า / ตัวพิมพ์ใหญ่ก็รับ", () => { expect(hexToRgb01("ff8000")).toEqual({ r: 1, g: 128 / 255, b: 0 }); expect(hexToRgb01("#FF8000")).toEqual({ r: 1, g: 128 / 255, b: 0 }); }); it("รูปแบบผิด → ดำ (fallback กัน parse พัง)", () => { expect(hexToRgb01("abc")).toEqual({ r: 0, g: 0, b: 0 }); expect(hexToRgb01("#zzzzzz")).toEqual({ r: 0, g: 0, b: 0 }); expect(hexToRgb01("")).toEqual({ r: 0, g: 0, b: 0 }); }); }); describe("wrapTextByWidth", () => { // จำลองฟอนต์ที่ตัวอักษรกว้าง 1 หน่วยทุกตัว const w = (s: string) => s.length; it("ตัดทีละตัวอักษร (ภาษาไทยไม่มีช่องว่างคำ)", () => { expect(wrapTextByWidth("อบจทดสอบ", 5, w)).toEqual(["อบจทด", "สอบ"]); }); it("ขึ้นบรรทัดใหม่ด้วย \\n แยกจากการ wrap", () => { expect(wrapTextByWidth("aaaa\nbb", 5, w)).toEqual(["aaaa", "bb"]); }); it("คำยาวเกิน maxWidth → แตกต่อทันทีไม่มีการกลืน", () => { expect(wrapTextByWidth("x".repeat(12), 5, w)).toEqual(["xxxxx", "xxxxx", "xx"]); }); it("ข้อความว่าง → บรรทัดว่างเดียว", () => { expect(wrapTextByWidth("", 5, w)).toEqual([""]); }); }); describe("wrapStyledText", () => { // ตัวหนากว้าง 2 เท่าของปกติ const w = (s: string, bold: boolean) => s.length * (bold ? 2 : 1); it("run ผสมหนา/ปกติ wrap ตามความกว้างจริงของแต่ละตัว แล้วม้วนกลับเป็น run ต่อบรรทัด", () => { const out = wrapStyledText( [ { text: "ab", bold: true }, { text: "cdef", bold: false }, ], 6, w, ); // a(2)+b(2)+c(1)+d(1) = 6 พอดีบรรทัดแรก · e,f ไปบรรทัดสอง expect(out).toEqual([ [ { text: "ab", bold: true }, { text: "cd", bold: false }, ], [{ text: "ef", bold: false }], ]); }); it("\\n ใน run ตัดบรรทัดแบบชัด ๆ", () => { const out = wrapStyledText([{ text: "a\nb", bold: false }], 10, w); expect(out).toEqual([[{ text: "a", bold: false }], [{ text: "b", bold: false }]]); }); it("ตัวอักษรที่กว้างเกิน maxWidth ทั้งบรรทัด ยังวางลงบรรทัดใหม่ได้ (ไม่ทิ้ง)", () => { const out = wrapStyledText([{ text: "ab", bold: true }], 1, w); expect(out).toEqual([[{ text: "a", bold: true }], [{ text: "b", bold: true }]]); }); }); describe("encodeRFC5987", () => { it("ชื่อไฟล์ภาษาไทย encode เป็น percent-encoding UTF-8", () => { expect(encodeRFC5987("คำพิพากษา.pdf")).toBe( "%E0%B8%84%E0%B8%B3%E0%B8%9E%E0%B8%B4%E0%B8%9E%E0%B8%B2%E0%B8%81%E0%B8%A9%E0%B8%B2.pdf", ); }); it("อักขระ '()* ที่ encodeURIComponent ปล่อยไว้ ต้องถูก encode เพิ่มตาม RFC 5987", () => { expect(encodeRFC5987("a'b(c)*d")).toBe("a%27b%28c%29%2Ad"); }); });