import { describe, expect, it } from "vitest"; import { step } from "../src/lib/grid"; import type { Rect } from "../src/lib/grid"; /** Three cards per row, 100 wide and 40 tall, the way the tray wraps them. */ function tray(count: number): { id: number; rect: Rect }[] { return Array.from({ length: count }, (_, index) => ({ id: index, rect: { left: (index % 3) * 100, top: Math.floor(index / 3) * 40, width: 96, height: 36, }, })); } const rectOf = (item: { rect: Rect }): Rect => item.rect; const walk = ( items: ReturnType, from: number, direction: Parameters[2], ): number | null => step(items, items[from], direction, rectOf)?.id ?? null; describe("step", () => { it("moves along a row", () => { const items = tray(6); expect(walk(items, 0, "right")).toBe(1); expect(walk(items, 1, "left")).toBe(0); }); it("moves down a column, not just to the next card", () => { const items = tray(6); expect(walk(items, 1, "down")).toBe(4); expect(walk(items, 4, "up")).toBe(1); }); it("keeps the column when the last row is short", () => { const items = tray(5); expect(walk(items, 1, "down")).toBe(4); expect(walk(items, 2, "down")).toBe(4); }); it("carries on to the next row at the end of one", () => { const items = tray(6); expect(walk(items, 2, "right")).toBe(3); expect(walk(items, 3, "left")).toBe(2); }); it("stops at the top and the bottom", () => { const items = tray(6); expect(walk(items, 1, "up")).toBeNull(); expect(walk(items, 4, "down")).toBeNull(); }); it("stops at the very ends of the run", () => { const items = tray(6); expect(walk(items, 0, "left")).toBeNull(); expect(walk(items, 5, "right")).toBeNull(); }); it("falls back to the first card when the cursor is off the run", () => { const items = tray(3); const stray = { id: 99, rect: { left: 0, top: 0, width: 1, height: 1 } }; expect(step(items, stray, "right", rectOf)?.id).toBe(0); }); it("reads a row by the band each card fills, not by its centre", () => { // Top-aligned cards of different heights still sit on one row. const items = [ { id: 0, rect: { left: 0, top: 0, width: 96, height: 67 } }, { id: 1, rect: { left: 100, top: 0, width: 96, height: 67 } }, { id: 2, rect: { left: 200, top: 0, width: 96, height: 51 } }, { id: 3, rect: { left: 0, top: 73, width: 96, height: 51 } }, ]; expect(step(items, items[0], "up", rectOf)).toBeNull(); expect(step(items, items[0], "right", rectOf)?.id).toBe(1); expect(step(items, items[2], "left", rectOf)?.id).toBe(1); expect(step(items, items[0], "down", rectOf)?.id).toBe(3); expect(step(items, items[3], "up", rectOf)?.id).toBe(0); }); it("handles a single card without moving", () => { const items = tray(1); expect(walk(items, 0, "right")).toBeNull(); expect(walk(items, 0, "down")).toBeNull(); }); });