cat / 012
clone

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

cat/module-path
012 / internal / ui / srcview / view_test.go
view_test.go 4.5K
1package srcview23import (4 "strconv"5 "strings"6 "testing"78 tea "charm.land/bubbletea/v2"9 "github.com/charmbracelet/x/ansi"1011 "src.rest/cat/012/internal/locale"12 "src.rest/cat/012/internal/sheet"13 "src.rest/cat/012/internal/ui/theme"14)1516// fakeHost is a source of n rows, row i holding i and "r<i>", whose17// rows from lost on are on their way.18type fakeHost struct {19 th theme.Theme20 n, lost int6421 from, to int64 // what was last wanted22 counted bool23 wantCalled int24}2526func newHost(n int64) *fakeHost {27 return &fakeHost{th: theme.New(true), n: n, lost: n, counted: true}28}2930func (h *fakeHost) Theme() *theme.Theme { return &h.th }31func (h *fakeHost) Locale() *locale.Locale { return locale.Canonical }32func (h *fakeHost) Columns() []Column { return []Column{{Name: "id"}, {Name: "name"}} }33func (h *fakeHost) Rows() (int64, bool) { return h.n, h.counted }3435func (h *fakeHost) Row(i int64) (int64, []sheet.LiveCell, bool) {36 if i >= h.lost || i >= h.n {37 return 0, nil, false38 }39 return i, []sheet.LiveCell{{V: sheet.Value{Kind: sheet.Number, Num: float64(i)}},40 {V: sheet.Value{Kind: sheet.Text, Str: "r" + strconv.FormatInt(i, 10)}}}, true41}4243func (h *fakeHost) Want(from, to int64) { h.from, h.to, h.wantCalled = from, to, h.wantCalled+1 }4445func view(n int64, width, height int) (*View, *fakeHost) {46 h := newHost(n)47 v := New(h)48 v.Resize(width, height)49 return v, h50}5152func key(s string) tea.KeyPressMsg {53 switch s {54 case "down":55 return tea.KeyPressMsg{Code: tea.KeyDown}56 case "pgdown":57 return tea.KeyPressMsg{Code: tea.KeyPgDown}58 case "ctrl+down":59 return tea.KeyPressMsg{Code: tea.KeyDown, Mod: tea.ModCtrl}60 case "ctrl+home":61 return tea.KeyPressMsg{Code: tea.KeyHome, Mod: tea.ModCtrl}62 case "end":63 return tea.KeyPressMsg{Code: tea.KeyEnd}64 }65 return tea.KeyPressMsg{Code: []rune(s)[0], Text: s}66}6768func TestDrawNumbersRowsAsTheTab(t *testing.T) {69 v, h := view(10_000_000, 60, 13)70 lines := v.Draw()71 if len(lines) != 13 {72 t.Fatalf("%d lines", len(lines))73 }74 if l := ansi.Strip(lines[namesLine]); !strings.Contains(l, "1 id") && !strings.Contains(l, "id") {75 t.Errorf("header row %q", l)76 }77 if l := ansi.Strip(lines[firstRow]); !strings.HasPrefix(strings.TrimSpace(l), "2 ") || !strings.Contains(l, "r0") {78 t.Errorf("first row %q", l)79 }80 if h.from > 0 || h.to < 10 {81 t.Errorf("wanted %d to %d", h.from, h.to)82 }83 // The last row is the tab's row 10,000,001, and the thumb is at the84 // bottom of the track.85 v.Key(key("ctrl+down"))86 lines = v.Draw()87 last := ansi.Strip(lines[len(lines)-1])88 if !strings.HasPrefix(strings.TrimSpace(last), "10000001 ") || !strings.HasSuffix(last, "┃") {89 t.Errorf("last row %q", last)90 }91 if first := ansi.Strip(lines[firstRow]); !strings.HasSuffix(first, "│") {92 t.Errorf("the track at the top %q", first)93 }94}9596func TestRowsOnTheirWay(t *testing.T) {97 v, h := view(100, 60, 10)98 h.lost = 399 lines := v.Draw()100 if l := ansi.Strip(lines[firstRow+3]); !strings.Contains(l, "…") {101 t.Errorf("a row on its way %q", l)102 }103 if _, _, ok := v.Cell(); !ok {104 t.Error("the first row is here")105 }106 h.counted = false107 v.Refit()108 v.Draw()109 if v.fitted {110 t.Error("fitted before the rows were counted")111 }112}113114func TestKeysAndScroll(t *testing.T) {115 v, _ := view(1000, 60, 13)116 v.Draw()117 v.Key(key("down"))118 if row, _ := v.Active(); row != 1 {119 t.Errorf("down: row %d", row)120 }121 v.Key(key("pgdown"))122 if row, _ := v.Active(); row != int64(v.Lines()) || v.Top() != int64(v.Lines()-1) {123 t.Errorf("page down: row %d top %d", row, v.Top())124 }125 v.Key(key("end"))126 if _, col := v.Active(); col != 1 {127 t.Errorf("end: column %d", col)128 }129 v.Key(key("ctrl+home"))130 if row, col := v.Active(); row != 0 || col != 0 || v.Top() != 0 {131 t.Errorf("ctrl+home: %d %d top %d", row, col, v.Top())132 }133 if v.Key(key("x")) {134 t.Error("a letter is the view's")135 }136 v.Scroll(500)137 if row, _ := v.Active(); row < v.Top() || row >= v.Top()+int64(v.Lines()) {138 t.Errorf("the active row %d left the window from %d", row, v.Top())139 }140}141142func TestMouse(t *testing.T) {143 v, _ := view(1000, 60, 13)144 v.Draw()145 if hit := v.HitAt(59, firstRow+2); !hit.Bar {146 t.Errorf("the scrollbar isn't hit: %+v", hit)147 }148 v.DragBar(firstRow + v.Lines() - 1)149 if last := int64(1000 - v.Lines()); v.Top() != last {150 t.Errorf("the thumb dragged to the bottom shows from %d, want %d", v.Top(), last)151 }152 x := v.ColX(1)153 if hit := v.HitAt(x, firstRow); !hit.Cell || hit.Col != 1 || hit.Row != v.Top() {154 t.Errorf("a click on column B: %+v", hit)155 }156 if hit := v.HitAt(x, namesLine); hit.Cell || hit.Bar {157 t.Errorf("the header row is hit: %+v", hit)158 }159}