import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createTestRenderer } from '@opentui/core/testing'; import { OpenTuiShell, footerGuides, helpDialogGeometry, palette } from '../src/opentui-shell.ts'; import { HarnessInventoryController } from '../src/harness-inventory.ts'; import type { Settings } from '../src/settings.ts'; const inventoryWith = (harnesses: NonNullable, discover: (signal: AbortSignal) => Promise> = async () => []) => { let settings: Settings = { version: 1, revision: 0, agents: [], providers: [], runtimes: [], harnesses }; return new HarnessInventoryController({ read: () => structuredClone(settings), save: (candidate, expected) => settings = { ...structuredClone(candidate), revision: expected + 1 } }, discover); }; const inventory = () => inventoryWith([{ id: 'fixture', label: 'Fixture Harness', state: 'available', providers: [], reason: 'Synthetic test executable detected', executable: '/fixture/bin/harness' }]); const text = (frame: string) => frame.endsWith('\n') ? frame.slice(0, -1).split('\n') : frame.split('\n'); const waitForEscape = () => new Promise(resolve => setTimeout(resolve, 50)); const cell = (ui: Awaited>, x: number, y: number) => { let offset = 0; for (const span of ui.captureSpans().lines[y]!.spans) { if (x < offset + span.width) return span; offset += span.width; } throw Error(`missing cell ${x},${y}`); }; const rgb = (value: any) => [value.buffer['0'], value.buffer['1'], value.buffer['2']].join(','); test('memory renderer draws the exact wide empty shell and focus perimeter', async () => { const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventory()); try { await ui.renderOnce(); let frame = ui.captureCharFrame(); assert.ok(frame.includes('⬢ BEEHIVE')); assert.ok(frame.includes('HOST AGENTS HARNESSES PROVIDERS')); assert.ok(frame.includes('SIGNED OUT')); assert.ok(frame.includes(footerGuides.wide)); assert.doesNotMatch(frame, /loading|sample|placeholder|sign in to/i); assert.equal(text(frame).length, 40); // Host is protected and therefore owns one literally empty full-width body. assert.equal(text(frame).slice(3, 38).join('').replace(/[│─┌┐└┘ ]/g, ''), ''); ui.mockInput.pressArrow('right'); await ui.renderOnce(); assert.equal(shell.state.headerIndex, 1); assert.equal(shell.state.activeSection, 1); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); frame = ui.captureCharFrame(); assert.equal(shell.state.activeSection, 2); assert.equal(shell.state.focus, 'list'); assert.ok(frame.includes('│')); assert.ok(frame.includes('Fixture Harness')); assert.ok(frame.includes('● READY')); assert.ok(frame.includes('State ● READY')); const spans = JSON.stringify(ui.captureSpans()); assert.ok(spans.includes('"0":255,"1":211,"2":78'), `focus color missing from ${spans.slice(0, 300)}`); } finally { shell.close(); } }); test('memory renderer owns split geometry, flat hierarchy, and focus inset without width shifts', async () => { const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventory()); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); let frame = text(ui.captureCharFrame()); // 35 list cells, exactly one neutral divider, then 84 detail cells (35 + 1 + 84 = 120). assert.equal(frame[3]!.length, 120); assert.equal(frame[3]![35], '│'); assert.equal(frame[3]!.slice(0, 35).length, 35); assert.equal(frame[3]!.slice(36).length, 84); assert.doesNotMatch(frame[3]!, /┐┌/); assert.equal(rgb(cell(ui, 35, 4).fg), '74,81,73'); assert.equal(rgb(cell(ui, 0, 3).fg), '255,211,78'); // focused list's inset perimeter ui.mockInput.pressArrow('right'); await ui.renderOnce(); frame = text(ui.captureCharFrame()); assert.equal(shell.state.focus, 'detail'); assert.equal(shell.state.activeSection, 2, 'pane movement never activates another destination'); assert.equal(frame[3]![35], '│'); assert.equal(frame[3]!.slice(36).length, 84); assert.equal(rgb(cell(ui, 36, 3).fg), '255,211,78'); // right moves the perimeter to Details ui.mockInput.pressArrow('left'); await ui.renderOnce(); assert.equal(shell.state.focus, 'list'); assert.equal(rgb(cell(ui, 0, 3).fg), '255,211,78'); ui.mockInput.pressTab(); await ui.renderOnce(); frame = text(ui.captureCharFrame()); assert.equal(shell.state.focus, 'header', 'Tab skips informational Details that have no action'); ui.mockInput.pressEscape(); await waitForEscape(); await ui.renderOnce(); frame = text(ui.captureCharFrame()); assert.equal(frame[3]![35], '│'); assert.doesNotMatch(frame.slice(3, 38).join('\n'), /[┌┐└┘]/); // unfocused panes remain flat } finally { shell.close(); } }); test('memory renderer keeps keyboard, pointer, resize, and Help return focus visible', async () => { const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventory()); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); await ui.mockMouse.click(60, 10); await ui.renderOnce(); assert.equal(shell.state.focus, 'detail', 'pointer focuses the visible Details pane'); ui.resize(90, 30); await ui.renderOnce(); assert.equal(shell.state.focus, 'detail'); ui.mockInput.pressKey('?'); await ui.renderOnce(); ui.mockInput.pressEscape(); await waitForEscape(); await ui.renderOnce(); assert.deepEqual({ focus: shell.state.focus, help: shell.state.helpOpen }, { focus: 'detail', help: false }); ui.mockInput.pressArrow('left'); await ui.renderOnce(); assert.equal(shell.state.focus, 'list'); await ui.mockMouse.click(10, 10); await ui.renderOnce(); assert.equal(shell.state.focus, 'list', 'pointer returns focus to the visible List pane'); } finally { shell.close(); } }); test('memory renderer layers a full scrim and naturally sized help surface with constrained-body geometry', async () => { const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventory()); try { ui.mockInput.pressKey('?'); await ui.renderOnce(); const geometry = helpDialogGeometry(120, 40); assert.deepEqual(geometry, { width: 62, maxHeight: 36, naturalHeight: 18, height: 18, bodyHeight: 10, bodyWidth: 56, scrollbar: false }); assert.equal(rgb(cell(ui, 0, 0).bg), '7,9,7'); // scrim covers cells outside the dialog assert.equal(rgb(cell(ui, 30, 14).bg), '13,16,14'); // surface is above the scrim assert.equal(rgb(cell(ui, 29, 13).fg), '156,164,155'); // centered dialog boundary // A longer body is constrained to H - 4 while title/actions retain their fixed rows. const overflowing = Array.from({ length: 12 }, (_, row) => `overflow row ${row}`).join('\n'); assert.deepEqual(helpDialogGeometry(50, 20, overflowing), { width: 46, maxHeight: 16, naturalHeight: 20, height: 16, bodyHeight: 8, bodyWidth: 39, scrollbar: true }); assert.equal(helpDialogGeometry(60, 20).height, 16); // added provider controls scroll at the supported minimum } finally { shell.close(); } }); test('memory renderer binds refresh to its command row and applies real async results coherently', async () => { let complete!: (rows: NonNullable) => void; const discovered = new Promise>(resolve => { complete = resolve; }); const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const controller = inventoryWith([], () => discovered); const shell = new OpenTuiShell(ui.renderer, controller); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); assert.ok(ui.captureCharFrame().includes('Saved local harness inventory')); ui.mockInput.pressEnter(); await ui.renderOnce(); assert.equal(shell.state.focus, 'detail', 'the command row opens its owned Details action without executing'); assert.ok(ui.captureCharFrame().includes('Refresh harnesses')); ui.mockInput.pressEnter(); await ui.renderOnce(); assert.ok(ui.captureCharFrame().includes('Refreshing local harness inventory')); complete([{ id: 'codex', label: 'Codex', state: 'incompatible', providers: [], reason: 'Codex ACP 1.10.0 or newer required; version unknown/outdated' }]); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce(); const frame = ui.captureCharFrame(); assert.ok(frame.includes('Codex')); assert.ok(frame.includes('INCOMPATIBLE')); assert.ok(frame.includes('Refresh harnesses')); assert.equal(shell.state.harnessSelection, 'command:refresh', 'surviving command identity and its completed operation Details remain selected'); } finally { shell.close(); } }); test('renderer shows refresh failure, permits recovery, and fences completion after leaving Harnesses', async () => { let attempts = 0; let lateResolve!: (rows: NonNullable) => void; const late = new Promise>(resolve => { lateResolve = resolve; }); const controller = inventoryWith([], async () => { attempts++; if (attempts === 1) throw Error('/private/diagnostic'); if (attempts === 2) return [{ id: 'buzz-agent', label: 'Buzz Agent', state: 'available', providers: ['openai'], reason: 'Detected; provider/model access unverified', executable: '/fixture/buzz-agent' }]; return late; }); const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, controller); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); ui.mockInput.pressEnter(); ui.mockInput.pressEnter(); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce(); assert.ok(ui.captureCharFrame().includes('Harness refresh failed')); assert.doesNotMatch(ui.captureCharFrame(), /private\/diagnostic/); ui.mockInput.pressEnter(); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce(); assert.ok(ui.captureCharFrame().includes('Buzz Agent')); assert.ok(ui.captureCharFrame().includes('● READY')); ui.mockInput.pressArrow('down'); ui.mockInput.pressEnter(); ui.mockInput.pressEnter(); await ui.renderOnce(); ui.mockInput.pressArrow('up'); ui.mockInput.pressArrow('up'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); assert.equal(shell.state.activeSection, 3); lateResolve([{ id: 'late', label: 'Late', state: 'available', providers: [], reason: 'late', executable: '/fixture/late' }]); await new Promise(resolve => setTimeout(resolve, 0)); assert.equal(controller.snapshot().harnesses.some(row => row.id === 'late'), false); } finally { shell.close(); } }); test('pointer selection does not execute a command and its Details action dispatches once', async () => { let attempts = 0; const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventoryWith([], async () => { attempts++; return []; })); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); await ui.mockMouse.click(3, 6); await ui.renderOnce(); assert.equal(attempts, 0, 'a List click only selects the command row'); ui.mockInput.pressEnter(); await ui.renderOnce(); await ui.mockMouse.click(40, 15); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce(); assert.equal(attempts, 1, 'mouse bubbling cannot double-dispatch the Details action'); } finally { shell.close(); } }); test('virtual list click mapping targets the currently visible row after overflow scroll', async () => { const harnesses = Array.from({ length: 20 }, (_, index) => ({ id: `fixture-${index}`, label: `Harness ${index}`, state: 'available' as const, providers: [], reason: 'Synthetic test executable detected', executable: `/fixture/${index}` })); const ui = await createTestRenderer({ width: 60, height: 20, exitOnCtrlC: false }); const shell = new OpenTuiShell(ui.renderer, inventoryWith(harnesses)); try { ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); for (let index = 0; index < 14; index++) ui.mockInput.pressArrow('down'); await ui.renderOnce(); assert.ok(shell.state.listOffset > 0); const expected = shell.state.harnessRows[shell.state.listOffset]!.id; await ui.mockMouse.click(3, 6); await ui.renderOnce(); assert.equal(shell.state.harnessSelection, expected, 'the slot maps through the current virtual offset, never a stale absolute index'); } finally { shell.close(); } });