import { describe, expect, it } from "vitest"; import { automationBlockReason, buildInventoryPagePlan, buildGridModel, requiresAdminForAutomation, } from "./automationPlanner"; describe("automationPlanner", () => { it("requires admin whenever this app's own process is not elevated", () => { expect(requiresAdminForAutomation({ ok: true, isElevated: false, platform: "win32" })).toBe(true); expect(requiresAdminForAutomation({ ok: true, isElevated: true, platform: "win32" })).toBe(false); expect(requiresAdminForAutomation({ ok: false, isElevated: false, platform: "win32" })).toBe(false); expect(requiresAdminForAutomation(null)).toBe(false); }); it("returns a user-facing admin block reason when not elevated", () => { expect(automationBlockReason({ ok: true, isElevated: false, platform: "win32" })).toContain("Administrator"); expect(automationBlockReason({ ok: true, isElevated: true, platform: "win32" })).toBe(""); }); it("builds a dense click grid from partial detected centers", () => { const grid = buildGridModel({ rows: 3, cols: 4, confidence: 96, source: "detected", centers: [ { row: 0, col: 0, x: 100, y: 200 }, { row: 0, col: 1, x: 220, y: 200 }, { row: 0, col: 3, x: 460, y: 200 }, { row: 2, col: 0, x: 100, y: 500 }, { row: 2, col: 3, x: 460, y: 500 }, ], }); expect(grid).not.toBeNull(); expect(grid?.targets).toHaveLength(12); expect(grid?.stepX).toBe(120); expect(grid?.stepY).toBe(150); expect(grid?.targets.find((target) => target.row === 1 && target.col === 2)).toMatchObject({ x: 340, y: 350 }); }); it("plans overlapping inventory pages for a partial final page", () => { const targets = Array.from({ length: 32 }, (_, index) => ({ row: Math.floor(index / 8), col: index % 8, x: index * 10, y: index * 10, })); const firstPage = buildInventoryPagePlan({ targets, cols: 8, rows: 4, totalTargetCount: 45, processedTargets: 0, rowsQueued: 0, }); expect(firstPage.pageTargets).toHaveLength(32); expect(firstPage.startIndex).toBe(0); expect(firstPage.scrollRowsAfterPage).toBe(2); const finalPage = buildInventoryPagePlan({ targets, cols: 8, rows: 4, totalTargetCount: 45, processedTargets: 32, rowsQueued: 4, }); expect(finalPage.pageTargets).toHaveLength(13); expect(finalPage.startIndex).toBe(16); expect(finalPage.pageTargets[0]).toMatchObject({ row: 2, col: 0 }); expect(finalPage.scrollRowsAfterPage).toBe(0); }); it("keeps the first page top-aligned when the whole inventory fits inside one visible page", () => { const targets = Array.from({ length: 32 }, (_, index) => ({ row: Math.floor(index / 8), col: index % 8, x: index * 10, y: index * 10, })); const singlePage = buildInventoryPagePlan({ targets, cols: 8, rows: 4, totalTargetCount: 16, processedTargets: 0, rowsQueued: 0, }); expect(singlePage.pageTargets).toHaveLength(16); expect(singlePage.startIndex).toBe(0); expect(singlePage.pageTargets[0]).toMatchObject({ row: 0, col: 0 }); expect(singlePage.pageTargets[15]).toMatchObject({ row: 1, col: 7 }); expect(singlePage.scrollRowsAfterPage).toBe(0); }); });