eCourt-note/tests/utils/coordinates.test.ts
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 eddcec28c7 Add application codebase: PDF annotation for e-Court
Next.js 14 + Drizzle/PostgreSQL + SSE realtime sync — highlight,
freehand, comment, bookmark + ฟีเจอร์ต่อยอด F1–F19 (edit/move, undo/redo,
shapes, redaction, export PDF/CSV, reply, resolve, search, summary,
textbox, stamp, version history)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 14:29:40 +07:00

145 lines
4.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
clamp,
getPointerPercent,
mergeRectsByLine,
pathToPercent,
pathToPx,
pointToPercent,
pointToPx,
rectToPercent,
rectToPx,
toPercent,
toPx,
type Size,
} from "@/utils/coordinates";
const size: Size = { width: 800, height: 600 };
describe("toPercent / toPx", () => {
it("px → % ตามสัดส่วนขนาดที่ส่งเข้ามา", () => {
expect(toPercent(400, 800)).toBe(50);
expect(toPercent(300, 600)).toBe(50);
expect(toPercent(0, 800)).toBe(0);
});
it("roundtrip px → % → px ได้ค่าเดิม", () => {
expect(toPx(toPercent(123, 800), 800)).toBeCloseTo(123);
});
});
describe("clamp", () => {
it("ต่ำกว่า min → min, อยู่ในช่วง → ค่าเดิม, เกิน max → max", () => {
expect(clamp(-5, 0, 100)).toBe(0);
expect(clamp(42, 0, 100)).toBe(42);
expect(clamp(120, 0, 100)).toBe(100);
});
});
describe("point/rect/path converters", () => {
it("point: x อิง width, y อิง height (แกนแยกกัน)", () => {
expect(pointToPercent({ x: 400, y: 300 }, size)).toEqual({ x: 50, y: 50 });
expect(pointToPx({ x: 25, y: 75 }, size)).toEqual({ x: 200, y: 450 });
});
it("rect: แปลงครบทั้ง 4 ค่า", () => {
expect(rectToPercent({ x: 80, y: 60, width: 160, height: 120 }, size)).toEqual({
x: 10,
y: 10,
width: 20,
height: 20,
});
expect(rectToPx({ x: 10, y: 10, width: 20, height: 20 }, size)).toEqual({
x: 80,
y: 60,
width: 160,
height: 120,
});
});
it("path: แปลงทุกจุดในลิสต์", () => {
expect(
pathToPercent(
[
{ x: 0, y: 0 },
{ x: 800, y: 600 },
],
size,
),
).toEqual([
{ x: 0, y: 0 },
{ x: 100, y: 100 },
]);
expect(pathToPx([{ x: 50, y: 50 }], size)).toEqual([{ x: 400, y: 300 }]);
});
});
describe("getPointerPercent", () => {
const makeEl = (left: number, top: number, width: number, height: number) =>
({ getBoundingClientRect: () => ({ left, top, width, height }) }) as HTMLElement;
it("คิดจากขอบ element ไม่ใช่ขอบจอ", () => {
const el = makeEl(100, 50, 200, 100);
expect(getPointerPercent({ clientX: 200, clientY: 100 }, el)).toEqual({ x: 50, y: 50 });
});
it("เกินขอบ element → clamp 100 / ต่ำกว่าขอบ → clamp 0", () => {
const el = makeEl(100, 50, 200, 100);
expect(getPointerPercent({ clientX: 999, clientY: 999 }, el)).toEqual({ x: 100, y: 100 });
expect(getPointerPercent({ clientX: 0, clientY: 0 }, el)).toEqual({ x: 0, y: 0 });
});
});
describe("mergeRectsByLine", () => {
it("rects บรรทัดเดียวกัน (y overlap) → รวมเป็น 1 rect ครอบกว้าง/สูงสุด", () => {
const out = mergeRectsByLine([
{ x: 10, y: 20, width: 30, height: 5 },
{ x: 45, y: 21, width: 20, height: 5 },
]);
expect(out).toHaveLength(1);
expect(out[0]).toEqual({ x: 10, y: 20, width: 55, height: 6 });
});
it("rects คนละบรรทัด (y ไม่ overlap) → คงแยกกัน", () => {
const out = mergeRectsByLine([
{ x: 10, y: 20, width: 30, height: 5 },
{ x: 12, y: 30, width: 30, height: 5 },
]);
expect(out).toHaveLength(2);
});
it("ลำดับ input ไม่สำคัญ — rect ซ้ายมาทีหลังก็รวมถูกจุดเริ่ม", () => {
const out = mergeRectsByLine([
{ x: 45, y: 21, width: 20, height: 5 },
{ x: 10, y: 20, width: 30, height: 5 },
]);
expect(out).toHaveLength(1);
expect(out[0]).toMatchObject({ x: 10, width: 55 });
});
it("ช่องว่างระหว่างคำในบรรทัดเดียวกันถูกครอบด้วย (union แกน x)", () => {
const out = mergeRectsByLine([
{ x: 10, y: 20, width: 10, height: 5 },
{ x: 80, y: 20, width: 10, height: 5 },
]);
expect(out).toHaveLength(1);
expect(out[0].width).toBe(80); // 10 → 90
});
it("array ว่าง → ว่าง / rect เดี่ยว → ค่าเดิม", () => {
expect(mergeRectsByLine([])).toEqual([]);
expect(mergeRectsByLine([{ x: 1, y: 2, width: 3, height: 4 }])).toEqual([
{ x: 1, y: 2, width: 3, height: 4 },
]);
});
it("ไม่แก้ array และ rect ต้นฉบับ", () => {
const input = [
{ x: 45, y: 21, width: 20, height: 5 },
{ x: 10, y: 20, width: 30, height: 5 },
];
const snapshot = input.map((r) => ({ ...r }));
mergeRectsByLine(input);
expect(input).toEqual(snapshot);
});
});