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 / sheet / proposal_test.go
proposal_test.go 4.6K
1package sheet23import (4 "errors"5 "testing"6)78// proposalBook is a workbook with 1, 2, 3 in A1:A3 and their sum in A4,9// its history shared by two authors, 1 the person and 2 the agent.10func proposalBook(t *testing.T) *Workbook {11 t.Helper()12 w := New().Book()13 s := w.Sheet(0)14 for i, in := range []string{"1", "2", "3", "=SUM(A1:A3)"} {15 if err := s.Set(Addr{Row: i}, in); err != nil {16 t.Fatal(err)17 }18 }19 w.ShareHistory()20 return w21}2223func setCells(entries map[Addr]string) func(*Workbook) error {24 return func(w *Workbook) error {25 for a, in := range entries {26 if err := w.Sheet(0).Set(a, in); err != nil {27 return err28 }29 }30 return nil31 }32}3334func TestProposeLeavesTheWorkbook(t *testing.T) {35 w := proposalBook(t)36 state := w.StateID()37 p, err := Propose(w, "set", setCells(map[Addr]string{{Row: 0}: "10", {Row: 1, Col: 1}: "x"}))38 if err != nil {39 t.Fatal(err)40 }41 if w.StateID() != state || w.Sheet(0).Value(Addr{Row: 3}).Num != 6 {42 t.Fatal("proposing changed the workbook")43 }44 if len(p.Cells) != 2 || p.Whole() || p.Remote {45 t.Fatalf("cells %+v, other %v", p.Cells, p.Other)46 }47 if c := p.Cells[0]; c.At != (Addr{Row: 0}) || c.Was != "1" || c.Now != "10" {48 t.Fatalf("first cell %+v", c)49 }50 if got := p.Result().Sheet(0).Value(Addr{Row: 3}).Num; got != 15 {51 t.Fatalf("the copy's sum is %v", got)52 }53}5455func TestProposalAcceptedWholeIsOneStepOfItsAuthor(t *testing.T) {56 w := proposalBook(t)57 p, _ := Propose(w, "fix", setCells(map[Addr]string{{Row: 0}: "10", {Row: 1}: "20"}))58 w.SetAuthor(1)59 w.Sheet(0).Set(Addr{Col: 2}, "mine")60 w.SetAuthor(2)61 if err := p.Apply(w, nil); err != nil {62 t.Fatal(err)63 }64 if got := w.Sheet(0).Value(Addr{Row: 3}).Num; got != 33 {65 t.Fatalf("sum %v after accepting", got)66 }67 w.SetAuthor(2)68 if w.UndoLabel() != "fix" {69 t.Fatalf("the agent's undo is %q", w.UndoLabel())70 }71 w.Undo()72 if got := w.Sheet(0).Value(Addr{Row: 3}).Num; got != 6 || w.Sheet(0).Cell(Addr{Col: 2}).Input != "mine" {73 t.Fatal("the agent's undo took back more than its step")74 }75}7677func TestProposalAcceptedByCell(t *testing.T) {78 w := proposalBook(t)79 p, _ := Propose(w, "fix", setCells(map[Addr]string{{Row: 0}: "10", {Row: 1}: "20", {Row: 2}: ""}))80 if len(p.Cells) != 3 {81 t.Fatalf("cells %+v", p.Cells)82 }83 if err := p.Apply(w, func(i int) bool { return i != 1 }); err != nil {84 t.Fatal(err)85 }86 s := w.Sheet(0)87 if inputOf(s, Addr{Row: 0}) != "10" || inputOf(s, Addr{Row: 1}) != "2" || inputOf(s, Addr{Row: 2}) != "" {88 t.Fatal("the picked cells weren't the ones set")89 }90 if got := s.Value(Addr{Row: 3}).Num; got != 12 {91 t.Fatalf("sum %v", got)92 }93}9495func TestProposalRejectedChangesNothing(t *testing.T) {96 w := proposalBook(t)97 state := w.StateID()98 p, _ := Propose(w, "fix", setCells(map[Addr]string{{Row: 0}: "10"}))99 if err := p.Apply(w, func(int) bool { return false }); err != nil || w.StateID() != state {100 t.Fatal("picking no cell made a step")101 }102}103104func TestProposalKeepsFormats(t *testing.T) {105 w := proposalBook(t)106 p, _ := Propose(w, "bold", func(w *Workbook) error {107 w.Sheet(0).SetStyle(Rect{From: Addr{Row: 0}, To: Addr{Row: 0}}, func(st *Style) { st.Bold = true })108 return nil109 })110 if len(p.Cells) != 1 || !p.Cells[0].Look || p.Cells[0].Was != p.Cells[0].Now {111 t.Fatalf("cells %+v", p.Cells)112 }113 p.Apply(w, nil)114 if !w.Sheet(0).Cell(Addr{Row: 0}).Style.Bold {115 t.Fatal("the style wasn't applied")116 }117}118119func TestProposalOfRowsIsWhole(t *testing.T) {120 w := proposalBook(t)121 p, _ := Propose(w, "insert", func(w *Workbook) error { return w.Sheet(0).InsertRows(0, 1) })122 if !p.Whole() || len(p.Sheets) != 1 || p.Sheets[0] != "Sheet1" {123 t.Fatalf("other %v, sheets %v", p.Other, p.Sheets)124 }125 if err := p.Apply(w, func(int) bool { return false }); !errors.Is(err, ErrWhole) && len(p.Cells) > 0 {126 t.Fatalf("part of a whole change: %v", err)127 }128 if err := p.Apply(w, nil); err != nil {129 t.Fatal(err)130 }131 if w.Sheet(0).Cell(Addr{Row: 1}).Input != "1" {132 t.Fatal("the row wasn't inserted")133 }134}135136func TestProposalNotesRemoteFormulas(t *testing.T) {137 w := proposalBook(t)138 p, _ := Propose(w, "ask", setCells(map[Addr]string{{Col: 1}: `=JEV.CLASSIFY(A1, "parity", "odd, even")`}))139 if !p.Remote {140 t.Fatalf("a JEV formula isn't remote: %+v", p.Cells)141 }142}143144func TestAdoptKeepsEarlierStepsTheirs(t *testing.T) {145 w := New().Book()146 w.Sheet(0).Set(Addr{}, "1")147 w.ShareHistory()148 w.Adopt(1)149 w.SetAuthor(1)150 if !w.CanUndo() {151 t.Fatal("the adopted step isn't the author's")152 }153 w.SetAuthor(2)154 if w.CanUndo() {155 t.Fatal("another author can undo it")156 }157}158159// inputOf is the input at a, "" for a blank cell.160func inputOf(s *Sheet, a Addr) string {161 if c := s.Cell(a); c != nil {162 return c.Input163 }164 return ""165}