using TinyTUI.Input; namespace TinyTUI.Tests.Input; public sealed class InputInfrastructureTests { [Fact] public void StdinBufferSplitsBatchedEscapeSequencesAndPaste() { var buffer = new StdinBuffer(); Assert.Empty(buffer.Process("\e[")); Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Data, "\e[A")], buffer.Process("A")); Assert.Equal( [ new StdinBufferEvent(StdinBufferEventKind.Data, "\e[97u"), new StdinBufferEvent(StdinBufferEventKind.Data, "\e[97;1:3u"), ], buffer.Process("\e[97u\e[97;1:3u")); Assert.Empty(buffer.Process("\e[200~hello ")); Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Paste, "hello world")], buffer.Process("world\e[201~")); } [Fact] public void StdinBufferFlushesIncompleteSequences() { var buffer = new StdinBuffer(); Assert.Empty(buffer.Process("\e[<35")); Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Data, "\e[<35")], buffer.Flush()); } [Fact] public void DefaultInputParserParsesCommonKeysAndPaste() { var parser = new DefaultInputParser(); Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Up)], parser.Parse("\e[A")); Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Up))], parser.Parse("\e[1;2A")); Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("c"))], parser.Parse("\x03")); Assert.Equal([new TuiInputEvent(TuiInputEventKind.Text, "abc")], parser.Parse("abc")); Assert.Equal([new TuiInputEvent(TuiInputEventKind.Paste, "pasted")], parser.Parse("\e[200~pasted\e[201~")); } [Fact] public void KeybindingRegistryReportsUserConflictsAndNormalizesAliases() { var registry = KeybindingRegistry.CreateDefault(); registry.SetUserBindings(new Dictionary> { [TuiKeybindings.InputSubmit] = [KeyNames.PageUp], [TuiKeybindings.InputCancel] = ["pageUp"], }); var conflict = Assert.Single(registry.Conflicts); Assert.Equal(KeyNames.PageUp, conflict.Key.Value); Assert.Contains(TuiKeybindings.InputSubmit, conflict.Actions); Assert.Contains(TuiKeybindings.InputCancel, conflict.Actions); } [Fact] public void DefaultInputParserParsesShiftTabAndAltLetters() { var parser = new DefaultInputParser(); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Tab))], parser.Parse("\e[Z")); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Alt("a"))], parser.Parse("\ea")); } [Fact] public void DefaultInputParserParsesKittyCsiUKeys() { var parser = new DefaultInputParser(); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, "a", RawSequence: "\e[97u")], parser.Parse("\e[97u")); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("a"), RawSequence: "\e[97;5u")], parser.Parse("\e[97;5u")); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift("e"), RawSequence: "\e[69;2u")], parser.Parse("\e[69;2u")); var repeat = Assert.Single(parser.Parse("\e[97;1:2u")); Assert.Equal(TuiInputEventKind.Key, repeat.Kind); Assert.Equal("a", repeat.Value); Assert.Equal(TuiKeyEventType.Repeat, repeat.EventType); Assert.Equal("\e[97;1:2u", repeat.RawSequence); } [Fact] public void DefaultInputParserCarriesKittyAlternateKeyMetadata() { var parser = new DefaultInputParser(); var input = Assert.Single(parser.Parse("\e[1089::99;5u")); Assert.Equal(TuiInputEventKind.Key, input.Kind); Assert.Equal(KeyNames.Ctrl("c"), input.Value); Assert.Null(input.AlternateKey); Assert.Equal("\e[1089::99;5u", input.RawSequence); var shifted = Assert.Single(parser.Parse("\e[99:67:99;2u")); Assert.Equal(KeyNames.Shift("c"), shifted.Value); Assert.Equal("C", shifted.AlternateKey); } [Fact] public void DefaultInputParserParsesModifyOtherKeysCombinations() { var parser = new DefaultInputParser(); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("c"), RawSequence: "\e[27;5;99~")], parser.Parse("\e[27;5;99~")); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Tab), RawSequence: "\e[27;2;9~")], parser.Parse("\e[27;2;9~")); Assert.Equal( [new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Alt(KeyNames.Enter), RawSequence: "\e[27;3;13~")], parser.Parse("\e[27;3;13~")); } [Fact] public void DefaultInputParserKeepsKittyReleaseEvents() { var parser = new DefaultInputParser(); var release = Assert.Single(parser.Parse("\e[97;1:3u")); Assert.Equal(TuiInputEventKind.Key, release.Kind); Assert.Equal("a", release.Value); Assert.Equal(TuiKeyEventType.Release, release.EventType); Assert.Equal("\e[97;1:3u", release.RawSequence); } [Fact] public void KeybindingRegistryDoesNotMatchReleaseEvents() { var registry = KeybindingRegistry.CreateDefault(); registry.SetUserBinding(TuiKeybindings.InputSubmit, KeyNames.Enter); var release = new TuiInputEvent( TuiInputEventKind.Key, KeyNames.Enter, TuiKeyEventType.Release, RawSequence: "\e[13;1:3u"); Assert.False(registry.Matches(release, TuiKeybindings.InputSubmit)); } }