// Unit tests for RTS Background Sync import { jest } from "@jest/globals"; // Mock Dexie and jQuery globally before importing modules that use them const mockAnswersWhere = jest.fn(); const mockAnswersFilter = jest.fn(); const mockAnswersToArray = jest.fn(); const mockAnswersPut = jest.fn(); const mockTransaction = jest.fn(); globalThis.Dexie = class { constructor(name) { this.name = name; this.answers = { where: mockAnswersWhere, put: mockAnswersPut, }; } version() { return { stores: () => {} }; } transaction(mode, tables, cb) { return mockTransaction(mode, tables, cb); } }; // Define mock jQuery $.post const mockPostDone = jest.fn(); const mockPostFail = jest.fn(); const mockPost = jest.fn(() => ({ done: (cb) => { mockPostDone(cb); return { fail: (failCb) => { mockPostFail(failCb); } }; } })); globalThis.$ = { post: mockPost, }; // Mock DOM elements const mockHtml = jest.fn(); globalThis.document = { getElementById: jest.fn(() => ({ set innerHTML(val) { mockHtml(val); } })), }; // Dynamic import to avoid ES module hoisting execution before mocks are set up let performSync, updateSyncUI; describe("RTS Sync Unit Tests", () => { beforeAll(async () => { const syncModule = await import("../js/sync.js"); performSync = syncModule.performSync; updateSyncUI = syncModule.updateSyncUI; }); beforeEach(() => { jest.clearAllMocks(); }); test("updateSyncUI updates the DOM container", () => { updateSyncUI("syncing"); expect(globalThis.document.getElementById).toHaveBeenCalledWith("sync-status"); expect(mockHtml).toHaveBeenCalledWith(expect.stringContaining("Saving to server...")); }); test("performSync does nothing if exam details are missing", async () => { await performSync(null, {}); expect(mockAnswersWhere).not.toHaveBeenCalled(); }); test("performSync processes unsynced answers and marks them as synced", async () => { const examDetails = { aid: "session-123", cid: "9999", eid: "rapid/1", start_time: 1234567, }; const URLS = { exam_submit_url: "/submit-url", }; const unsyncedAnswers = [ { aid: "session-123", cid: "9999", eid: "rapid/1", qid: 1, qidn: "1", ans: "Normal", synced: false } ]; mockAnswersWhere.mockReturnValue({ filter: mockAnswersFilter.mockReturnValue({ toArray: mockAnswersToArray.mockResolvedValue(unsyncedAnswers) }) }); mockPostDone.mockImplementation((cb) => { cb({ success: true }); }); mockTransaction.mockImplementation(async (mode, tables, cb) => { await cb(); }); await performSync(examDetails, URLS); expect(mockAnswersWhere).toHaveBeenCalledWith({ aid: examDetails.aid, cid: examDetails.cid, eid: examDetails.eid, }); expect(mockPost).toHaveBeenCalledWith( URLS.exam_submit_url, expect.objectContaining({ in_progress: "true", }), null, "json" ); }); });