1package sheet23import (4 "reflect"5 "testing"6)78func TestFillSeries(t *testing.T) {9 tests := []struct {10 name string11 src []string // A1 down12 n int // cells to fill below13 want []string14 }{15 {"counting", []string{"1", "2"}, 3, []string{"3", "4", "5"}},16 {"even numbers", []string{"2", "4", "6"}, 2, []string{"8", "10"}},17 {"decimals without float noise", []string{"0.1", "0.2"}, 2, []string{"0.3", "0.4"}},18 {"linear trend", []string{"1", "2", "4", "5"}, 1, []string{"6.5"}},19 {"counting down", []string{"10", "7"}, 2, []string{"4", "1"}},20 {"a single number is copied", []string{"5"}, 2, []string{"5", "5"}},21 {"currency keeps its format", []string{"$100", "$150"}, 1, []string{"$200"}},22 {"months", []string{"Jan", "Feb"}, 2, []string{"Mar", "Apr"}},23 {"months wrap around", []string{"Nov"}, 3, []string{"Dec", "Jan", "Feb"}},24 {"long months, every other", []string{"January", "March"}, 1, []string{"May"}},25 {"May reads as long or short", []string{"Apr", "May"}, 1, []string{"Jun"}},26 {"days keep their case", []string{"MON", "TUE"}, 2, []string{"WED", "THU"}},27 {"long days", []string{"friday"}, 2, []string{"saturday", "sunday"}},28 {"text with a number", []string{"Item 1"}, 2, []string{"Item 2", "Item 3"}},29 {"text with a step", []string{"Q1", "Q3"}, 1, []string{"Q5"}},30 {"zero padding", []string{"Run 08", "Run 09"}, 2, []string{"Run 10", "Run 11"}},31 {"dates by day", []string{"9/26/2026"}, 2, []string{"9/27/2026", "9/28/2026"}},32 {"dates by week", []string{"9/26/2026", "10/3/2026"}, 1, []string{"10/10/2026"}},33 {"different days of the month go on by days", []string{"1/31/2026", "2/28/2026"}, 1, []string{"3/28/2026"}},34 {"dates on the same day by month", []string{"1/15/2026", "3/15/2026"}, 2, []string{"5/15/2026", "7/15/2026"}},35 {"plain text is copied in turn", []string{"a", "b"}, 3, []string{"a", "b", "a"}},36 {"mixed is copied", []string{"1", "x"}, 2, []string{"1", "x"}},37 {"formulas are copied with their references", []string{"=B1", "=B2*2"}, 2, []string{"=B3", "=B4*2"}},38 }39 for _, tt := range tests {40 t.Run(tt.name, func(t *testing.T) {41 s := New()42 for i, in := range tt.src {43 s.Set(Addr{Row: i}, in)44 }45 s.ClearHistory()46 src := Rect{To: Addr{Row: len(tt.src) - 1}}47 dst := Rect{To: Addr{Row: len(tt.src) + tt.n - 1}}48 got, err := s.FillSeries(src, dst)49 if err != nil || got != dst {50 t.Fatalf("FillSeries = %v, %v", got, err)51 }52 if got := column(s, "A", len(tt.src)+1, len(tt.src)+tt.n); !reflect.DeepEqual(got, tt.want) {53 t.Errorf("filled %q, want %q", got, tt.want)54 }55 s.Undo()56 if s.CanUndo() || s.Len() != len(tt.src) {57 t.Errorf("fill isn't one undo step: %v", inputs(s))58 }59 })60 }61}6263func TestFillSeriesDirections(t *testing.T) {64 s := sheetOf(t, map[string]string{"C3": "5", "C4": "6", "D3": "Mon", "D4": "Tue"})65 // Up continues backwards.66 s.FillSeries(Rect{From: at("C3"), To: at("D4")}, Rect{From: at("C1"), To: at("D4")})67 if got := column(s, "C", 1, 2); !reflect.DeepEqual(got, []string{"3", "4"}) {68 t.Errorf("up: C = %q", got)69 }70 if got := column(s, "D", 1, 2); !reflect.DeepEqual(got, []string{"Sat", "Sun"}) {71 t.Errorf("up: D = %q", got)72 }73 // Right and left, per row.74 s = sheetOf(t, map[string]string{"B1": "1", "C1": "3", "B2": "x"})75 s.FillSeries(Rect{From: at("B1"), To: at("C2")}, Rect{From: at("A1"), To: at("C2")})76 s.FillSeries(Rect{From: at("A1"), To: at("C2")}, Rect{From: at("A1"), To: at("E2")})77 want := map[string]string{"A1": "-1", "B1": "1", "C1": "3", "D1": "5", "E1": "7", "A2": "", "B2": "x", "D2": "", "E2": "x"}78 for a, in := range want {79 if got := input(s, a); got != in {80 t.Errorf("%s = %q, want %q", a, got, in)81 }82 }83}8485func input(s *Sheet, a string) string {86 if c := s.Cell(at(a)); c != nil {87 return c.Input88 }89 return ""90}9192func TestFillDownContinuesSeries(t *testing.T) {93 tests := []struct {94 name string95 cells map[string]string96 fill string97 want []string98 }{99 {"a series with blanks below continues", map[string]string{"A1": "1", "A2": "2"}, "A1:A5", []string{"1", "2", "3", "4", "5"}},100 {"one row is copied, as Sheets' Ctrl+D", map[string]string{"A1": "Item 1"}, "A1:A3", []string{"Item 1", "Item 1", "Item 1"}},101 {"filled cells below: copy the top row", map[string]string{"A1": "1", "A2": "2", "A4": "x"}, "A1:A4", []string{"1", "1", "1", "1"}},102 }103 for _, tt := range tests {104 t.Run(tt.name, func(t *testing.T) {105 s := sheetOf(t, tt.cells)106 r, _ := ParseRange(tt.fill)107 if _, err := s.FillDown(r); err != nil {108 t.Fatal(err)109 }110 if got := column(s, "A", 1, len(tt.want)); !reflect.DeepEqual(got, tt.want) {111 t.Errorf("A = %q, want %q", got, tt.want)112 }113 })114 }115 s := sheetOf(t, map[string]string{"A1": "Jan", "B1": "Feb"})116 s.FillRight(Rect{From: at("A1"), To: at("D1")})117 if got := input(s, "D1"); got != "Apr" {118 t.Errorf("fill right: D1 = %q", got)119 }120}