Files
genshin-assistant/src/lib/automationPlanner.test.ts
T
AzuTear 639b0b7f59 feat(scanner): add native artifact pipeline
Add native IK-style capture processing, Artifact Inventory, explicit promotion and single-result review. Confirm the three live OCR corrections in the eval corpus and preserve extraction/value separation.
2026-07-09 23:30:42 +02:00

102 lines
3.3 KiB
TypeScript

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);
});
});