termd/internal/keymap/keymap_test.go
2026-07-02 22:13:49 -03:00

135 lines
4.3 KiB
Go

package keymap
import (
"testing"
uv "github.com/charmbracelet/ultraviolet"
)
func TestParseEvents(t *testing.T) {
cases := []struct {
name string
want uv.KeyPressEvent
}{
{"Enter", uv.KeyPressEvent{Code: uv.KeyEnter}},
{"enter", uv.KeyPressEvent{Code: uv.KeyEnter}},
{"Escape", uv.KeyPressEvent{Code: uv.KeyEscape}},
{"Esc", uv.KeyPressEvent{Code: uv.KeyEscape}},
{"Tab", uv.KeyPressEvent{Code: uv.KeyTab}},
{"BTab", uv.KeyPressEvent{Code: uv.KeyTab, Mod: uv.ModShift}},
{"S-Tab", uv.KeyPressEvent{Code: uv.KeyTab, Mod: uv.ModShift}},
{"Backspace", uv.KeyPressEvent{Code: uv.KeyBackspace}},
{"Space", uv.KeyPressEvent{Code: ' '}},
{"Up", uv.KeyPressEvent{Code: uv.KeyUp}},
{"Down", uv.KeyPressEvent{Code: uv.KeyDown}},
{"Left", uv.KeyPressEvent{Code: uv.KeyLeft}},
{"Right", uv.KeyPressEvent{Code: uv.KeyRight}},
{"Home", uv.KeyPressEvent{Code: uv.KeyHome}},
{"End", uv.KeyPressEvent{Code: uv.KeyEnd}},
{"PgUp", uv.KeyPressEvent{Code: uv.KeyPgUp}},
{"PageDown", uv.KeyPressEvent{Code: uv.KeyPgDown}},
{"Insert", uv.KeyPressEvent{Code: uv.KeyInsert}},
{"Delete", uv.KeyPressEvent{Code: uv.KeyDelete}},
{"F1", uv.KeyPressEvent{Code: uv.KeyF1}},
{"F12", uv.KeyPressEvent{Code: uv.KeyF12}},
{"a", uv.KeyPressEvent{Code: 'a'}},
{"Z", uv.KeyPressEvent{Code: 'Z'}},
{"%", uv.KeyPressEvent{Code: '%'}},
{"C-c", uv.KeyPressEvent{Code: 'c', Mod: uv.ModCtrl}},
{"C-C", uv.KeyPressEvent{Code: 'c', Mod: uv.ModCtrl}}, // normalized to lowercase
{"C-Space", uv.KeyPressEvent{Code: ' ', Mod: uv.ModCtrl}},
{"C-[", uv.KeyPressEvent{Code: '[', Mod: uv.ModCtrl}},
{"C-]", uv.KeyPressEvent{Code: ']', Mod: uv.ModCtrl}},
{"C-_", uv.KeyPressEvent{Code: '_', Mod: uv.ModCtrl}},
{"M-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModAlt}},
{"M-Enter", uv.KeyPressEvent{Code: uv.KeyEnter, Mod: uv.ModAlt}},
{"M-Escape", uv.KeyPressEvent{Code: uv.KeyEscape, Mod: uv.ModAlt}},
{"C-M-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModCtrl | uv.ModAlt}},
{"M-C-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModCtrl | uv.ModAlt}},
{"S-a", uv.KeyPressEvent{Code: 'A'}},
{"C-S-a", uv.KeyPressEvent{Code: 'a', Mod: uv.ModCtrl}}, // shift folds into the letter, ctrl re-lowers
{"-", uv.KeyPressEvent{Code: '-'}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
k, err := Parse(tc.name)
if err != nil {
t.Fatalf("Parse(%q): %v", tc.name, err)
}
ev, ok := k.Event()
if !ok {
raw, _ := k.Raw()
t.Fatalf("Parse(%q) produced raw bytes %q, want event", tc.name, raw)
}
if ev != tc.want {
t.Errorf("Parse(%q) = %+v, want %+v", tc.name, ev, tc.want)
}
})
}
}
// Modified special keys have no vt-encoder support and must come out as
// pre-encoded xterm sequences: CSI 1;m<ch> and CSI n;m~ with
// m = 1 + Shift(1) + Alt(2) + Ctrl(4).
func TestParseModifiedSpecials(t *testing.T) {
cases := []struct {
name string
want string
}{
{"C-Up", "\x1b[1;5A"},
{"S-Up", "\x1b[1;2A"},
{"M-Up", "\x1b[1;3A"},
{"C-S-Up", "\x1b[1;6A"},
{"C-M-Up", "\x1b[1;7A"},
{"C-M-S-Up", "\x1b[1;8A"},
{"C-Down", "\x1b[1;5B"},
{"C-Right", "\x1b[1;5C"},
{"C-Left", "\x1b[1;5D"},
{"C-Home", "\x1b[1;5H"},
{"S-End", "\x1b[1;2F"},
{"C-F1", "\x1b[1;5P"},
{"S-F4", "\x1b[1;2S"},
{"C-Delete", "\x1b[3;5~"},
{"S-Insert", "\x1b[2;2~"},
{"M-PgUp", "\x1b[5;3~"},
{"C-PgDn", "\x1b[6;5~"},
{"C-F5", "\x1b[15;5~"},
{"S-F12", "\x1b[24;2~"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
k, err := Parse(tc.name)
if err != nil {
t.Fatalf("Parse(%q): %v", tc.name, err)
}
raw, ok := k.Raw()
if !ok {
ev, _ := k.Event()
t.Fatalf("Parse(%q) produced event %+v, want raw bytes", tc.name, ev)
}
if string(raw) != tc.want {
t.Errorf("Parse(%q) = %q, want %q", tc.name, raw, tc.want)
}
})
}
}
func TestParseErrors(t *testing.T) {
for _, name := range []string{
"", // empty
"NoSuch", // unknown named key
"C-", // dangling modifier
"X-a", // bad modifier letter
"C-1", // no control character for digits
"C--", // no control character for dash
"S-5", // shift is only meaningful on letters
"C-Enter", // no escape sequence exists
"ab", // multi-rune non-name
} {
t.Run("bad:"+name, func(t *testing.T) {
if k, err := Parse(name); err == nil {
t.Errorf("Parse(%q) succeeded (%+v), want error", name, k)
}
})
}
}