From e03b0ce058d6c80ccb37364f9abb8d19ee85ff9a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 19:06:11 +0100 Subject: [PATCH] test(tui): cover description layout boundaries --- .../components/searchable-select-list.test.ts | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/tui/components/searchable-select-list.test.ts b/src/tui/components/searchable-select-list.test.ts index b618036743f..35bada1f09a 100644 --- a/src/tui/components/searchable-select-list.test.ts +++ b/src/tui/components/searchable-select-list.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { visibleWidth } from "../../terminal/ansi.js"; import { SearchableSelectList, type SearchableSelectListTheme } from "./searchable-select-list.js"; const mockTheme: SearchableSelectListTheme = { @@ -48,6 +49,60 @@ describe("SearchableSelectList", () => { expect(output).toContain(tail); }); + it("does not show description layout at width 40 (boundary)", () => { + const items = [ + { value: "one", label: "one", description: "desc" }, + { value: "two", label: "two", description: "desc" }, + ]; + const list = new SearchableSelectList(items, 5, mockTheme); + list.setSelectedIndex(1); // ensure first row is not selected so description styling is applied + + const output = list.render(40).join("\n"); + expect(output).not.toContain("(desc)"); + }); + + it("shows description layout at width 41 (boundary)", () => { + const items = [ + { value: "one", label: "one", description: "desc" }, + { value: "two", label: "two", description: "desc" }, + ]; + const list = new SearchableSelectList(items, 5, mockTheme); + list.setSelectedIndex(1); // ensure first row is not selected so description styling is applied + + const output = list.render(41).join("\n"); + expect(output).toContain("(desc)"); + }); + + it("keeps ANSI-highlighted description rows within terminal width", () => { + const ansiTheme: SearchableSelectListTheme = { + selectedPrefix: (t) => t, + selectedText: (t) => t, + description: (t) => t, + scrollInfo: (t) => t, + noMatch: (t) => t, + searchPrompt: (t) => t, + searchInput: (t) => t, + matchHighlight: (t) => `\u001b[31m${t}\u001b[0m`, + }; + const label = `provider/${"x".repeat(80)}`; + const items = [ + { value: label, label, description: "Some description text that should not overflow" }, + { value: "other", label: "other", description: "Other description" }, + ]; + const list = new SearchableSelectList(items, 5, ansiTheme); + list.setSelectedIndex(1); // make first row non-selected so description styling is applied + + for (const ch of "provider") { + list.handleInput(ch); + } + + const width = 80; + const output = list.render(width); + for (const line of output) { + expect(visibleWidth(line)).toBeLessThanOrEqual(width); + } + }); + it("filters items when typing", () => { const list = new SearchableSelectList(testItems, 5, mockTheme);