eCourt-Doc-Edit/addin/src/services/fileService.test.ts
2026-08-19 23:06:57 +07:00

137 lines
4.7 KiB
TypeScript

import { getCurrentDocumentFile, DOCX_CONTENT_TYPE } from "./fileService";
interface FileMockState {
getFileCalls: number;
requestedSlices: number[];
closeCalls: number;
}
/** mock Office.context.document.getFileAsync + File.getSliceAsync/closeAsync */
function installOfficeFileMock(
slices: Uint8Array[],
options: {
failSliceIndex?: number;
failClose?: boolean;
/** รูปร่างของ slice.data ที่ host จริงส่งกลับ — ต่างกันตามแพลตฟอร์ม (Win=ArrayBuffer, Mac=string/array) */
sliceDataForm?: "buffer" | "view" | "array" | "base64";
} = {}
): FileMockState {
const state: FileMockState = { getFileCalls: 0, requestedSlices: [], closeCalls: 0 };
const toSliceData = (bytes: Uint8Array): unknown => {
switch (options.sliceDataForm) {
case "view":
return bytes;
case "array":
return Array.from(bytes);
case "base64":
return Buffer.from(bytes).toString("base64");
default:
return bytes.buffer;
}
};
const file = {
sliceCount: slices.length,
getSliceAsync: (index: number, callback: (result: unknown) => void) => {
state.requestedSlices.push(index);
if (options.failSliceIndex === index) {
callback({ status: "failed", error: { message: `อ่าน slice ${index} พัง (mock)` } });
} else {
callback({
status: "succeeded",
value: { index, data: toSliceData(slices[index]), size: slices[index].byteLength },
});
}
},
closeAsync: (callback?: (result: unknown) => void) => {
state.closeCalls++;
callback?.({ status: options.failClose ? "failed" : "succeeded" });
},
};
(globalThis as unknown as Record<string, unknown>).Office = {
FileType: { Compressed: "compressed", Text: "text" },
AsyncResultStatus: { Succeeded: "succeeded", Failed: "failed" },
context: {
document: {
getFileAsync: (
_fileType: unknown,
_options: unknown,
callback: (result: unknown) => void
) => {
state.getFileCalls++;
callback({ status: "succeeded", value: file });
},
},
},
};
return state;
}
function bytes(...values: number[]): Uint8Array {
return new Uint8Array(values);
}
describe("getCurrentDocumentFile", () => {
it("ประกอบ slice หลากขนาดเป็น Blob เดียว อ่านครบทุก slice ตามลำดับ และ closeAsync หนึ่งครั้ง", async () => {
const state = installOfficeFileMock([
bytes(1, 2, 3),
bytes(4, 5),
new Uint8Array(2048).fill(9),
]);
const blob = await getCurrentDocumentFile();
expect(blob.type).toBe(DOCX_CONTENT_TYPE);
expect(blob.size).toBe(3 + 2 + 2048);
expect(state.requestedSlices).toEqual([0, 1, 2]);
expect(state.getFileCalls).toBe(1);
expect(state.closeCalls).toBe(1);
});
it("slice มาเป็น typed array view (ไม่ใช่ ArrayBuffer) ก็ประกอบได้", async () => {
const state = installOfficeFileMock([bytes(10, 20), bytes(30)], { sliceDataForm: "view" });
const blob = await getCurrentDocumentFile();
expect(blob.size).toBe(3);
expect(state.closeCalls).toBe(1);
});
it("slice มาเป็น byte array ธรรมดา number[] (Word บน Mac) ก็ประกอบได้", async () => {
const state = installOfficeFileMock([bytes(10, 20), bytes(30, 40)], { sliceDataForm: "array" });
const blob = await getCurrentDocumentFile();
expect(blob.size).toBe(4);
expect(state.closeCalls).toBe(1);
});
it("slice มาเป็น base64 string ก็ decode แล้วประกอบได้", async () => {
const state = installOfficeFileMock([bytes(200, 100, 50), bytes(255)], {
sliceDataForm: "base64",
});
const blob = await getCurrentDocumentFile();
expect(blob.size).toBe(4);
expect(state.closeCalls).toBe(1);
});
it("อ่าน slice กลางที่พัง → reject พร้อม error แต่ยังเรียก closeAsync เสมอ", async () => {
const state = installOfficeFileMock([bytes(1), bytes(2), bytes(3)], { failSliceIndex: 1 });
await expect(getCurrentDocumentFile()).rejects.toThrow("อ่าน slice 1 พัง (mock)");
expect(state.closeCalls).toBe(1);
expect(state.requestedSlices).toEqual([0, 1]);
});
it("closeAsync fail เอง → ไม่ทำ flow พัง (blob ยังคืนมา)", async () => {
installOfficeFileMock([bytes(7, 8, 9)], { failClose: true });
const blob = await getCurrentDocumentFile();
expect(blob.size).toBe(3);
});
});