import { describe, expect, it } from "vitest"; import { detectColumns, detectRows, newRowPosition, rowEndPosition, type KeyRect } from "../src/domain/rows"; const key = (id: string, x: number, y: number, w = 1, h = 1): KeyRect => ({ id, x, y, w, h }); describe("row detection", () => { it("groups row-staggered keys by y and traces columns", () => { const keys = [ key("tab", 0, 0, 1.5), key("q", 1.5, 0), key("w", 2.5, 0), key("caps", 0, 1, 1.75), key("a", 1.75, 1), key("s", 2.75, 1), key("space", 0.5, 2, 3), ]; expect(detectRows(keys)).toEqual([["tab", "q", "w"], ["caps", "a", "s"], ["space"]]); expect(detectColumns(keys)[0]).toEqual(["tab", "caps", "space"]); }); it("traces column-staggered rows through the nearest key of the next column", () => { const offsets = [0.5, 0.25, 0, 0.25]; const keys = offsets.flatMap((dy, col) => [0, 1, 2].map((row) => key(`c${col}r${row}`, col, row + dy))); expect(detectRows(keys)).toEqual([0, 1, 2].map((row) => offsets.map((_, col) => `c${col}r${row}`))); expect(detectColumns(keys)[2]).toEqual(["c2r0", "c2r1", "c2r2"]); }); it("keeps rows apart when an outer column has fewer keys", () => { const keys = [ key("p1", 0, 1.5), key("p2", 0, 2.5), ...[0, 1, 2].map((row) => key(`r${row}`, 1, row + 1.25)), ...[0, 1, 2, 3].map((row) => key(`i${row}`, 2, row + 1)), ]; expect(detectRows(keys)).toEqual([["p1", "r0", "i0"], ["p2", "r1", "i1"], ["r2", "i2"], ["i3"]]); }); it("does not link keys a full row apart", () => { const keys = [key("a0", 0, 0), key("a1", 0, 1), key("b1", 1, 1.1), key("b2", 1, 2.1)]; expect(detectRows(keys)).toEqual([["a0"], ["a1", "b1"], ["b2"]]); }); it("puts a tall key in its top row", () => { const keys = [key("7", 0, 0), key("8", 1, 0), key("plus", 2, 0, 1, 2), key("4", 0, 1), key("5", 1, 1)]; expect(detectRows(keys)).toEqual([ ["7", "8", "plus"], ["4", "5"], ]); }); it("links the inner columns across the gap between halves", () => { const columns: [string, number, number][] = [ ["l", 0, 0.5], ["m", 1, 0.25], ["n", 4, 0.25], ["o", 5, 0.5], ]; const keys = columns.flatMap(([name, x, dy]) => [0, 1, 2].map((row) => key(`${name}${row}`, x, row + dy)), ); expect(detectRows(keys)).toEqual([0, 1, 2].map((row) => columns.map(([name]) => `${name}${row}`))); }); }); describe("key insertion position", () => { const keys = [key("a", 0, 0), key("b", 1, 0, 2), key("c", 0, 1)]; it("places a new key after the right end of the selected key's row", () => { expect(rowEndPosition(keys, "a")).toEqual({ x: 3, y: 0 }); expect(rowEndPosition(keys, "c")).toEqual({ x: 1, y: 1 }); expect(rowEndPosition(keys, "missing")).toBeUndefined(); }); it("starts a new row at the left below every key", () => { expect(newRowPosition([...keys, key("tall", 3, 0.5, 1, 2)])).toEqual({ x: 0, y: 2.5 }); expect(newRowPosition([])).toBeUndefined(); }); });