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; sliceDataForm?: "buffer" | "view" } = {} ): FileMockState { const state: FileMockState = { getFileCalls: 0, requestedSlices: [], closeCalls: 0 }; 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 { // host จริงอาจส่งมาเป็น ArrayBuffer หรือ view — ทดสอบทั้งคู่ const data = options.sliceDataForm === "view" ? slices[index] : slices[index].buffer; callback({ status: "succeeded", value: { index, data, size: slices[index].byteLength } }); } }, closeAsync: (callback?: (result: unknown) => void) => { state.closeCalls++; callback?.({ status: options.failClose ? "failed" : "succeeded" }); }, }; (globalThis as unknown as Record).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 กลางที่พัง → 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); }); });