import { type TuiTheme, badge, kv, miniBar, panelBlank, panelBottom, panelRow, panelTop, sectionDivider, skillBar, wordWrap, } from "../../../src/extensions/lib/tui"; // Identity theme — returns text unchanged so we can assert on plain strings. const T: TuiTheme = { fg: (_k, s) => s, getFgAnsi: (_k) => "", }; // ─── wordWrap ────────────────────────────────────────────────────────────── describe("wordWrap()", () => { it("returns a single line when text fits", () => { expect(wordWrap("hello world", 20)).toEqual(["hello world"]); }); it("wraps at word boundary when line exceeds width", () => { const lines = wordWrap("one two three four", 10); expect(lines).toEqual(["one two", "three four"]); for (const l of lines) expect(l.length).toBeLessThanOrEqual(10); }); it("does not break a single long word", () => { const lines = wordWrap("superlongwordthatexceedswidth", 10); expect(lines).toEqual(["superlongwordthatexceedswidth"]); }); it("returns empty array for empty string", () => { expect(wordWrap("", 20)).toEqual([]); }); }); // ─── panelTop ───────────────────────────────────────────────────────────── describe("panelTop()", () => { it("starts with ╭ and ends with ╮", () => { const s = panelTop(10, "accent", T); expect(s).toMatch(/^╭/); expect(s).toMatch(/╮$/); }); it("inner width is innerW + 2 dashes", () => { const s = panelTop(10, "accent", T); // ╭ + 12 dashes + ╮ = 14 chars expect(s).toBe("╭" + "─".repeat(12) + "╮"); }); }); // ─── panelBottom ────────────────────────────────────────────────────────── describe("panelBottom()", () => { it("starts with ╰ and ends with ╯", () => { const s = panelBottom(30, "hint", "accent", T); expect(s).toMatch(/^╰/); expect(s).toMatch(/╯$/); }); it("contains the dismiss hint", () => { expect(panelBottom(30, "any key", "accent", T)).toContain("any key"); }); it("total dashes + hint equals innerW + 2", () => { const hint = "dismiss"; const innerW = 20; const s = panelBottom(innerW, hint, "accent", T); // ╰ + hint + fill + ╯ = 1 + hint.len + fill + 1 // fill = max(0, innerW + 2 - hint.len) const fill = Math.max(0, innerW + 2 - hint.length); expect(s).toBe("╰" + hint + "─".repeat(fill) + "╯"); }); }); // ─── panelBlank ─────────────────────────────────────────────────────────── describe("panelBlank()", () => { it("starts and ends with │", () => { const s = panelBlank(10, "accent", T); expect(s).toMatch(/^│/); expect(s).toMatch(/│$/); }); it("has innerW + 2 spaces between walls", () => { const s = panelBlank(8, "accent", T); expect(s).toBe("│" + " ".repeat(10) + "│"); }); }); // ─── panelRow ───────────────────────────────────────────────────────────── describe("panelRow()", () => { it("wraps content between │ walls", () => { const s = panelRow("hello", 5, 10, "accent", T); expect(s).toMatch(/^│/); expect(s).toMatch(/│$/); }); it("pads short content to innerW", () => { const s = panelRow("hi", 2, 10, "accent", T); // │ + space + "hi" + 8 spaces + space + │ expect(s).toBe("│ hi" + " ".repeat(8) + " │"); }); it("does not truncate content that exactly fills innerW", () => { const content = "a".repeat(10); const s = panelRow(content, 10, 10, "accent", T); expect(s).toContain(content); }); }); // ─── badge ──────────────────────────────────────────────────────────────── describe("badge()", () => { it("wraps label in [ ] brackets", () => { expect(badge("HELLO", "accent", T)).toBe("[ HELLO ]"); }); it("visual width is label.length + 4", () => { const label = "TEST"; expect(badge(label, "accent", T).length).toBe(label.length + 4); }); }); // ─── kv ─────────────────────────────────────────────────────────────────── describe("kv()", () => { it("returns text containing label and value", () => { const { text } = kv("Sessions", "5", 12, "accent", T); expect(text).toContain("Sessions"); expect(text).toContain("5"); }); it("label is padded to labelW", () => { const { text } = kv("Hi", "val", 10, "accent", T); expect(text.startsWith("Hi" + " ".repeat(8))).toBe(true); }); it("visualW equals labelW + 2 + value.length", () => { const { visualW } = kv("Label", "42", 10, "accent", T); expect(visualW).toBe(10 + 2 + 2); }); }); // ─── sectionDivider ─────────────────────────────────────────────────────── describe("sectionDivider()", () => { it("visualW equals innerW", () => { expect(sectionDivider("skills", 40, T).visualW).toBe(40); }); it("contains the label", () => { expect(sectionDivider("top skills", 30, T).text).toContain("top skills"); }); }); // ─── miniBar ────────────────────────────────────────────────────────────── describe("miniBar()", () => { it("full bar is all █", () => { expect(miniBar(8, 8, "accent", T)).toBe("█".repeat(8)); }); it("empty bar is all ░", () => { expect(miniBar(0, 8, "accent", T)).toBe("░".repeat(8)); }); it("mixed bar has correct counts", () => { const s = miniBar(3, 8, "accent", T); const filled = (s.match(/█/g) ?? []).length; const empty = (s.match(/░/g) ?? []).length; expect(filled).toBe(3); expect(empty).toBe(5); }); it("clamps filledCount to [0, barW]", () => { expect(miniBar(-1, 5, "accent", T)).toBe("░".repeat(5)); expect(miniBar(10, 5, "accent", T)).toBe("█".repeat(5)); }); }); // ─── skillBar ───────────────────────────────────────────────────────────── describe("skillBar()", () => { it("text contains the skill name prefixed with /", () => { const { text } = skillBar("homework", 4, 4, 10, 8, "accent", T); expect(text).toContain("/homework"); }); it("text contains filled bar chars", () => { const { text } = skillBar("leetcode", 4, 4, 10, 8, "accent", T); expect(text).toContain("█"); }); it("text contains session count", () => { const { text } = skillBar("explain", 3, 4, 10, 8, "accent", T); expect(text).toContain("3 sessions"); }); it("singular 'session' when count is 1", () => { const { text } = skillBar("review", 1, 4, 10, 8, "accent", T); expect(text).toContain("1 session"); expect(text).not.toContain("1 sessions"); }); it("visualW matches nameW + 2 + barW + 2 + countStr.length", () => { const count = 3; const nameW = 12; const barW = 10; const { visualW } = skillBar("skill", count, 5, nameW, barW, "accent", T); const countStr = `${count} sessions`; expect(visualW).toBe(nameW + 2 + barW + 2 + countStr.length); }); });