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 / nbstmts_test.go
nbstmts_test.go 3.9K
1package ui23import (4 "encoding/base64"5 "slices"6 "strings"7 "testing"89 "src.rest/cat/012/internal/notebook"10)1112// ownersCell is a cell of two statements: files is its own variable,13// read by its second line.14const ownersCell = "files = ls | where type == file\n$files | where size > 1kb | sort-by size --reverse"1516// varsOut is what nu prints for a run handing back variables: a line17// for the output and each variable, its NUON as base64.18func varsOut(out string, vars ...string) string {19 enc := func(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) }20 lines := []string{"__out " + enc(out)}21 for i := 0; i+1 < len(vars); i += 2 {22 lines = append(lines, vars[i]+" "+enc(vars[i+1]))23 }24 return strings.Join(lines, "\n") + "\n"25}2627// nuCommand is what nu runs for src.28func nuCommand(src string) string {29 cmd, _, _ := notebook.Parse(src).Command()30 return cmd31}3233// The owner's cell runs as a script: files is the cell's own, not a cell34// it reads, its output is its last line's, it has no name, and a later35// cell reads $files from its run.36func TestNotebookStatements(t *testing.T) {37 big := "[[name, size]; [c.txt, 5kb], [a.txt, 2kb]]"38 m, nu := notebookModel(t, map[string]string{39 nuCommand(ownersCell): varsOut(big, "files", lsOut),40 "$files | length": "3",41 })42 write(t, m, ownersCell)43 write(t, m, "$files | length")44 press(t, m, "<ctrl+enter>") // the second runs the first first45 if got := nu.ran(); len(got) != 2 || !strings.HasPrefix(got[0], "let files = ls") {46 t.Fatalf("ran %q", got)47 }48 if !slices.Equal(nu.jobs[0].Vars, []string{"files"}) || len(nu.jobs[0].Tables) != 0 {49 t.Errorf("the first job %+v", nu.jobs[0])50 }51 if got := string(nu.jobs[1].Tables["files"]); got != lsOut {52 t.Errorf("$files reached the second cell as %q", got)53 }54 s := screen(m)55 if strings.Contains(s, "failed") || strings.Contains(s, "─ files ─") || !strings.Contains(s, "c.txt") {56 t.Errorf("screen:\n%s", s)57 }58 cs := m.sheet.NotebookCells()59 if o := m.book().Output(cs[0].ID); o == nil || string(o.NUON) != big || string(o.Vars["files"]) != lsOut {60 t.Errorf("output %+v", o)61 }62 press(t, m, "<up>", "<up>", "<up>")63 if i, _ := m.nbView().Selected(); i != 0 {64 t.Fatalf("selected %d", i)65 }66 if got := line(m, contextLine); !strings.Contains(got, "sets $files") || strings.Contains(got, "reads") {67 t.Errorf("context line %q", got)68 }69 // Running it again makes the reader stale.70 press(t, m, "<ctrl+enter>")71 if st := m.cellState(m.sheet, cs[1].ID); !st.Stale {72 t.Error("the reader isn't stale")73 }74 // Naming the cell names its last statement.75 press(t, m, "n")76 send(m, pasteMsg("big"))77 press(t, m, "<enter>")78 if got := m.sheet.NotebookCells()[0].Source; got != "files = ls | where type == file\nbig = $files | where size > 1kb | sort-by size --reverse" {79 t.Errorf("named %q", got)80 }81}8283// Comments are nu's: a commented-out assignment names nothing and a84// commented-out $name reads nothing.85func TestNotebookComments(t *testing.T) {86 src := "# files = ls\nls # | where name == $nope"87 m, nu := notebookModel(t, map[string]string{nuCommand(src): lsOut})88 write(t, m, src)89 press(t, m, "<ctrl+enter>")90 if len(nu.jobs) != 1 || len(nu.jobs[0].Tables) != 0 || strings.Contains(nu.jobs[0].Command, "#") {91 t.Fatalf("ran %+v", nu.jobs)92 }93 if strings.Contains(screen(m), "failed") || m.sheet.NotebookCells()[0].Name() != "" {94 t.Errorf("screen:\n%s", screen(m))95 }96}9798// A cell of several statements streams its last one's values, its own99// variables bound first and none handed back.100func TestNotebookStreamStatements(t *testing.T) {101 streamPolls = nil102 m, _ := notebookModel(t, nil)103 nu := newStreamNu()104 m.SetShellRunner(nu)105 write(t, m, "log = open app.log\n$log | lines")106 press(t, m, "f")107 defer m.stopCells()108 script := <-nu.scripts109 if !strings.Contains(script, "let log = open app.log\n$log | lines") || strings.Contains(script, "__out") {110 t.Errorf("stream script %s", script)111 }112}