eCourt-Doc-Edit/addin/src/services/metadataService.test.ts

196 lines
7.5 KiB
TypeScript
Raw Normal View History

2026-08-19 21:35:07 +07:00
import {
ADDIN_SIGNATURE_GUID,
buildVersionHistoryXml,
CUSTOM_XML_NAMESPACE,
isTrackedDocument,
readContentControlFields,
readTrackedProperties,
readVersionHistoryFromXml,
stampTrackedProperties,
writeVersionHistoryToXml,
XmlVersionEntry,
} from "./metadataService";
import { installWordMock } from "../test/wordMock";
describe("readTrackedProperties", () => {
it("อ่านค่าได้ครบทั้ง 4 properties เมื่อมีทั้งหมด", async () => {
installWordMock({
properties: {
SavedByAddIn: ADDIN_SIGNATURE_GUID,
DocumentTrackingId: "11111111-1111-1111-1111-111111111111",
LastVersionId: "22222222-2222-2222-2222-222222222222",
LastVersionTimestamp: "2026-08-19T10:00:00Z",
},
});
await expect(readTrackedProperties()).resolves.toEqual({
savedByAddIn: ADDIN_SIGNATURE_GUID,
documentTrackingId: "11111111-1111-1111-1111-111111111111",
lastVersionId: "22222222-2222-2222-2222-222222222222",
lastVersionTimestamp: "2026-08-19T10:00:00Z",
});
});
it("property ที่ไม่มีในเอกสารเป็น undefined ไม่ throw", async () => {
installWordMock({ properties: { UnrelatedProperty: "x" } });
const properties = await readTrackedProperties();
expect(properties.savedByAddIn).toBeUndefined();
expect(properties.documentTrackingId).toBeUndefined();
expect(properties.lastVersionId).toBeUndefined();
expect(properties.lastVersionTimestamp).toBeUndefined();
});
});
describe("isTrackedDocument (FR-1)", () => {
it("true เมื่อ SavedByAddIn ตรง ADDIN_SIGNATURE_GUID", async () => {
installWordMock({ properties: { SavedByAddIn: ADDIN_SIGNATURE_GUID } });
await expect(isTrackedDocument()).resolves.toBe(true);
});
it("false เมื่อค่าไม่ตรง หรือไม่มี property", async () => {
installWordMock({ properties: { SavedByAddIn: "OTHER-ADDIN-GUID" } });
await expect(isTrackedDocument()).resolves.toBe(false);
installWordMock({ properties: {} });
await expect(isTrackedDocument()).resolves.toBe(false);
});
});
describe("stampTrackedProperties (FR-2 ขั้นตอนที่ 2)", () => {
it("stamp ครบ 4 properties ด้วย add() ทับค่าเดิมได้", async () => {
const state = installWordMock({
properties: { SavedByAddIn: "OTHER-ADDIN-GUID", DocumentTrackingId: "old-tracking-id" },
});
await stampTrackedProperties({
documentTrackingId: "new-tracking-id",
lastVersionId: "version-2",
lastVersionTimestamp: "2026-08-19T11:00:00Z",
});
expect(state.propertyAdds).toEqual([
{ key: "SavedByAddIn", value: ADDIN_SIGNATURE_GUID },
{ key: "DocumentTrackingId", value: "new-tracking-id" },
{ key: "LastVersionId", value: "version-2" },
{ key: "LastVersionTimestamp", value: "2026-08-19T11:00:00Z" },
]);
// add() ของ property ที่มีอยู่ = update ค่าในเอกสารจริง
expect(state.properties.SavedByAddIn).toBe(ADDIN_SIGNATURE_GUID);
expect(state.properties.DocumentTrackingId).toBe("new-tracking-id");
});
it("ไม่ส่งค่าใดมา = stamp เฉพาะ SavedByAddIn", async () => {
const state = installWordMock({});
await stampTrackedProperties({});
expect(state.propertyAdds).toEqual([{ key: "SavedByAddIn", value: ADDIN_SIGNATURE_GUID }]);
});
});
describe("Custom XML Part (delete-then-add, silent fail)", () => {
const entries: XmlVersionEntry[] = [
{ id: "version-3", number: 3, timestamp: "2026-08-19T12:00:00Z", user: "tester" },
{ id: "version-2", number: 2, timestamp: "2026-08-19T11:00:00Z", user: "other" },
];
it("writeVersionHistoryToXml ลบ part เดิมทั้งหมดก่อน add ของใหม่ แล้วคืน true", async () => {
const state = installWordMock({
xmlParts: [
buildVersionHistoryXml([{ id: "version-1", number: 1, timestamp: "t", user: "u" }]),
],
});
await expect(writeVersionHistoryToXml(entries)).resolves.toBe(true);
// อันเดิมถูกลบ เหลืออันใหม่อันเดียว (อยู่ใน namespace ของเรา)
expect(state.xmlParts).toHaveLength(1);
expect(state.xmlParts[0].namespace).toBe(CUSTOM_XML_NAMESPACE);
expect(state.xmlParts[0].xml).toContain('id="version-3"');
expect(state.xmlParts[0].xml).toContain('number="3"');
expect(state.xmlParts[0].xml).toContain('user="tester"');
});
it("เขียนไม่ได้ (known bug บน Mac) → คืน false ไม่ throw (AC-10)", async () => {
installWordMock({ failXmlAdd: true });
await expect(writeVersionHistoryToXml(entries)).resolves.toBe(false);
});
it("readVersionHistoryFromXml parse รายการและเรียงใหม่ → เก่า", async () => {
installWordMock({
xmlParts: [
buildVersionHistoryXml([
{ id: "version-1", number: 1, timestamp: "2026-08-19T09:00:00Z", user: "a" },
{ id: "version-3", number: 3, timestamp: "2026-08-19T12:00:00Z", user: "b" },
{ id: "version-2", number: 2, timestamp: "2026-08-19T11:00:00Z", user: "c" },
]),
],
});
const result = await readVersionHistoryFromXml();
expect(result.map((entry) => entry.number)).toEqual([3, 2, 1]);
expect(result[0]).toEqual({
id: "version-3",
number: 3,
timestamp: "2026-08-19T12:00:00Z",
user: "b",
});
});
it("อ่าน fail → คืน [] ไม่ throw (XML part เป็น secondary เท่านั้น)", async () => {
installWordMock({ failXmlRead: true });
await expect(readVersionHistoryFromXml()).resolves.toEqual([]);
});
it("buildVersionHistoryXml escape อักขระพิเศษใน attribute", () => {
const xml = buildVersionHistoryXml([
{ id: 'a"b', number: 1, timestamp: "<t>&", user: "u'ser" },
]);
expect(xml).toContain('id="a&quot;b"');
expect(xml).toContain('timestamp="&lt;t&gt;&amp;"');
expect(xml).toContain('user="u&apos;ser"');
});
});
describe("readContentControlFields (FR-11)", () => {
it("แปลง contentControls เป็น map tag → text", async () => {
installWordMock({
contentControls: [
{ tag: "employee_name", text: "สมชาย ใจดี" },
{ tag: "salary", text: "50000" },
{ tag: "start_date", text: "2026-08-01" },
],
});
await expect(readContentControlFields()).resolves.toEqual({
employee_name: "สมชาย ใจดี",
salary: "50000",
start_date: "2026-08-01",
});
});
it("tag ซ้ำเอาตัวแรกตามลำดับในเอกสาร / tag ว่างข้าม", async () => {
installWordMock({
contentControls: [
{ tag: "position", text: "first" },
{ tag: "position", text: "second" },
{ tag: "", text: "ไม่มี tag" },
{ tag: " ", text: "tag วรรคล้วน" },
{ tag: "note", text: "ok" },
],
});
await expect(readContentControlFields()).resolves.toEqual({
position: "first",
note: "ok",
});
});
it("เอกสารไม่มี content controls → คืน {} ไม่ error", async () => {
installWordMock({ contentControls: [] });
await expect(readContentControlFields()).resolves.toEqual({});
});
});