1package tui
2
3import "testing"
4
5func TestReaderParsesCSIUShiftEnter(t *testing.T) {
6 k := readKey(t, "\x1b[13;2u")
7 if k.Kind != KeyEnter || !k.Shift || k.Alt {
8 t.Fatalf("Read kind=%v shift=%v alt=%v, want shift+enter", k.Kind, k.Shift, k.Alt)
9 }
10}
11
12func TestReaderParsesModifyOtherKeysShiftEnter(t *testing.T) {
13 k := readKey(t, "\x1b[27;2;13~")
14 if k.Kind != KeyEnter || !k.Shift || k.Alt {
15 t.Fatalf("Read kind=%v shift=%v alt=%v, want shift+enter", k.Kind, k.Shift, k.Alt)
16 }
17}
18
19func TestReaderParsesCSIUCtrlC(t *testing.T) {
20 k := readKey(t, "\x1b[99;5u")
21 if k.Kind != KeyCtrlC || !k.Ctrl {
22 t.Fatalf("Read kind=%v ctrl=%v, want ctrl+c", k.Kind, k.Ctrl)
23 }
24}
25
26func TestReaderParsesCSIUCtrlNumber(t *testing.T) {
27 k := readKey(t, "\x1b[49;5u")
28 if k.Kind != KeyRune || k.Rune != '1' || !k.Ctrl {
29 t.Fatalf("Read kind=%v rune=%q ctrl=%v, want ctrl+1", k.Kind, k.Rune, k.Ctrl)
30 }
31}
32
33func TestReaderParsesCSIUSuperNumber(t *testing.T) {
34 k := readKey(t, "\x1b[50;9u")
35 if k.Kind != KeyRune || k.Rune != '2' || !k.Super {
36 t.Fatalf("Read kind=%v rune=%q super=%v, want super+2", k.Kind, k.Rune, k.Super)
37 }
38}
39
40func TestReaderParsesCSIUSuperNumberWithEventType(t *testing.T) {
41 k := readKey(t, "\x1b[51;9:3u")
42 if k.Kind != KeyRune || k.Rune != '3' || !k.Super {
43 t.Fatalf("Read kind=%v rune=%q super=%v, want super+3", k.Kind, k.Rune, k.Super)
44 }
45}
46
47func TestReaderParsesCSIUHyperNumberAsSuper(t *testing.T) {
48 k := readKey(t, "\x1b[52;33u")
49 if k.Kind != KeyRune || k.Rune != '4' || !k.Super {
50 t.Fatalf("Read kind=%v rune=%q super=%v, want hyper+4 as super", k.Kind, k.Rune, k.Super)
51 }
52}
53
54func TestReaderParsesRawCtrlVAsClipboardPaste(t *testing.T) {
55 k := readKey(t, "\x16")
56 if k.Kind != KeyPasteClipboard || !k.Ctrl {
57 t.Fatalf("Read kind=%v ctrl=%v, want ctrl+v clipboard paste", k.Kind, k.Ctrl)
58 }
59}
60
61func TestReaderParsesCSIUCtrlVAsClipboardPaste(t *testing.T) {
62 k := readKey(t, "\x1b[118;5u")
63 if k.Kind != KeyPasteClipboard || !k.Ctrl {
64 t.Fatalf("Read kind=%v ctrl=%v, want enhanced ctrl+v clipboard paste", k.Kind, k.Ctrl)
65 }
66}
67
68func TestReaderParsesModifyOtherKeysCtrlC(t *testing.T) {
69 k := readKey(t, "\x1b[27;5;99~")
70 if k.Kind != KeyCtrlC || !k.Ctrl {
71 t.Fatalf("Read kind=%v ctrl=%v, want ctrl+c", k.Kind, k.Ctrl)
72 }
73}
74
75func TestReaderParsesCSIUEsc(t *testing.T) {
76 k := readKey(t, "\x1b[27u")
77 if k.Kind != KeyEsc {
78 t.Fatalf("Read kind=%v, want esc", k.Kind)
79 }
80}
81
82func TestReaderParsesCSIUTabAndBackspace(t *testing.T) {
83 if k := readKey(t, "\x1b[9u"); k.Kind != KeyTab {
84 t.Fatalf("Read kind=%v, want tab", k.Kind)
85 }
86 if k := readKey(t, "\x1b[9;2u"); k.Kind != KeyShiftTab {
87 t.Fatalf("Read kind=%v, want shift-tab", k.Kind)
88 }
89 if k := readKey(t, "\x1b[127u"); k.Kind != KeyBackspace {
90 t.Fatalf("Read kind=%v, want backspace", k.Kind)
91 }
92}
93
94func TestReaderParsesSS3CursorKeys(t *testing.T) {
95 cases := []struct {
96 seq string
97 want KeyKind
98 }{
99 {"\x1bOA", KeyUp},
100 {"\x1bOB", KeyDown},
101 {"\x1bOC", KeyRight},
102 {"\x1bOD", KeyLeft},
103 }
104 for _, tc := range cases {
105 if k := readKey(t, tc.seq); k.Kind != tc.want {
106 t.Fatalf("Read(%q) kind=%v, want %v", tc.seq, k.Kind, tc.want)
107 }
108 }
109}
110
111func TestReaderParsesSGRMouseWheel(t *testing.T) {
112 cases := []struct {
113 seq string
114 want KeyKind
115 }{
116 {"\x1b[<64;10;20M", KeyMouseWheelUp},
117 {"\x1b[<65;10;20M", KeyMouseWheelDown},
118 }
119 for _, tc := range cases {
120 k := readKey(t, tc.seq)
121 if k.Kind != tc.want {
122 t.Fatalf("Read(%q) kind=%v, want %v", tc.seq, k.Kind, tc.want)
123 }
124 }
125}
126
127func readKey(t *testing.T, seq string) Key {
128 t.Helper()
129 idx := 0
130 r := NewReader(func() (byte, error) {
131 b := seq[idx]
132 idx++
133 return b, nil
134 })
135 k, err := r.Read()
136 if err != nil {
137 t.Fatalf("Read(%q): %v", seq, err)
138 }
139 return k
140}