cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / ui / menuoverlay_test.go
menuoverlay_test.go 2.7K
1package ui23import (4 "strings"5 "testing"67 tea "charm.land/bubbletea/v2"8 "github.com/charmbracelet/x/ansi"910 "src.rest/cat/012/internal/ui/overlay"11 "src.rest/cat/012/internal/ui/theme"12)1314// fakeMenuHost is a menu's host without a model: every command is15// available but "off", and what the menu asks for is recorded.16type fakeMenuHost struct {17 th theme.Theme18 ran []string19 closed int20 bar int21 right [2]int22}2324func (f *fakeMenuHost) styles() *theme.Theme { return &f.th }25func (f *fakeMenuHost) size() (width, height int) { return 80, 24 }26func (f *fakeMenuHost) available(id string) bool { return id != "off" }27func (f *fakeMenuHost) shortcut(id string) string { return map[string]string{"a": "Ctrl+A"}[id] }28func (f *fakeMenuHost) isChecked(id string) bool { return id == "b" }29func (f *fakeMenuHost) showBarMenu(i int) { f.bar = i }30func (f *fakeMenuHost) rightClick(x, y int) { f.right = [2]int{x, y} }31func (f *fakeMenuHost) closeOverlay() { f.closed++ }3233func (f *fakeMenuHost) runFromOverlay(id string) tea.Cmd {34 f.ran = append(f.ran, id)35 return nil36}3738func fakeMenu(h menuHost) *menuOverlay {39 items := []menuItem{40 {title: "Alpha", cmd: "a"}, {title: "Beta", cmd: "b"}, sep,41 {title: "Off", cmd: "off"},42 {title: "More", items: []menuItem{{title: "Gamma", cmd: "c"}}},43 }44 return &menuOverlay{m: h, bar: -1, x: 10, y: 5, levels: []*menuLevel{newLevel(h, items, true)}}45}4647func TestMenuAlone(t *testing.T) {48 h := &fakeMenuHost{th: theme.New(true), bar: -1}49 o := fakeMenu(h)50 b := o.Layout()[0]51 out := ansi.Strip(strings.Join(b.Lines, "\n"))52 for _, want := range []string{"Alpha", "Ctrl+A", "Beta", "✓", "Off", "›"} {53 if !strings.Contains(out, want) {54 t.Errorf("menu lacks %q:\n%s", want, out)55 }56 }57 if b.X != 10 || b.Y != 5 {58 t.Errorf("menu at %d,%d", b.X, b.Y)59 }60 // Down skips the separator and the unavailable item.61 o.Key(tea.KeyPressMsg{Code: tea.KeyDown})62 o.Key(tea.KeyPressMsg{Code: tea.KeyDown})63 if it := o.top().items[o.top().sel]; it.title != "More" {64 t.Fatalf("highlighted %q", it.title)65 }66 o.Key(tea.KeyPressMsg{Code: tea.KeyRight})67 o.Key(tea.KeyPressMsg{Code: tea.KeyEnter})68 if len(o.levels) != 2 || strings.Join(h.ran, ",") != "c" {69 t.Errorf("levels %d, ran %v", len(o.levels), h.ran)70 }71 // A letter runs the only item starting with it.72 o.Key(tea.KeyPressMsg{Code: tea.KeyEscape})73 o.Key(tea.KeyPressMsg{Code: 'b', Text: "b"})74 if strings.Join(h.ran, ",") != "c,b" {75 t.Errorf("ran %v", h.ran)76 }77 // Right-clicking outside closes the menu and opens one there.78 o.Mouse(overlay.MouseEvent{Kind: overlay.MousePress, Button: tea.MouseRight, X: 50, Y: 20})79 if h.closed != 1 || h.right != [2]int{50, 20} {80 t.Errorf("closed %d, right-click at %v", h.closed, h.right)81 }82}