eCourt-note/tests/wordaddin/autoSaveService.test.ts
Suphonchai Phoonsawat 454c90d17e Add unit tests for autoSaveService to handle document dirty states and periodic saves
- Implement tests for various scenarios including user edits, initial dirty state, and document changes.
- Ensure proper flagging of uncommitted changes and silent saves for documents opened in a dirty state.
- Test debounce behavior for document change notifications and version clearing functionality.
- Validate suspension and resumption of auto-saving during versioning processes.
2026-11-03 13:34:21 +07:00

115 lines
5.4 KiB
TypeScript

// autoSaveService (FR-17/FR-3) — คุมพฤติกรรม flag "มีการแก้ไขที่ยังไม่เป็น version":
// ติดเฉพาะ clean→dirty (ผู้ใช้แก้จริง) หรือ notifyDocumentChanged ของ service ที่แก้เอกสาร
// ส่วนไฟล์ที่เปิดมา dirty ตั้งแต่แรก (quirk ของ Word หลัง stamp properties หรือแก้ค้าง
// จาก session ก่อน) ต้องถูก save เงียบ ๆ ไม่เด้ง banner ให้ผู้ใช้งงว่าไม่ได้ทำอะไร
// (คำขอผู้ใช้ 2026-11-03) — mock documentService แทน Word.run ทั้งหมด
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { autoSaveConfig, autoSaveService } from "@/wordaddin/services/autoSaveService";
const doc = vi.hoisted(() => ({ dirty: false, saves: 0 }));
vi.mock("@/wordaddin/services/documentService", () => ({
isDocumentDirty: vi.fn(async () => doc.dirty),
saveDocument: vi.fn(async () => {
doc.saves += 1;
}),
}));
/** หมายเหตุ timing: seedBaseline ของ start() อ่าน dirty ณ ตอนเรียก — จะซิม "เปิดมา dirty"
* ต้องตั้ง doc.dirty ก่อน startService() แล้ว flush microtask ให้ baseline จบก่อนเดินเวลา */
async function flushMicrotasks(): Promise<void> {
await vi.advanceTimersByTimeAsync(0);
}
/** เดินเวลาไปอีกหนึ่ง periodic tick (30 วิ) แบบ flush microtask ของ tick ด้วย */
async function tick(): Promise<void> {
await vi.advanceTimersByTimeAsync(autoSaveConfig.periodicIntervalMs);
}
beforeEach(() => {
vi.useFakeTimers();
doc.dirty = false;
doc.saves = 0;
});
afterEach(() => {
autoSaveService.stop();
autoSaveService.clearUncommittedSinceVersion();
vi.useRealTimers();
vi.restoreAllMocks();
});
describe("flag FR-3 (hasUncommitted) กับ periodic save", () => {
it("เปิดไฟล์สะอาด → ผู้ใช้แก้จริง (dirty ภายหลัง) → tick ตั้ง flag + save", async () => {
autoSaveService.start();
await flushMicrotasks(); // baseline: สะอาด
doc.dirty = true; // ผู้ใช้พิมพ์
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(true);
expect(doc.saves).toBe(1);
});
it("เปิดมา dirty ตั้งแต่แรก (quirk หลัง stamp/แก้ค้าง) → save เงียบ ๆ ไม่ตั้ง flag", async () => {
doc.dirty = true; // ก่อน start — ไฟล์เปิดมา dirty
autoSaveService.start();
await flushMicrotasks();
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(false);
expect(doc.saves).toBe(1);
});
it("quirk ถาวร (save แล้วยัง dirty ทุกรอบ) → หลาย tick ก็ไม่ตั้ง flag (แต่ยัง save)", async () => {
doc.dirty = true;
autoSaveService.start();
await flushMicrotasks();
await tick();
await tick();
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(false);
expect(doc.saves).toBe(3);
});
it("dirty แรก (quirk) → save แล้วสะอาด → แก้จริงหลังจากนั้น → flag ติด (เห็น clean→dirty)", async () => {
doc.dirty = true;
autoSaveService.start();
await flushMicrotasks();
await tick(); // save + ไม่ flag
doc.dirty = false; // save ทำให้สะอาด
await tick(); // เห็น clean → จุดอ้างอิงกลับมา
doc.dirty = true; // ผู้ใช้แก้จริง
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(true);
});
it("notifyDocumentChanged (service ที่แก้เอกสาร เช่น เครื่องมือ template) → flag ทันที + debounce save 3 วิ", async () => {
autoSaveService.start();
await flushMicrotasks();
doc.dirty = true;
autoSaveService.notifyDocumentChanged();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(true); // ไม่รอ tick
expect(doc.saves).toBe(0); // ยังไม่ save — รอ debounce
await vi.advanceTimersByTimeAsync(autoSaveConfig.debounceMs);
expect(doc.saves).toBe(1);
});
it("สร้าง version สำเร็จ → clearUncommittedSinceVersion เคลียร์ flag", async () => {
autoSaveService.start();
await flushMicrotasks();
doc.dirty = true;
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(true);
autoSaveService.clearUncommittedSinceVersion();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(false);
});
it("suspend ระหว่าง flow (versioning) → tick ไม่ save ไม่แตะ flag", async () => {
autoSaveService.start();
await flushMicrotasks();
doc.dirty = true;
await autoSaveService.suspend();
await tick();
expect(autoSaveService.hasUncommittedSinceVersion).toBe(false);
expect(doc.saves).toBe(0);
autoSaveService.resume();
});
});