char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
b3a4fb7
main
1import { assertEquals } from "@std/assert" ; 2import { TERMINAL_PALETTE } from "@berry/util/palette.ts" ; 3import { ansiParser , type AnsiRun , ansiRuns } from "./ansi.ts" ; 4 5Deno . test ( "ANSI output preserves literal text, whitespace, and newlines" , () => { 6const text = ' <script>alert("&")</script>\n\tend\n' ; 7assertEquals ( ansiRuns ( text ), [{ text, style :{} }]); 8assertEquals ( ansiRuns ( "" ), []); 9}); 10 11Deno . test ( "ANSI colours and emphasis persist across lines until reset" , () => { 12assertEquals ( ansiRuns ( "before\x1b[1;31mred\nline\x1b[22m normal\x1b[39m after" ), [ 13{ text :"before" , style :{} }, 14{ text :"red\nline" , style :{ color :TERMINAL_PALETTE [ 1 ], "font-weight" :"bold" } }, 15{ text :" normal" , style :{ color :TERMINAL_PALETTE [ 1 ] } }, 16{ text :" after" , style :{} }, 17]); 18assertEquals ( ansiRuns ( "\x1b[2;94;103mdim\x1b[mplain" ), [ 19{ 20text :"dim" , 21style :{ 22color :TERMINAL_PALETTE [ 12 ], 23"background-color" :TERMINAL_PALETTE [ 11 ], 24opacity :"0.5" , 25}, 26}, 27{ text :"plain" , style :{} }, 28]); 29}); 30 31Deno . test ( "ANSI extended colours support the palette, cube, grayscale, and RGB" , () => { 32assertEquals ( 33ansiRuns ( "\x1b[38;5;2mpalette\x1b[38;5;196mcube\x1b[48;5;244mgray\x1b[38;2;12;34;56mrgb" ), 34[ 35{ text :"palette" , style :{ color :TERMINAL_PALETTE [ 2 ] } }, 36{ text :"cube" , style :{ color :"rgb(255, 0, 0)" } }, 37{ 38text :"gray" , 39style :{ color :"rgb(255, 0, 0)" , "background-color" :"rgb(128, 128, 128)" }, 40}, 41{ 42text :"rgb" , 43style :{ color :"rgb(12, 34, 56)" , "background-color" :"rgb(128, 128, 128)" }, 44}, 45], 46); 47}); 48 49Deno . test ( "ANSI resets foreground and background independently and clears decorations" , () => { 50assertEquals ( ansiRuns ( "\x1b[3;4;9;32;41mstyled\x1b[39mbackground\x1b[49;23;24;29mplain" ), [ 51{ 52text :"styled" , 53style :{ 54color :TERMINAL_PALETTE [ 2 ], 55"background-color" :TERMINAL_PALETTE [ 1 ], 56"font-style" :"italic" , 57"text-decoration" :"underline line-through" , 58}, 59}, 60{ 61text :"background" , 62style :{ 63"background-color" :TERMINAL_PALETTE [ 1 ], 64"font-style" :"italic" , 65"text-decoration" :"underline line-through" , 66}, 67}, 68{ text :"plain" , style :{} }, 69]); 70}); 71 72Deno . test ( "ANSI reverse video uses defaults and can be turned off" , () => { 73assertEquals ( ansiRuns ( "\x1b[7mreverse\x1b[27mplain" ), [ 74{ text :"reverse" , style :{ color :"var(--panel)" , "background-color" :"var(--text)" } }, 75{ text :"plain" , style :{} }, 76]); 77}); 78 79Deno . test ( "unsupported terminal controls and OSC links keep their visible text" , () => { 80const text = 81"\x1b[?25l\x1b[2K\x1b[H\x1b]0;title\x07\x1b]8;;https://example.com\x1b\\link\x1b]8;;\x07\x1b[999m end" ; 82assertEquals ( ansiRuns ( text ). map (( run ) => run . text ). join ( "" ), "link end" ); 83}); 84 85Deno . test ( "incomplete streaming escapes stay hidden and render once complete" , () => { 86const partial = "start\x1b[38;2;12;" ; 87assertEquals ( ansiRuns ( partial ), [{ text :"start" , style :{} }]); 88assertEquals ( ansiRuns ( partial + "34;56mend" ), [ 89{ text :"start" , style :{} }, 90{ text :"end" , style :{ color :"rgb(12, 34, 56)" } }, 91]); 92assertEquals ( ansiRuns ( "text\x1b]unfinished title" ), [{ text :"text" , style :{} }]); 93}); 94 95Deno . test ( "malformed extended colours do not consume their components as SGR styles" , () => { 96assertEquals ( ansiRuns ( "\x1b[38;2;999;1;2mtext\x1b[48;5;999mend" ), [ 97{ text :"text" , style :{} }, 98{ text :"end" , style :{} }, 99]); 100}); 101 102Deno . test ( "ANSI streaming preserves styles and hides escapes at every chunk boundary" , () => { 103const sources = [ 104"before\x1b[1;31mred\nline\x1b[22m normal\x1b[39m after" , 105"\x1b[38;2;12;34;56mrgb\x1b[48;5;244mgray\x1b[0mplain" , 106"\x1b]8;;https://example.com\x1b\\link\x1b]8;;\x07 end" , 107"\x1b[?25l\x1b[2K\x1b[H\x1b]0;title\x07text\x1b(B end" , 108"text\x1b[38;2;1;" , 109]; 110const characters = ( runs :AnsiRun []) => 111runs . flatMap (({ text, style}) => [ ...text ]. map (( text ) => ({ text, style}))); 112for ( const source of sources ) { 113const expected = characters ( ansiRuns ( source )); 114for ( let split = 0 ; split <= source . length ; split ++ ) { 115const parser = ansiParser (); 116assertEquals ( 117characters ([ 118 ...parser . write ( source . slice ( 0 , split )), 119 ...parser . write ( source . slice ( split )), 120]), 121expected , 122`split at ${ split } ` , 123); 124} 125const parser = ansiParser (); 126assertEquals ( characters ([ ...source ]. flatMap (( char ) => parser . write ( char ))), expected ); 127} 128}); 129 130Deno . test ( "ANSI streaming discards long control payloads without leaking them or losing later styles" , () => { 131const parser = ansiParser (); 132assertEquals ( parser . write ( "\x1b]0;" ), []); 133for ( let i = 0 ; i < 100 ; i ++ ) assertEquals ( parser . write ( "x" . repeat ( 10000 )), []); 134assertEquals ( parser . write ( "\x1b" ), []); 135assertEquals ( parser . write ( "\\\x1b[32mgreen" ), [ 136{ text :"green" , style :{ color :TERMINAL_PALETTE [ 2 ] } }, 137]); 138assertEquals ( parser . write ( "\x1b[" + "1;" . repeat ( 10000 )), []); 139assertEquals ( parser . write ( "mstill green\x1b[mplain" ), [ 140{ text :"still green" , style :{ color :TERMINAL_PALETTE [ 2 ] } }, 141{ text :"plain" , style :{} }, 142]); 143});