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()
})
})