97 lines
3.7 KiB
TypeScript
97 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { PNG } from "pngjs";
|
|
import { clickPoint, clampRegion, cropPng, parseRegion } from "../../src/capabilities/vision";
|
|
|
|
function makeImage(width = 100, height = 80): PNG {
|
|
const png = new PNG({ width, height });
|
|
for (let i = 0; i < png.data.length; i += 4) {
|
|
png.data[i] = 200;
|
|
png.data[i + 1] = 100;
|
|
png.data[i + 2] = 50;
|
|
png.data[i + 3] = 255;
|
|
}
|
|
return png;
|
|
}
|
|
|
|
describe("parseRegion", () => {
|
|
it("accepts object x/y/width/height", () => {
|
|
const image = makeImage();
|
|
expect(parseRegion('{"x":10,"y":20,"width":30,"height":40}', image)).toEqual({ x: 10, y: 20, width: 30, height: 40 });
|
|
});
|
|
|
|
it("accepts array [x,y,width,height]", () => {
|
|
const image = makeImage();
|
|
expect(parseRegion("[1,2,3,4]", image)).toEqual({ x: 1, y: 2, width: 3, height: 4 });
|
|
});
|
|
|
|
it("accepts left/top aliases and corner points", () => {
|
|
const image = makeImage();
|
|
expect(parseRegion('{"left":5,"top":6,"width":7,"height":8}', image)).toEqual({ x: 5, y: 6, width: 7, height: 8 });
|
|
expect(parseRegion('{"x":5,"y":6,"x2":15,"y2":16}', image)).toEqual({ x: 5, y: 6, width: 10, height: 10 });
|
|
});
|
|
|
|
it("rejects malformed JSON and unknown shapes", () => {
|
|
const image = makeImage();
|
|
expect(() => parseRegion("not-json", image)).toThrow(/无法解析/);
|
|
expect(() => parseRegion('{"foo":1}', image)).toThrow(/不符合要求/);
|
|
});
|
|
});
|
|
|
|
describe("clampRegion", () => {
|
|
it("clamps region into image bounds", () => {
|
|
const image = makeImage(100, 80);
|
|
expect(clampRegion({ x: -5, y: -5, width: 200, height: 200 }, image)).toEqual({ x: 0, y: 0, width: 100, height: 80 });
|
|
expect(clampRegion({ x: 90, y: 70, width: 50, height: 50 }, image)).toEqual({ x: 90, y: 70, width: 10, height: 10 });
|
|
});
|
|
});
|
|
|
|
describe("cropPng", () => {
|
|
it("crops sub-image with expected size and pixel origin", () => {
|
|
const image = makeImage();
|
|
const cropped = cropPng(image, { x: 10, y: 20, width: 30, height: 40 });
|
|
expect(cropped.width).toBe(30);
|
|
expect(cropped.height).toBe(40);
|
|
const index = (0 * 30 + 0) * 4;
|
|
expect([cropped.data[index], cropped.data[index + 1], cropped.data[index + 2]]).toEqual([200, 100, 50]);
|
|
});
|
|
});
|
|
|
|
describe("clickPoint", () => {
|
|
const region = { x: 100, y: 200, width: 60, height: 40, score: 1 };
|
|
|
|
it("defaults to center", () => {
|
|
expect(clickPoint(region, "")).toEqual({ x: 130, y: 220 });
|
|
expect(clickPoint(region, "center")).toEqual({ x: 130, y: 220 });
|
|
});
|
|
|
|
it("supports nine-grid anchors", () => {
|
|
expect(clickPoint(region, "leftTop")).toEqual({ x: 100, y: 200 });
|
|
expect(clickPoint(region, "rightBottom")).toEqual({ x: 160, y: 240 });
|
|
expect(clickPoint(region, "top")).toEqual({ x: 130, y: 200 });
|
|
expect(clickPoint(region, "left")).toEqual({ x: 100, y: 220 });
|
|
});
|
|
|
|
it("supports custom offset JSON", () => {
|
|
expect(clickPoint(region, '{"x":5,"y":6}')).toEqual({ x: 105, y: 206 });
|
|
});
|
|
|
|
it("falls back to center for unknown values", () => {
|
|
expect(clickPoint(region, "bogus")).toEqual({ x: 130, y: 220 });
|
|
});
|
|
});
|
|
|
|
describe("legacy position aliases", () => {
|
|
it("accepts kebab-case anchors saved by older catalogs", () => {
|
|
const region = { x: 100, y: 200, width: 60, height: 40, score: 1 };
|
|
expect(clickPoint(region, "left-top")).toEqual({ x: 100, y: 200 });
|
|
expect(clickPoint(region, "right-top")).toEqual({ x: 160, y: 200 });
|
|
expect(clickPoint(region, "left-bottom")).toEqual({ x: 100, y: 240 });
|
|
expect(clickPoint(region, "right-bottom")).toEqual({ x: 160, y: 240 });
|
|
});
|
|
|
|
it("still falls back to center for unknown anchors", () => {
|
|
const region = { x: 100, y: 200, width: 60, height: 40, score: 1 };
|
|
expect(clickPoint(region, "middle")).toEqual({ x: 130, y: 220 });
|
|
});
|
|
});
|