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 / links_test.go
links_test.go 3.7K
1package ui23import (4 "fmt"5 "image/color"6 "strings"7 "testing"89 tea "charm.land/bubbletea/v2"10 uv "github.com/charmbracelet/ultraviolet"11 "github.com/charmbracelet/x/ansi"1213 "src.rest/cat/012/internal/ui/theme"14)1516func TestLinksRenderAsHyperlinks(t *testing.T) {17 m := newModel()18 press(t, m, "https://example.com", "<enter>", `=HYPERLINK("example.org", "Docs")`, "<enter>", "plain text", "<enter>")19 v := m.View().Content20 for _, want := range []string{"\x1b]8;;https://example.com\x07", "\x1b]8;;https://example.org\x07"} {21 if !strings.Contains(v, want) {22 t.Errorf("view lacks hyperlink %q", want)23 }24 }25 if strings.Contains(ansi.Strip(strings.Split(v, "\n")[gridTop+2]), "plain") && strings.Contains(strings.Split(v, "\n")[gridTop+2], "\x1b]8") {26 t.Error("plain text drawn as a link")27 }28 // The link survives the pointer and a composited overlay. The window is29 // wide enough that the palette doesn't cover column A.30 m.Update(tea.WindowSizeMsg{Width: 200, Height: 30})31 press(t, m, "<ctrl+home>", "<ctrl+k>")32 if !strings.Contains(m.View().Content, "https://example.com") {33 t.Error("hyperlink lost under the pointer with an overlay open")34 }35}3637func TestErrorCellsAreMarked(t *testing.T) {38 m := newModel()39 press(t, m, "10", "<enter>", "=A1/0", "<enter>", "=A2+1", "<enter>")40 v := m.View().Content41 // Curly underline (4:3) under the error's text only.42 if !strings.Contains(v, "4:3m#") || !strings.Contains(ansi.Strip(v), "#DIV/0!") {43 t.Errorf("no curly underline on errors: %q", v)44 }45 press(t, m, "<up>")46 if got := line(m, contextLine); got != "#DIV/0! From A2: division by zero in A1/0" {47 t.Errorf("context line %q", got)48 }49 press(t, m, "<up>")50 if got := line(m, contextLine); got != "#DIV/0! Division by zero in A1/0" {51 t.Errorf("context line %q", got)52 }53 press(t, m, "<up>")54 if got := line(m, contextLine); got != "" {55 t.Errorf("context line on a number %q", got)56 }57}5859func TestCursorOnlyWhileTyping(t *testing.T) {60 m := newModel()61 cases := []struct {62 keys []string63 shape tea.CursorShape64 shown bool65 }{66 {nil, 0, false},67 {[]string{"1"}, tea.CursorBar, true},68 {[]string{"<esc>", "<f5>"}, tea.CursorBar, true},69 {[]string{"<esc>", "=", "<up>"}, 0, false}, // pointing: the pointer shows where70 {[]string{"<esc>", "<esc>", "<ctrl+k>"}, tea.CursorBar, true},71 {[]string{"<esc>", "<alt+f>"}, 0, false},72 }73 for i, c := range cases {74 press(t, m, c.keys...)75 cur := m.View().Cursor76 if (cur != nil) != c.shown || cur != nil && cur.Shape != c.shape {77 t.Errorf("step %d (%v): cursor %+v", i, c.keys, cur)78 }79 if cur != nil && cur.Y >= gridTop && cur.Y < m.height-1 {80 t.Errorf("step %d: cursor in the grid at %d", i, cur.Y)81 }82 }83}8485func TestLiveLightDark(t *testing.T) {86 m := newModel()87 for _, ev := range []tea.Msg{uv.LightColorSchemeEvent{}, uv.DarkColorSchemeEvent{}} {88 _, cmd := m.Update(ev)89 asked := false90 for _, msg := range flatten(cmd) {91 asked = asked || strings.Contains(fmt.Sprintf("%T", msg), "ackgroundColor")92 }93 if !asked {94 t.Errorf("%T didn't ask for the background color", ev)95 }96 }97 send(m, tea.BackgroundColorMsg{Color: color.White})98 if m.th.Link.GetForeground() != theme.New(false).Link.GetForeground() {99 t.Error("theme didn't follow a light background")100 }101}102103func TestNotifyOnlyWhenBlurred(t *testing.T) {104 m := newModel()105 if m.term.notify("done") != nil {106 t.Error("notified while focused")107 }108 send(m, tea.BlurMsg{})109 msgs := flatten(m.term.notify("Import done\x1b]0;x\x07"))110 if len(msgs) != 1 || msgs[0].(tea.RawMsg).Msg != "\x1b]9;012: Import done]0;x\x07" {111 t.Errorf("notification %q", msgs)112 }113 send(m, tea.FocusMsg{})114 if m.term.notify("done") != nil {115 t.Error("notified after focus came back")116 }117 if !m.View().ReportFocus {118 t.Error("focus reporting off")119 }120}