import { describe, expect, it } from 'vitest' import { renderUnknownXml } from '../src/components/xml-tool-output.ts' const render = (source: string, limit = 4, expanded = false): string[] | undefined => renderUnknownXml( source, limit, expanded, text => text.replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/gu, control => `\\x${control.charCodeAt(0).toString(16).padStart(2, '0')}`), text => `[label]${text}[/label]`, text => `[body]${text}[/body]`, count => ` … +${count} lines`, ) describe('unknown-tool XML rendering', () => { it('renders nested elements and attributes as an indented tree', () => { expect(render(` /tmp/a.txt file hello world `)).toEqual([ '[label]result[/label]', ' [label]path:[/label] [body]/tmp/a.txt[/body]', ' [label]type:[/label] [body]file[/body]', ' [label]content[/label]', ' [label]line (number="1"):[/label] [body]hello[/body]', ' [label]line (number="2"):[/label] [body]world[/body]', ]) }) it('renders root text, CDATA, empty elements, and multiline nested text', () => { expect(render(' \nfirst\nsecond\n ')).toEqual([ '[label]result[/label]', ' [body]first[/body]', ' [body]second[/body]', ]) expect(render('\nfirst\nsecond\n', 1, true)).toEqual([ '[label]result[/label]', ' [body]first[/body]', ' [body]second[/body]', ]) expect(render(']]>')).toEqual([ '[label]result[/label]', ' [label]value:[/label] [body]literal [/body]', ' [label]empty[/label]', ]) // An interior blank line stays the empty string: styling it would emit an // escape-only row, which reads as a stray indented blank rather than a gap. expect(render('\nfirst\n\nsecond\n', 4, true)).toEqual([ '[label]result[/label]', ' [body]first[/body]', '', ' [body]second[/body]', ]) }) it('previews each top-level child independently and expands all rows', () => { const xml = '\na\nb\nc\nd\ne\nf\n\ng\nh\ni\nj\nk\nl\n' expect(render(xml, 3)).toEqual([ '[label]result[/label]', ' [label]first[/label]', ' [body]a[/body]', ' … +4 lines', ' [body]f[/body]', ' [label]second[/label]', ' [body]g[/body]', ' … +4 lines', ' [body]l[/body]', ]) expect(render(xml, 3, true)).toHaveLength(15) }) it('bounds the collapsed child count and counts the hidden lines', () => { const xml = `${Array.from({ length: 8 }, (_, index) => `${index}`).join('')}` expect(render(xml, 3)).toEqual([ '[label]result[/label]', ' [label]item:[/label] [body]0[/body]', ' [label]item:[/label] [body]1[/body]', ' … +5 lines', ' [label]item:[/label] [body]7[/body]', ]) expect(render(xml, 3, true)).toHaveLength(9) }) it('escapes control characters expanded from character references', () => { expect(render('tab csi›')).toEqual([ '[label]result (attr="a\\\\x9bb")[/label]', ' [body]tab\\x09csi\\x9b[/body]', ]) expect(render('')).toEqual([ '[label]result[/label]', ' [label]value:[/label] [body]del\\x7f[/body]', ]) }) it.each([ 'missing close', '', ' ', 'prefix /tmp/a', '/tmp/a suffix', '', '', '', '', '', ' \n ', ])('declines malformed or mixed text: %s', (source) => { expect(render(source)).toBeUndefined() }) })