195 lines
7.5 KiB
TypeScript
195 lines
7.5 KiB
TypeScript
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"b"');
|
|
expect(xml).toContain('timestamp="<t>&"');
|
|
expect(xml).toContain('user="u'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({});
|
|
});
|
|
});
|