add-in: auto-open taskpane phase 1 + final save after versioning + tracking id = fid
- manifest: TaskpaneId -> Office.AutoShowTaskpaneWithDocument (auto-open marker), bump 1.0.0.4 - metadataService.enableAutoOpenTaskpane: tag doc setting (idempotent, best-effort) called from detect() after sign-in - versionFlow: final saveDocumentVerified at end of both versioning flows (bug: stamped docx never persisted -> reopen showed untracked) - tracking id now uses ticket fid (TRACKING_ID_SOURCE="fid", rollback by switching to "guid"), resolveTrackingId + authService.getCurrentFid - WordAddinGate: restore history.pushState/replaceState that office.js nullifies in taskpane (fixes Next App Router crash) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
02894f2705
commit
9fecaa7bca
8 changed files with 256 additions and 6 deletions
|
|
@ -93,3 +93,103 @@ describe("readContentControlFields", () => {
|
|||
expect(await readContentControlFields()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
// ─── enableAutoOpenTaskpane (auto-open 2026-11-05) ──────────────────────────
|
||||
// function มี module-level memo (กัน double-call ของ detect) — แยกเคสด้วย
|
||||
// vi.resetModules + dynamic import ให้ทุกเคสได้ module ใหม่ ไม่ให้ memo รั่ยข้ามเคส
|
||||
interface StubbedSettings {
|
||||
get: ReturnType<typeof vi.fn>;
|
||||
set: ReturnType<typeof vi.fn>;
|
||||
saveAsync: ReturnType<typeof vi.fn>;
|
||||
}
|
||||
|
||||
/** stub Office.context.document.settings + Word.run ของ saveDocumentVerified */
|
||||
function stubAutoOpen(options?: { existing?: boolean; saveOk?: boolean; nativeSaveThrows?: boolean }): {
|
||||
settings: StubbedSettings;
|
||||
saveFn: ReturnType<typeof vi.fn>;
|
||||
} {
|
||||
const settings = {
|
||||
get: vi.fn(() => (options?.existing === true ? true : undefined)),
|
||||
set: vi.fn(),
|
||||
saveAsync: vi.fn((callback: (result: { status: string }) => void) =>
|
||||
callback({ status: options?.saveOk === false ? "failed" : "succeeded" }),
|
||||
),
|
||||
};
|
||||
const saveFn = vi.fn(() => {
|
||||
if (options?.nativeSaveThrows) throw new Error("read-only");
|
||||
});
|
||||
vi.stubGlobal("Office", {
|
||||
AsyncResultStatus: { Succeeded: "succeeded", Failed: "failed" },
|
||||
context: { document: { settings } },
|
||||
});
|
||||
vi.stubGlobal("Word", {
|
||||
run: async (batch: (context: unknown) => Promise<unknown>) =>
|
||||
batch({
|
||||
document: { save: saveFn, load: vi.fn(), saved: true },
|
||||
sync: async () => undefined,
|
||||
}),
|
||||
});
|
||||
return { settings, saveFn };
|
||||
}
|
||||
|
||||
async function importFreshMetadata() {
|
||||
vi.resetModules();
|
||||
return await import("@/wordaddin/services/metadataService");
|
||||
}
|
||||
|
||||
describe("enableAutoOpenTaskpane", () => {
|
||||
it("ยังไม่มี setting → set(true) + saveAsync + native save ครบ คืน true", async () => {
|
||||
const { settings, saveFn } = stubAutoOpen();
|
||||
const metadata = await importFreshMetadata();
|
||||
|
||||
const ok = await metadata.enableAutoOpenTaskpane();
|
||||
|
||||
expect(ok).toBe(true);
|
||||
expect(metadata.AUTO_OPEN_SETTING).toBe("Office.AutoShowTaskpaneWithDocument");
|
||||
expect(settings.set).toHaveBeenCalledWith("Office.AutoShowTaskpaneWithDocument", true);
|
||||
expect(settings.saveAsync).toHaveBeenCalledTimes(1);
|
||||
expect(saveFn).toHaveBeenCalled(); // saveDocumentVerified ตามหลัง saveAsync
|
||||
});
|
||||
|
||||
it("มี setting = true อยู่แล้ว → no-op idempotent ไม่ set ไม่ saveAsync", async () => {
|
||||
const { settings, saveFn } = stubAutoOpen({ existing: true });
|
||||
const metadata = await importFreshMetadata();
|
||||
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(true);
|
||||
expect(settings.set).not.toHaveBeenCalled();
|
||||
expect(settings.saveAsync).not.toHaveBeenCalled();
|
||||
expect(saveFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("นอก host Word (ไม่มี Office global) → คืน false ไม่ throw", async () => {
|
||||
const metadata = await importFreshMetadata(); // ไม่ stub อะไรเลย
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(false);
|
||||
});
|
||||
|
||||
it("settings.saveAsync ล้ม → คืน false ไม่ throw", async () => {
|
||||
stubAutoOpen({ saveOk: false });
|
||||
const metadata = await importFreshMetadata();
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(false);
|
||||
});
|
||||
|
||||
it("native save ล้ม (ไฟล์ read-only) → ยังคืน true (setting เข้า memory doc แล้ว) + warn ไม่ throw", async () => {
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
||||
stubAutoOpen({ nativeSaveThrows: true });
|
||||
const metadata = await importFreshMetadata();
|
||||
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(true);
|
||||
expect(warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("memo: ล้มครั้งแรกแล้วล้าง — call ถัดไปยิงใหม่ได้ (สำเร็จในครั้งที่สอง)", async () => {
|
||||
const { settings } = stubAutoOpen({ saveOk: false });
|
||||
const metadata = await importFreshMetadata();
|
||||
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(false);
|
||||
settings.saveAsync.mockImplementation(
|
||||
(callback: (result: { status: string }) => void) => callback({ status: "succeeded" }),
|
||||
);
|
||||
expect(await metadata.enableAutoOpenTaskpane()).toBe(true);
|
||||
expect(settings.saveAsync).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
18
tests/wordaddin/versionFlow.test.ts
Normal file
18
tests/wordaddin/versionFlow.test.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// @vitest-environment jsdom
|
||||
// resolveTrackingId (งาน B 2026-11-05) — tracking id ของ Start Versioning ใช้ fid จาก
|
||||
// ticket แทน GUID สุ่มใหม่ (TRACKING_ID_SOURCE = "fid") · ไม่มี fid = fallback GUID เดิม
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { resolveTrackingId } from "@/wordaddin/services/versionFlow";
|
||||
|
||||
const GUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
||||
|
||||
describe("resolveTrackingId", () => {
|
||||
it("มี fid (sign-in ผ่านแล้ว) → ใช้ fid ตรง ๆ ตรงกับ docId ฝั่ง server", () => {
|
||||
expect(resolveTrackingId("T12_FD155_3s-long_ZZZ")).toBe("T12_FD155_3s-long_ZZZ");
|
||||
});
|
||||
|
||||
it("ไม่มี fid (null) → fallback เป็น GUID รูปแบบเดิม", () => {
|
||||
expect(resolveTrackingId(null)).toMatch(GUID_RE);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue