Files
NianAIGC/tests/seedream-viewport.test.ts
2026-09-11 15:33:18 +08:00

50 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
import { canvasAnnotationPoint, constrainCanvasView, fitCanvasImage, FIT_CANVAS_VIEW, zoomCanvasAt } from "@/lib/client/seedream-viewport";
import { buildSeedreamInteractivePrompt } from "@/lib/seedream/creation";
describe("Seedream canvas view geometry", () => {
const viewport = { width: 800, height: 500 };
it.each([{ width: 1000, height: 2000 }, { width: 2000, height: 1000 }])("fits a $width × $height image without stretching or inner scrollbars", (image) => {
const fitted = fitCanvasImage(image, viewport);
expect(fitted.width).toBeLessThanOrEqual(780);
expect(fitted.height).toBeLessThanOrEqual(480);
expect(fitted.width / fitted.height).toBeCloseTo(image.width / image.height);
});
it("keeps the cursor over the same image point during zoom", () => {
const fitted = fitCanvasImage({ width: 1000, height: 1000 }, viewport);
const before = { zoom: 2, x: 30, y: -20 };
const anchor = { x: 100, y: 80 };
const after = zoomCanvasAt(before, 3, anchor, fitted, viewport);
expect((anchor.x - after.x) / after.zoom).toBeCloseTo((anchor.x - before.x) / before.zoom);
expect((anchor.y - after.y) / after.zoom).toBeCloseTo((anchor.y - before.y) / before.zoom);
});
it("constrains pan at image edges and restores a centered fit", () => {
const fitted = { width: 240, height: 480 };
expect(constrainCanvasView({ zoom: 3, x: 900, y: -9999 }, fitted, viewport)).toEqual({ zoom: 3, x: 0, y: -470 });
expect(constrainCanvasView(FIT_CANVAS_VIEW, fitted, viewport)).toEqual(FIT_CANVAS_VIEW);
expect(constrainCanvasView({ zoom: 99, x: 0, y: 0 }, fitted, viewport).zoom).toBe(8);
expect(constrainCanvasView({ zoom: 0.01, x: 0, y: 0 }, fitted, viewport).zoom).toBe(0.25);
});
it("maps zoomed and panned box, point and brush coordinates back to the original image", () => {
const normalized = [{ x: 100, y: 200 }, { x: 456, y: 321 }, { x: 700, y: 800 }];
for (const bounds of [
{ left: 100, top: 50, width: 400, height: 600 },
{ left: -720, top: -380, width: 1600, height: 2400 },
{ left: 320, top: 120, width: 800, height: 1200 }
]) {
const points = normalized.map((point) => canvasAnnotationPoint({ x: bounds.left + bounds.width * point.x / 1000, y: bounds.top + bounds.height * point.y / 1000 }, bounds));
expect(points).toEqual(normalized);
expect(buildSeedreamInteractivePrompt("修改 @标注1", [{ id: "point", index: 1, kind: "point", point: points[1]! }])).toBe("修改 图1<point>456 321</point>");
}
});
it("clamps drawing outside the image and ignores an unloaded image", () => {
expect(canvasAnnotationPoint({ x: -1, y: 999 }, { left: 0, top: 0, width: 100, height: 100 })).toEqual({ x: 0, y: 1000 });
expect(canvasAnnotationPoint({ x: 0, y: 0 }, { left: 0, top: 0, width: 0, height: 0 })).toBeNull();
});
});