// Model list table tests cover terminal table rendering for model list output. import { describe, expect, it, vi } from "vitest"; import { visibleWidth } from "../../../packages/terminal-core/src/ansi.js"; import { printModelTable } from "./list.table.js"; import type { ModelRow } from "./list.types.js"; describe("printModelTable", () => { it("prints an empty model list as valid structured JSON", () => { const runtime = { log: vi.fn(), error: vi.fn() }; printModelTable([], runtime as never, { json: true }); expect(runtime.log).toHaveBeenCalledTimes(1); expect(JSON.parse(runtime.log.mock.calls[0]![0] as string)).toEqual({ count: 0, models: [], }); }); it("keeps an empty plain model list silent", () => { const runtime = { log: vi.fn(), error: vi.fn() }; printModelTable([], runtime as never, { plain: true }); expect(runtime.log).not.toHaveBeenCalled(); }); it("writes populated plain model rows directly to stdout", () => { const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), writeStdout: vi.fn(), writeJson: vi.fn(), }; const rows: ModelRow[] = [ { key: "anthropic/claude-sonnet-4-6", name: "Claude Sonnet", input: "text", contextWindow: 200_000, local: false, available: true, tags: [], }, ]; printModelTable(rows, runtime, { plain: true }); expect(runtime.writeStdout).toHaveBeenCalledExactlyOnceWith("anthropic/claude-sonnet-4-6"); expect(runtime.log).not.toHaveBeenCalled(); }); it("prints context caps with sanitized tags in their original order", () => { const runtime = { log: vi.fn(), error: vi.fn() }; const tags = [ "\u001b[31mdefault\u001b[0m", "fallback#2", "img-fallback#1", "alias:a\tb", "unknown", "default", ]; const rows: ModelRow[] = [ { key: "openai/gpt-5.5", name: "GPT-5.5", input: "text+image", contextWindow: 400_000, contextTokens: 272_000, local: false, available: true, tags, }, ]; printModelTable(rows, runtime as never); // Decimal windows render in decimal K: 272000 -> "272k", 400000 -> "400k". expect(runtime.log.mock.calls).toEqual([ ["Model Input Ctx Local Auth Tags"], [ "openai/gpt-5.5 text+image 272k/400k no yes default,fallback#2,img-fallback#1,alias:a\\tb,unknown,default", ], ]); expect(rows[0]?.tags).toEqual([ "\u001b[31mdefault\u001b[0m", "fallback#2", "img-fallback#1", "alias:a\tb", "unknown", "default", ]); }); it("keeps fixed-width rows aligned when model keys contain wide graphemes", () => { const runtime = { log: vi.fn(), error: vi.fn() }; const wideKey = `${"a".repeat(41)}่กจ`; const rows: ModelRow[] = [ { key: wideKey, name: wideKey, input: "text", contextWindow: 128_000, contextTokens: undefined, local: false, available: true, tags: [], }, ]; printModelTable(rows, runtime as never); const [header, row] = runtime.log.mock.calls.map(([line]) => line); expect(typeof header).toBe("string"); expect(typeof row).toBe("string"); const headerInputIndex = (header as string).indexOf("Input"); const rowInputIndex = (row as string).indexOf("text"); expect(headerInputIndex).toBeGreaterThan(0); expect(rowInputIndex).toBeGreaterThan(0); expect(visibleWidth((row as string).slice(0, rowInputIndex))).toBe( visibleWidth((header as string).slice(0, headerInputIndex)), ); }); });