import { describe, expect, it } from 'vitest' import { estimatedMsgHeight, messageHeightKey, wrappedLines } from '../lib/virtualHeights.js' import type { Msg } from '../types.js' describe('virtual height estimates', () => { it('uses stable content keys across resumed message objects', () => { const msg: Msg = { role: 'assistant', text: 'same text', tools: ['Search Files [long message]'] } expect(messageHeightKey(msg)).toBe(messageHeightKey({ ...msg })) }) it('accounts for wrapping and preserved blank-block rhythm', () => { const msg: Msg = { role: 'assistant', text: `one\n\n${'x'.repeat(90)}` } expect(wrappedLines(msg.text, 30)).toBe(5) expect(estimatedMsgHeight(msg, 35, { compact: false, details: false })).toBeGreaterThan(5) }) it('uses compound user prompt width when estimating user message wrapping', () => { const msg: Msg = { role: 'user', text: 'x'.repeat(21) } expect(estimatedMsgHeight(msg, 26, { compact: false, details: false, userPrompt: '❯' })).toBe(3) expect(estimatedMsgHeight(msg, 26, { compact: false, details: false, userPrompt: 'Ψ >' })).toBe(4) }) it('includes detail sections when visible', () => { const msg: Msg = { role: 'assistant', text: 'ok', thinking: 'line 1\nline 2', tools: ['Tool A', 'Tool B'] } expect(estimatedMsgHeight(msg, 80, { compact: false, details: true })).toBeGreaterThan( estimatedMsgHeight(msg, 80, { compact: false, details: false }) ) }) it('reserves two extra rows for the inter-turn separator on non-first user messages', () => { const msg: Msg = { role: 'user', text: 'follow-up question' } const base = estimatedMsgHeight(msg, 80, { compact: false, details: false }) const withSep = estimatedMsgHeight(msg, 80, { compact: false, details: false, withSeparator: true }) expect(withSep).toBe(base + 2) }) })