import test from "node:test"; import assert from "node:assert/strict"; import { isCalibrationReady, isRectInside, parseFieldValue, parseStoreHeaderText, parseTooltipText, rectFromPoints, resolveFieldRect, } from "../src/parser.js"; test("normalizes a reverse drag into a positive rectangle", () => { assert.deepEqual(rectFromPoints({ x: 140, y: 90 }, { x: 20, y: 30 }), { x: 20, y: 30, width: 120, height: 60, }); }); test("moves calibrated fields together with the detected anchor", () => { assert.deepEqual( resolveFieldRect( { x: 400, y: 250 }, { offsetX: 32, offsetY: 80, width: 180, height: 24 }, ), { x: 432, y: 330, width: 180, height: 24 }, ); }); test("parses formatted prices and rejects empty numeric OCR", () => { assert.equal(parseFieldValue("price", "1 250,000 adena"), 1250000); assert.equal(parseFieldValue("quantity", "not found"), null); assert.equal(parseFieldValue("item", " Soulshot: S-grade\n"), "Soulshot: S-grade"); }); test("requires both anchors and exact OCR regions", () => { assert.equal(isCalibrationReady({ saleAnchor: null }), false); assert.equal( isCalibrationReady({ saleAnchor: { image: "data:image/png;base64,x", width: 20, height: 10 }, storeHeaderRegion: { offsetX: -50, offsetY: -300, width: 250, height: 24 }, tooltipSearchRegion: { offsetX: -100, offsetY: -300, width: 300, height: 100 }, tooltipAnchor: { image: "data:image/png;base64,y", width: 30, height: 12 }, itemNameRegion: { offsetX: 0, offsetY: -18, width: 220, height: 18 }, itemPriceRegion: { offsetX: 0, offsetY: 0, width: 160, height: 18 }, }), true, ); }); test("detects OCR rectangles outside the captured frame", () => { assert.equal(isRectInside({ x: 10, y: 10, width: 20, height: 20 }, 100, 100), true); assert.equal(isRectInside({ x: 90, y: 10, width: 20, height: 20 }, 100, 100), false); }); test("parses a hovered item tooltip", () => { assert.deepEqual( parseTooltipText("Tears of Eva\nPrice : 3,000,000 Adena\n(3 Million Adena)"), { name: "Tears of Eva", quantity: null, priceAdena: 3000000, rawText: "Tears of Eva\nPrice : 3,000,000 Adena\n(3 Million Adena)", }, ); assert.deepEqual(parseTooltipText("Animal Skin (18)\nFor Each 400 Adena"), { name: "Animal Skin", quantity: 18, priceAdena: 400, rawText: "Animal Skin (18)\nFor Each 400 Adena", }); assert.equal(parseTooltipText("Animal Skin (18)\n400")?.priceAdena, 400); assert.equal(parseTooltipText("Animal Skin (18)\nPrice : 400")?.priceAdena, 400); assert.equal(parseTooltipText("Blessed Spiritshot D (5,600)\nFor Each 56 Adena")?.quantity, 5600); assert.equal(parseTooltipText("Purchase List\nAdena 5,403,160"), null); }); test("parses trade side and merchant from the store header", () => { assert.deepEqual(parseStoreHeaderText("Private Store(Buy) - Hikvision"), { side: "buy", merchant: "Hikvision", rawText: "Private Store(Buy) - Hikvision", }); assert.deepEqual(parseStoreHeaderText(" Private Store({Sell) - Dwa \n"), { side: "sell", merchant: "Dwa", rawText: "Private Store({Sell) - Dwa", }); assert.equal(parseStoreHeaderText("Items on Sale"), null); });