// @vitest-environment jsdom // readContentControlFields (FR-11) — คุมว่าตอนสร้าง version อ่านค่า CC **ทุกชนิด**: // host WordApi 1.5+ ต้องยิง Document.getContentControls() (เห็น plain text CC ซึ่งเป็น // ส่วนใหญ่ของ template ศาล — เคสจริง T3 มี plain 26 + rich 1 แต่เดิมเหลือแค่ rich ตัว // เดียวคือ detail1) ส่วน host ต่ำกว่า 1.3 ยังพังลง rich-text-only ตามเดิม (fallback // ไม่ throw) + กติกา tag ว่างข้าม / tag ซ้ำเอาตัวแรกตามลำดับเอกสาร import { afterEach, describe, expect, it, vi } from "vitest"; import { readContentControlFields } from "@/wordaddin/services/metadataService"; interface FakeControl { tag?: string; text: string; } /** stub Word + Office ระดับ global — คืน handle ให้ test ดึง collection ที่ถูกขอไปใช้ */ function stubWord(controls: FakeControl[], options?: { getAllTypes?: boolean }): { documentControls: { load: (p: string) => void }; allTypesControls: { load: (p: string) => void }; } { const makeCollection = () => ({ load: vi.fn(), items: controls.map((control) => ({ tag: control.tag, text: control.text })), }); const documentControls = makeCollection(); const allTypesControls = makeCollection(); vi.stubGlobal("Office", { context: { requirements: { isSetSupported: vi.fn(() => options?.getAllTypes ?? true), }, }, }); vi.stubGlobal("Word", { run: async (batch: (context: unknown) => Promise) => batch({ document: { contentControls: documentControls, getContentControls: vi.fn(() => allTypesControls), }, sync: async () => undefined, }), }); return { documentControls, allTypesControls }; } afterEach(() => { vi.unstubAllGlobals(); }); describe("readContentControlFields", () => { it("host 1.5+ ใช้ getContentControls() — เก็บค่า plain text CC ได้ครบทุกตัว", async () => { const { documentControls, allTypesControls } = stubWord([ { tag: "case_no", text: "ด1" }, // plain text — ตัวที่เดิมหลุดไปทั้งหมด { tag: "court_name", text: "ศาลปกครอง" }, { tag: "detail1", text: "ตุลาการเจ้าของสำนวนได้พิจารณา…" }, ], { getAllTypes: true }); const fields = await readContentControlFields(); expect(fields).toEqual({ case_no: "ด1", court_name: "ศาลปกครอง", detail1: "ตุลาการเจ้าของสำนวนได้พิจารณา…", }); expect(allTypesControls.load).toHaveBeenCalledWith("tag, text"); expect(documentControls.load).not.toHaveBeenCalled(); }); it("host ต่ำกว่า 1.5 (เช่น Office 2019 = 1.3) ยังใช้ collection rich-text-only ตามเดิม", async () => { const { documentControls, allTypesControls } = stubWord([{ tag: "detail1", text: "rich ตัวเดียว" }], { getAllTypes: false, }); const fields = await readContentControlFields(); expect(fields).toEqual({ detail1: "rich ตัวเดียว" }); expect(documentControls.load).toHaveBeenCalledWith("tag, text"); expect(allTypesControls.load).not.toHaveBeenCalled(); }); it("tag ว่างข้าม + tag ซ้ำเอาตัวแรกตามลำดับในเอกสาร + ไม่มี CC คืน {}", async () => { stubWord([ { tag: " ", text: "tag ว่าง" }, { tag: "plain1", text: "ค่าแรก" }, { tag: "plain1", text: "ค่าหลัง" }, ]); expect(await readContentControlFields()).toEqual({ plain1: "ค่าแรก" }); }); it("ไม่มี content control เลย → คืน {} ไม่ error", async () => { stubWord([]); expect(await readContentControlFields()).toEqual({}); }); });