1package ui23import (4 "strings"5 "testing"67 tea "charm.land/bubbletea/v2"8 "github.com/charmbracelet/x/ansi"910 "src.rest/cat/012/internal/sheet"11)1213// keyEvents is the terminal's answer when it reports key releases.14var keyEvents = tea.KeyboardEnhancementsMsg{Flags: 1 | ansi.KittyReportEventTypes}1516func space() tea.Key { return tea.Key{Code: tea.KeySpace, Text: " "} }1718// The view asks for key releases; Shift+Enter accepts an entry and moves19// up, and Ctrl+I, which only the protocol tells from Tab, is italic.20func TestKeyboardProtocol(t *testing.T) {21 m := newModel()22 if !m.View().KeyboardEnhancements.ReportEventTypes {23 t.Error("the view doesn't ask for key releases")24 }25 press(t, m, "<down>", "<down>", "5", "<shift+enter>")26 if input(m, "A3") != "5" || m.cur != addr("A2") {27 t.Errorf("Shift+Enter: A3 %q at %v", input(m, "A3"), m.cur)28 }29 press(t, m, "<down>", "<ctrl+i>")30 if c := m.sheet.Cell(addr("A3")); m.cur != addr("A3") || c == nil || !c.Style.Italic {31 t.Errorf("Ctrl+I: at %v", m.cur)32 }33 press(t, m, "<tab>")34 if m.cur != addr("B3") {35 t.Errorf("Tab: at %v", m.cur)36 }37 // A release with nothing held does nothing.38 if _, cmd := m.Update(tea.KeyReleaseMsg{Code: 'x', Text: "x"}); cmd != nil || m.mode != modeReady {39 t.Errorf("release: mode %v", m.mode)40 }41}4243// Holding Space on a selected chart shows it across the grid until44// Space is let go, on a terminal that reports releases.45func TestHoldSpaceEnlargesChart(t *testing.T) {46 m := wideModel()47 chartTable(t, m)48 m.runCommand("insert.chart")49 press(t, m, "<enter>")50 c := m.sheet.Charts()[0]51 if strings.Contains(line(m, contextLine), "hold") {52 t.Errorf("hint without releases: %q", line(m, contextLine))53 }54 send(m, keyEvents)55 if !strings.Contains(line(m, contextLine), "hold Space to zoom") {56 t.Errorf("context: %q", line(m, contextLine))57 }58 send(m, tea.KeyPressMsg(space()))59 z := m.displayCharts()[0]60 if z.W <= c.W || z.H <= c.H || z.At != (sheet.Addr{Col: m.left, Row: m.top}) {61 t.Fatalf("held: %+v", z)62 }63 if !strings.Contains(line(m, m.height-1), "let go") {64 t.Errorf("held status: %q", line(m, m.height-1))65 }66 repeat := space()67 repeat.IsRepeat = true68 send(m, tea.KeyPressMsg(repeat))69 send(m, tea.KeyReleaseMsg(space()))70 if got := m.displayCharts()[0]; got.W != c.W || got.At != c.At || m.sheet.Charts()[0].W != c.W {71 t.Errorf("after release: shown %+v, stored %+v", got, m.sheet.Charts()[0])72 }73 if _, ok := m.overlay.(*chartSel); !ok {74 t.Errorf("release left %T", m.overlay)75 }76 // Any other key lets go first.77 send(m, tea.KeyPressMsg(space()))78 press(t, m, "<right>")79 if got := m.sheet.Charts()[0]; got.W != c.W || got.At.Col != c.At.Col+1 {80 t.Errorf("Right while held: %+v", got)81 }82}8384// Without releases, Space on a selected chart deselects it and starts85// an entry, as it always has.86func TestSpaceOnChartWithoutReleases(t *testing.T) {87 m := wideModel()88 chartTable(t, m)89 m.runCommand("insert.chart")90 press(t, m, "<enter>")91 send(m, tea.KeyPressMsg(space()))92 if _, ok := m.overlay.(*chartSel); ok || m.mode != modeEnter {93 t.Errorf("Space: %T mode %v", m.overlay, m.mode)94 }95}9697// Holding Space in the theme picker hides the list, showing the sheet98// in the highlighted theme, until it's let go.99func TestHoldSpaceInThemePicker(t *testing.T) {100 m, _, _ := configured(t, "theme = Nord\n")101 send(m, keyEvents)102 press(t, m, "<alt+f>")103 openSettingsItem(t, m, "Theme")104 press(t, m, "<down>")105 name := m.th.Name106 if !strings.Contains(screen(m), "Type a name") || !strings.Contains(line(m, m.height-1), "hold to peek") {107 t.Fatalf("picker: %q\n%s", line(m, m.height-1), screen(m))108 }109 send(m, tea.KeyPressMsg(space()))110 if s := screen(m); strings.Contains(s, "Type a name") || m.th.Name != name {111 t.Fatalf("held: theme %q\n%s", m.th.Name, s)112 }113 send(m, tea.KeyReleaseMsg(space()))114 if s := screen(m); !strings.Contains(s, "Type a name") || m.overlay == nil {115 t.Fatalf("released:\n%s", s)116 }117 // With a search typed, Space is typed into it.118 press(t, m, "no")119 send(m, tea.KeyPressMsg(space()))120 if m.line.Text() != "no " {121 t.Errorf("search: %q", m.line.Text())122 }123}