cat / 012
clone

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

main
012 / internal / sheet / rulemove_test.go
rulemove_test.go 5.0K
1package sheet23import "testing"45// Rules move with cut and paste: the cut cells take them along, the6// cells they land on lose theirs, and relative formulas stay right for7// the cells that stayed and those that moved.8func TestRulesMoveWithCells(t *testing.T) {9 s := New()10 for r := range 10 {11 s.Set(Addr{Row: r}, "5")12 }13 mustAdd(t, s, CondFormat{Ranges: ranges("A1:A10"), Op: RuleFormula, Args: [2]string{"=A1>4"}, Style: green})14 mustAdd(t, s, CondFormat{Ranges: ranges("A1:A10"), Op: RuleGreater, Args: [2]string{"3"}, Style: green})15 if err := s.AddValidation(Validation{Ranges: ranges("A1:A10"), Kind: ValidNumber, Op: RuleBetween, Args: [2]string{"1", "9"}, Reject: true}); err != nil {16 t.Fatal(err)17 }18 if err := s.AddValidation(Validation{Ranges: ranges("C1:C3"), Kind: ValidList, Items: []string{"x"}}); err != nil {19 t.Fatal(err)20 }21 if _, err := s.Move(rect("A1:A3"), at("C2")); err != nil {22 t.Fatal(err)23 }24 fs, vs := s.CondFormats(), s.Validations()25 if len(fs) != 3 {26 t.Fatalf("formats %+v", fs)27 }28 // The formula rule splits: A4:A10 reads its own cells, and the moved29 // cells read theirs, at their new places.30 if RangesText(fs[0].Ranges) != "A4:A10" || fs[0].Args[0] != "=A4>4" {31 t.Errorf("kept part: %s %s", RangesText(fs[0].Ranges), fs[0].Args[0])32 }33 if RangesText(fs[1].Ranges) != "C2:C4" || fs[1].Args[0] != "=C2>4" {34 t.Errorf("moved part: %s %s", RangesText(fs[1].Ranges), fs[1].Args[0])35 }36 if RangesText(fs[2].Ranges) != "A4:A10,C2:C4" {37 t.Errorf("a plain rule keeps one list: %s", RangesText(fs[2].Ranges))38 }39 // The list rule loses the cells landed on; the number rule reaches them.40 if len(vs) != 2 || RangesText(vs[0].Ranges) != "A4:A10,C2:C4" || RangesText(vs[1].Ranges) != "C1" {41 t.Errorf("validations %s, %s", RangesText(vs[0].Ranges), RangesText(vs[len(vs)-1].Ranges))42 }43 if !s.Look(at("C3")).Styled || s.Look(at("A2")).Styled {44 t.Error("looks don't follow the move")45 }46 s.Undo()47 if fs := s.CondFormats(); len(fs) != 2 || RangesText(fs[0].Ranges) != "A1:A10" || fs[0].Args[0] != "=A1>4" {48 t.Errorf("undo: %+v", fs)49 }50}5152// A paste or fill is checked after the fact: InvalidIn lists what fails,53// and Discard takes the change back without leaving it to redo.54func TestInvalidInAndDiscard(t *testing.T) {55 s := New()56 if err := s.AddValidation(Validation{Ranges: ranges("B1:B9"), Kind: ValidNumber, Op: RuleBetween, Args: [2]string{"1", "10"}, Reject: true}); err != nil {57 t.Fatal(err)58 }59 s.Set(at("A1"), "5")60 s.Set(at("A2"), "50")61 s.Set(at("A3"), "x")62 if _, err := s.Paste(s.Copy(rect("A1:A3")), rect("B1"), false); err != nil {63 t.Fatal(err)64 }65 bad := s.InvalidIn(rect("B1:B3"))66 if len(bad) != 2 || bad[0].Addr != at("B2") || bad[1].Addr != at("B3") || !bad[0].Reject {67 t.Fatalf("invalid %+v", bad)68 }69 if !s.Discard() || s.Value(at("B2")).Kind != Empty || s.CanRedo() {70 t.Errorf("discard: B2 %v, redo %v", s.Value(at("B2")), s.CanRedo())71 }72 if len(s.InvalidIn(rect("B1:B3"))) != 0 {73 t.Error("still invalid after discarding")74 }75}7677// Cut on one sheet and pasted on another, the cells take their rules78// along: they leave the source's rules, the cells they land on lose79// theirs, and a moved formula reads the cells it read, naming the sheet80// it came from.81func TestRulesMoveToAnotherSheet(t *testing.T) {82 s := New()83 for r := range 4 {84 s.Set(Addr{Row: r}, "5")85 }86 mustAdd(t, s, CondFormat{Ranges: ranges("A1:A4"), Op: RuleGreater, Args: [2]string{"2"}, Style: green})87 mustAdd(t, s, CondFormat{Ranges: ranges("A1:A4"), Op: RuleFormula, Args: [2]string{"=$B1>0"}, Style: green})88 if err := s.AddValidation(Validation{Ranges: ranges("A2:A3"), Kind: ValidList, Items: []string{"5"}}); err != nil {89 t.Fatal(err)90 }91 other, err := s.Book().AddSheet("Other", 1)92 if err != nil {93 t.Fatal(err)94 }95 mustAdd(t, other, CondFormat{Ranges: ranges("C1:C2"), Op: RuleEmpty, Style: green})96 mustAdd(t, other, CondFormat{Ranges: ranges("D1"), Op: RuleEqual, Args: [2]string{"=C1"}, Style: green})97 if _, err := s.MoveTo(other, rect("A2:A3"), at("C1")); err != nil {98 t.Fatal(err)99 }100 fs := s.CondFormats()101 if len(fs) != 2 || RangesText(fs[0].Ranges) != "A1,A4" || RangesText(fs[1].Ranges) != "A1,A4" || fs[1].Args[0] != "=$B1>0" {102 t.Errorf("source formats %+v", fs)103 }104 if len(s.Validations()) != 0 {105 t.Errorf("source validations %+v", s.Validations())106 }107 ofs := other.CondFormats()108 if len(ofs) != 3 {109 t.Fatalf("destination formats %+v", ofs)110 }111 if RangesText(ofs[0].Ranges) != "D1" || RangesText(ofs[1].Ranges) != "C1:C2" || ofs[1].Op != RuleGreater {112 t.Errorf("destination %s %s", RangesText(ofs[0].Ranges), RangesText(ofs[1].Ranges))113 }114 if ofs[2].Args[0] != "=Sheet1!$B2>0" {115 t.Errorf("moved formula %q", ofs[2].Args[0])116 }117 if vs := other.Validations(); len(vs) != 1 || RangesText(vs[0].Ranges) != "C1:C2" {118 t.Errorf("destination validations %+v", vs)119 }120 if !other.Look(at("C1")).Styled {121 t.Error("the moved rule doesn't draw")122 }123 s.Book().Undo()124 if len(s.CondFormats()) != 2 || RangesText(s.CondFormats()[0].Ranges) != "A1:A4" || len(other.CondFormats()) != 2 || len(s.Validations()) != 1 {125 t.Errorf("undo: %+v %+v", s.CondFormats(), other.CondFormats())126 }127}