char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
bdffafa5d
char/aliceai-t5-moe
1// Guards the editing-key contract of the ChatFormInputRich: 2// undo/redo is replayed from source snapshots (the token rebuilds destroy 3// the native undo stack), and Tab is NOT intercepted (WCAG 2.1.2 no 4// keyboard trap), matching the plain textarea. 5 6import ChatFormInputRichHarness from './components/ChatFormInputRichHarness.svelte' ; 7import { tick } from 'svelte' ; 8import { describe , expect , it } from 'vitest' ; 9import { render } from 'vitest-browser-svelte' ; 10 11const SOURCE = 'see [docs](file:///a/b)' ; 12 13function editableIn ( container :HTMLElement ) :HTMLElement { 14const el = container . querySelector ( '[role="textbox"]' ); 15 16if ( ! ( el instanceof HTMLElement )) throw new Error ( 'ChatFormInputRich not rendered' ); 17 18return el ; 19} 20 21function type ( root :HTMLElement , text :string , inputType = 'insertText' ) { 22root . appendChild ( document . createTextNode ( text )); 23root . dispatchEvent ( new InputEvent ( 'input' , { bubbles :true , inputType})); 24} 25 26function keydown ( root :HTMLElement , init :KeyboardEventInit ) { 27const event = new KeyboardEvent ( 'keydown' , { bubbles :true , cancelable :true , ...init }); 28 29root . dispatchEvent ( event ); 30 31return event ; 32} 33 34describe ( 'ChatFormInputRich undo/redo' , () => { 35it ( 'undoes and redoes an edit across a badge-containing buffer' , async () => { 36const screen = render ( ChatFormInputRichHarness , { value :SOURCE }); 37 38await tick (); 39 40const root = editableIn ( screen . container ); 41 42type ( root , ' more' ); 43await tick (); 44expect ( screen . component . getValue ()). toBe ( ` ${ SOURCE } more` ); 45 46const undoEvent = keydown ( root , { ctrlKey :true , key :'z' }); 47 48await tick (); 49expect ( undoEvent . defaultPrevented ). toBe ( true ); 50expect ( screen . component . getValue ()). toBe ( SOURCE ); 51 52const redoEvent = keydown ( root , { ctrlKey :true , key :'z' , shiftKey :true }); 53 54await tick (); 55expect ( redoEvent . defaultPrevented ). toBe ( true ); 56expect ( screen . component . getValue ()). toBe ( ` ${ SOURCE } more` ); 57}); 58 59it ( 'redoes with Ctrl+Y as well' , async () => { 60const screen = render ( ChatFormInputRichHarness , { value :SOURCE }); 61 62await tick (); 63 64const root = editableIn ( screen . container ); 65 66type ( root , ' more' ); 67await tick (); 68keydown ( root , { key :'z' , metaKey :true }); 69await tick (); 70expect ( screen . component . getValue ()). toBe ( SOURCE ); 71 72keydown ( root , { ctrlKey :true , key :'y' }); 73await tick (); 74expect ( screen . component . getValue ()). toBe ( ` ${ SOURCE } more` ); 75}); 76 77it ( 'coalesces a typing burst into one undo step' , async () => { 78const screen = render ( ChatFormInputRichHarness , { value :'abc' }); 79 80await tick (); 81 82const root = editableIn ( screen . container ); 83 84type ( root , 'd' ); 85type ( root , 'e' ); 86await tick (); 87expect ( screen . component . getValue ()). toBe ( 'abcde' ); 88 89keydown ( root , { ctrlKey :true , key :'z' }); 90await tick (); 91expect ( screen . component . getValue ()). toBe ( 'abc' ); 92}); 93 94it ( 'keeps a newline as its own undo step' , async () => { 95const screen = render ( ChatFormInputRichHarness , { value :'abc' }); 96 97await tick (); 98 99const root = editableIn ( screen . container ); 100 101type ( root , 'd' ); 102type ( root , '\n' , 'insertLineBreak' ); 103await tick (); 104expect ( screen . component . getValue ()). toBe ( 'abcd\n' ); 105 106keydown ( root , { ctrlKey :true , key :'z' }); 107await tick (); 108expect ( screen . component . getValue ()). toBe ( 'abcd' ); 109 110keydown ( root , { ctrlKey :true , key :'z' }); 111await tick (); 112expect ( screen . component . getValue ()). toBe ( 'abc' ); 113}); 114 115it ( 'is a no-op when there is nothing to undo' , async () => { 116const screen = render ( ChatFormInputRichHarness , { value :'abc' }); 117 118await tick (); 119 120const root = editableIn ( screen . container ); 121const event = keydown ( root , { ctrlKey :true , key :'z' }); 122 123await tick (); 124 125expect ( event . defaultPrevented ). toBe ( true ); 126expect ( screen . component . getValue ()). toBe ( 'abc' ); 127}); 128 129it ( 'abandons the redo branch after a fresh edit' , async () => { 130const screen = render ( ChatFormInputRichHarness , { value :'abc' }); 131 132await tick (); 133 134const root = editableIn ( screen . container ); 135 136type ( root , 'd' ); 137await tick (); 138keydown ( root , { ctrlKey :true , key :'z' }); 139await tick (); 140expect ( screen . component . getValue ()). toBe ( 'abc' ); 141 142type ( root , 'e' ); 143await tick (); 144keydown ( root , { ctrlKey :true , key :'z' , shiftKey :true }); 145await tick (); 146expect ( screen . component . getValue ()). toBe ( 'abce' ); 147}); 148}); 149 150describe ( 'ChatFormInputRich Tab key' , () => { 151it ( 'does not trap Tab (focus can leave the editable)' , async () => { 152const screen = render ( ChatFormInputRichHarness , { value :SOURCE }); 153 154await tick (); 155 156const root = editableIn ( screen . container ); 157const event = keydown ( root , { key :'Tab' }); 158 159expect ( event . defaultPrevented ). toBe ( false ); 160}); 161});