import { describe, expect, it } from "vitest"; import { formatStructuredText, formatUsage, formatValueMarkdown, summarizeValue, } from "../display.ts"; describe("usage presentation", () => { it("separates cost/output from uncached and cached input without changing usage", () => { const usage = { input: 332086, output: 4283, total: 775665, cacheRead: 439296, cacheWrite: 0, cost: 0.16068544, }; const original = { ...usage }; expect(formatUsage(usage)).toEqual({ summary: "$0.1607 · 4.28k output", detail: "332k uncached input · 439k cache read · 0 cache write", }); expect(usage).toEqual(original); expect(formatUsage({ ...usage, total: 999999 })).toEqual( formatUsage(usage), ); }); it.each([ [0, "$0.0000"], [0.000001, "<$0.0001"], [0.0001, "$0.0001"], [12.34567, "$12.3457"], ])( "formats reported cost %s without making tiny positive charges look free", (cost, expected) => { expect( formatUsage({ input: 0, output: 0, total: 0, cacheRead: 0, cacheWrite: 0, cost, }).summary, ).toBe(`${expected} · 0 output`); }, ); }); describe("structured human views", () => { it.each([ [ { task: "integration", ownership: "ui-tests", files: ["a.ts"] }, "integration", ], [{ title: "Check boundary", detail: "long evidence" }, "Check boundary"], [{ summary: "Read\n source" }, "Read source"], ['{"task":"Encoded task"}', "Encoded task"], [[{ task: "one" }, { task: "two" }], "2 items"], [ { nested: { x: 1 }, files: ["a", "b"], ready: true }, "Nested: 1 field · Files: 2 items · Ready: true", ], [{}, "0 fields"], [[], "0 items"], [null, "None"], [false, "false"], [0, "0"], [undefined, ""], ])("summarizes %j without serializing it", (value, expected) => { expect(summarizeValue(value)).toBe(expected); }); it("bounds long labels and indicates omitted fields", () => { expect(summarizeValue({ title: "x".repeat(200) })).toBe( `${"x".repeat(119)}…`, ); expect(summarizeValue({ a: 1, b: 2, c: 3, d: 4 })).toContain("+1 fields"); }); it("renders nested fields, lists, multiline text, and empty values without dropping data", () => { const value = { answerStatus: "incomplete", findings: [ { file: "src/a.ts", reason: "First line\nSecond line", verified: false, }, ], missing: null, names: ["one", "two"], empty_list: [], empty_object: {}, }; const before = JSON.stringify(value); const markdown = formatValueMarkdown(value); expect(markdown).toContain("- **Answer Status:** incomplete"); expect(markdown).toContain( "- **Findings:**\n - **Item 1**\n - **File:** src/a.ts", ); expect(markdown).toContain("- **Reason:** First line \n Second line"); expect(markdown).toContain("- **Verified:** false"); expect(markdown).toContain("- **Missing:** None"); expect(markdown).toContain("- **Names:**\n - one\n - two"); expect(markdown).toContain("- **Empty list:**\n - No items"); expect(markdown).toContain("- **Empty object:**\n - No fields"); expect(markdown).not.toContain('"answerStatus":'); expect(JSON.stringify(value)).toBe(before); }); it("renders serialized structured values inside fields and lists", () => { expect(formatValueMarkdown({ details: '{"ready":true}' })).toBe( "- **Details:**\n - **Ready:** true", ); expect(formatValueMarkdown(['```json\n{"ready":true}\n```'])).toBe( "- **Item 1**\n - **Ready:** true", ); }); it("keeps field labels literal when they contain markdown syntax", () => { expect(formatValueMarkdown({ "*name*": "value" })).toBe( "- **\\*name\\*:** value", ); }); it.each(['{"ok":true}', '```json\n{"ok":true}\n```'])( "renders complete JSON text %s", (text) => { expect(formatStructuredText(text)).toBe("- **Ok:** true"); expect(formatStructuredText(text, summarizeValue)).toBe("Ok: true"); }, ); it.each([ "ordinary **markdown**", '{"unfinished":', "source example: { not JSON }", ])("preserves prose or incomplete JSON %s", (text) => expect(formatStructuredText(text)).toBe(text), ); });