1package ui23import (4 "slices"5 "strings"6 "testing"78 tea "charm.land/bubbletea/v2"910 "src.rest/cat/012/internal/fileio"11 "src.rest/cat/012/internal/sheet"12)1314const filesNUON = "[[name, size]; [a, 1646b], [b, 2kb]]"1516// stdinModel is a model that has read text from standard input, in a17// pipeline (to, or the input's format when 0) when pipe is set.18func stdinModel(t *testing.T, text string, pipe bool, to fileio.Kind) *Model {19 t.Helper()20 m := newModel()21 if pipe {22 m.SetPipe(to)23 }24 m.ReadStdin(strings.NewReader(text))25 run(m, m.startStdin())26 if m.xfer.Busy() {27 t.Fatal("still reading")28 }29 return m30}3132func TestStdinOpensAnUnsavedSheet(t *testing.T) {33 m := stdinModel(t, filesNUON, false, 0)34 if m.sheet.Name() != StdinName || !m.changed || m.filename != "" || m.xfer.Source != "" {35 t.Errorf("sheet %q, changed %v, file %q, source %q", m.sheet.Name(), m.changed, m.filename, m.xfer.Source)36 }37 if got := line(m, contextLine); !strings.Contains(got, "Read 3 rows of NUON from standard input") {38 t.Errorf("context line %q", got)39 }40 if got := sheet.FormatText(m.sheet.Value(addr("B2")), m.sheet.DisplayFormat(addr("B2"))); got != "1.6 kB" {41 t.Errorf("B2 shows %q", got)42 }43 press(t, m, "<ctrl+q>")44 if got := line(m, contextLine); !strings.Contains(got, "You have unsaved changes.") {45 t.Errorf("quitting outside a pipeline: %q", got)46 }47 if st := line(m, m.height-1); strings.Contains(st, "send") {48 t.Errorf("status line outside a pipeline: %q", st)49 }50}5152func TestPipeSendsTheSheet(t *testing.T) {53 m := stdinModel(t, filesNUON, true, 0)54 if st := line(m, m.height-1); !strings.Contains(st, "Ctrl+Q send the sheet as NUON") {55 t.Errorf("status line %q", st)56 }57 press(t, m, "<ctrl+q>")58 if got := line(m, contextLine); !strings.Contains(got, "Send the sheet as NUON?") ||59 !strings.Contains(got, "Enter Send sheet") || !strings.Contains(got, "D Don't send") || strings.Contains(got, "Send selection") {60 t.Errorf("question %q", got)61 }62 if _, quit := press(t, m, "<enter>").(tea.QuitMsg); !quit {63 t.Fatal("didn't quit")64 }65 snap, k, ok := m.Piped()66 if !ok || k != fileio.NUON || snap.Range.String() != "A1:B3" {67 t.Errorf("sent %v %v %v", snap, k, ok)68 }69}7071func TestPipeSendsTheSelection(t *testing.T) {72 m := stdinModel(t, filesNUON, true, fileio.CSV)73 press(t, m, "<shift+down>")74 if st := line(m, m.height-1); !strings.Contains(st, "send A1:A2 as CSV") {75 t.Errorf("status line %q", st)76 }77 press(t, m, "<ctrl+q>")78 if got := line(m, contextLine); !strings.Contains(got, "Send A1:A2 as CSV?") || !strings.Contains(got, "S Send sheet") {79 t.Errorf("question %q", got)80 }81 press(t, m, "<enter>")82 if snap, k, ok := m.Piped(); !ok || k != fileio.CSV || snap.Range.String() != "A1:A2" {83 t.Errorf("sent %v %v %v", snap.Range, k, ok)84 }8586 m = stdinModel(t, "a\tb\n1\t2\n", true, 0)87 press(t, m, "<shift+down>", "<ctrl+q>", "s")88 if snap, k, ok := m.Piped(); !ok || k != fileio.TSV || snap.Range.String() != "A1:B2" {89 t.Errorf("sent the sheet? %v %v %v", snap.Range, k, ok)90 }91}9293func TestPipeQuitWithoutSending(t *testing.T) {94 m := stdinModel(t, filesNUON, true, 0)95 press(t, m, "<ctrl+q>", "<esc>")96 if m.mode != modeReady {97 t.Errorf("Esc left mode %v", m.mode)98 }99 if _, quit := press(t, m, "<ctrl+q>", "d").(tea.QuitMsg); !quit {100 t.Fatal("didn't quit")101 }102 if _, _, ok := m.Piped(); ok {103 t.Error("sent anyway")104 }105}106107// TestPipeSendSelection: with --send selection, Quit sends the108// selection without asking, or the sheet when only one cell is109// selected, and the status line says which.110func TestPipeSendSelection(t *testing.T) {111 m := stdinModel(t, filesNUON, true, 0)112 m.SetSend(SendSelection)113 if st := line(m, m.height-1); !strings.Contains(st, "Ctrl+Q send the sheet as NUON") {114 t.Errorf("status line %q", st)115 }116 press(t, m, "<shift+down>", "<shift+right>")117 if st := line(m, m.height-1); !strings.Contains(st, "Ctrl+Q send A1:B2 as NUON") {118 t.Errorf("status line %q", st)119 }120 if _, quit := press(t, m, "<ctrl+q>").(tea.QuitMsg); !quit {121 t.Fatal("asked rather than quit")122 }123 if snap, _, ok := m.Piped(); !ok || snap.Range.String() != "A1:B2" {124 t.Errorf("sent %v %v", snap.Range, ok)125 }126127 m = stdinModel(t, filesNUON, true, 0)128 m.SetSend(SendSelection)129 press(t, m, "<down>")130 cmd, _ := m.runCmdLine("q")131 if _, quit := run(m, cmd).(tea.QuitMsg); !quit {132 t.Fatal(":q asked rather than quit")133 }134 if snap, _, ok := m.Piped(); !ok || snap.Range.String() != "A1:B3" {135 t.Errorf("one cell selected sent %v %v", snap.Range, ok)136 }137}138139// TestPipeSendSheet: with --send sheet, Quit sends the sheet whatever140// is selected, and Quit without sending is still in the palette.141func TestPipeSendSheet(t *testing.T) {142 m := stdinModel(t, filesNUON, true, fileio.CSV)143 m.SetSend(SendSheet)144 press(t, m, "<shift+down>")145 if st := line(m, m.height-1); !strings.Contains(st, "Ctrl+Q send the sheet as CSV") {146 t.Errorf("status line %q", st)147 }148 if _, quit := run(m, m.runCommand("quit")).(tea.QuitMsg); !quit {149 t.Fatal("File > Quit asked rather than quit")150 }151 if snap, k, ok := m.Piped(); !ok || k != fileio.CSV || snap.Range.String() != "A1:B3" {152 t.Errorf("sent %v %v %v", snap.Range, k, ok)153 }154155 m = stdinModel(t, filesNUON, true, 0)156 m.SetSend(SendSheet)157 if _, quit := run(m, m.runCommand("pipe.quit_unsent")).(tea.QuitMsg); !quit {158 t.Fatal("Quit without sending didn't quit")159 }160 if _, _, ok := m.Piped(); ok {161 t.Error("Quit without sending sent")162 }163}164165// TestPipeCommands: the send commands and Quit without sending are in166// File and the palette only in a pipeline, and run from there.167func TestPipeCommands(t *testing.T) {168 titles := func(m *Model) []string {169 var out []string170 for _, it := range paletteItems(m) {171 out = append(out, it.Title)172 }173 return out174 }175 m := newModel()176 if slices.Contains(titles(m), "Quit and send sheet") || len(m.applicable(menuBar[0].items)) != len(menuBar[0].items)-8 { // the send commands, sharing outside 012 serve, and agents before one is invited177 t.Error("send commands outside a pipeline")178 }179 m = stdinModel(t, filesNUON, true, 0)180 if !slices.Contains(titles(m), "Quit and send sheet") || !slices.Contains(titles(m), "Quit and send selection") ||181 !slices.Contains(titles(m), "Quit without sending") {182 t.Error("send commands missing in a pipeline")183 }184 if commands["pipe.send_selection"].available(m) {185 t.Error("send selection without a selection")186 }187 if _, quit := run(m, m.runCommand("pipe.send_sheet")).(tea.QuitMsg); !quit {188 t.Fatal("didn't quit")189 }190 if _, _, ok := m.Piped(); !ok {191 t.Error("nothing sent")192 }193}194195// TestPipeSendsAFileArgument: 012 --pipe data.nuon sends in the file's196// format, and a .012 file as NUON.197func TestPipeFormatOfAFile(t *testing.T) {198 m := newModel()199 m.SetPipe(0)200 if m.pipeKind() != fileio.NUON {201 t.Errorf("a .012 sheet sends %v", m.pipeKind())202 }203 m.xfer.Kind = fileio.JSON204 if m.pipeKind() != fileio.JSON {205 t.Errorf("a JSON file sends %v", m.pipeKind())206 }207 m.xfer.Kind = fileio.XLSX208 if m.pipeKind() != fileio.NUON {209 t.Errorf("an Excel file sends %v", m.pipeKind())210 }211}