cat / 012
clone

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

Tests: high-contrast screens in their own file, monochrome states as separate tests

Dami committed 75ec2bb
Keeps screenlist_test.go under 500 lines and each test under the
cognitive complexity limit.
commit
75ec2bbb85d2f6988d476b4800bbb1705dc0ead2
parent
bd785aa20552c7f6e61d101b4ed703f123dc6fd2
author
Dami <dami.etoile@gmail.com>
4 files changed, +136 -130
Me2e/screenlist_test.go9-
Me2e/screensetup_test.go8-
Ae2e/screenthemes_test.go25+++
Minternal/ui/monochrome_test.go224+++++++++++++++---------------
M e2e/screenlist_test.go +0 -9
M e2e/screensetup_test.go +0 -8
A e2e/screenthemes_test.go +25 -0
M internal/ui/monochrome_test.go +111 -113
@@ -144,129 +144,127 @@ func every(cs []monoCell, text bool, f func(monoCell) bool) bool {
144144 return true145145 }146146 147-func TestMonochromeSheetStates(t *testing.T) {148- isReverse := func(c monoCell) bool { return c.reverse }149- plain := func(c monoCell) bool { return c.attrs == attrs{} }147+func isReverse(c monoCell) bool { return c.reverse }148+func plain(c monoCell) bool { return c.attrs == attrs{} }150149 151- t.Run("pointer and plain cell", func(t *testing.T) {152- m := newModel()153- m.sheet.Set(addr("B1"), "text")154- if p := cellAt(m, addr("A1")); !every(p, false, isReverse) {155- t.Errorf("the pointer isn't in reverse video: %+v", p)156- }157- if c := cellAt(m, addr("B1")); !every(c, false, plain) {158- t.Errorf("a plain cell has attributes: %+v", c)159- }160- })150+func TestMonochromePointer(t *testing.T) {151+ m := newModel()152+ m.sheet.Set(addr("B1"), "text")153+ if p := cellAt(m, addr("A1")); !every(p, false, isReverse) {154+ t.Errorf("the pointer isn't in reverse video: %+v", p)155+ }156+ if c := cellAt(m, addr("B1")); !every(c, false, plain) {157+ t.Errorf("a plain cell has attributes: %+v", c)158+ }159+}161160 162- t.Run("selection and headers", func(t *testing.T) {163- m := newModel()164- press(t, m, "<shift+right>", "<shift+down>")165- for _, a := range []string{"A1", "B1", "A2", "B2"} {166- if c := cellAt(m, addr(a)); !every(c, false, isReverse) {167- t.Errorf("%s in the selection isn't in reverse video", a)168- }169- }170- if c := cellAt(m, addr("C3")); !every(c, false, plain) {171- t.Error("a cell outside the selection has attributes")172- }173- // Column headers: the active cell's (the selection's anchor, A1)174- // bold and reversed, another selected one reversed, the rest plain.175- hdr := monoLine(m, headerLine)176- x := m.hdrW()177- col := func(c int) []monoCell { return hdr[x+c*sheet.DefaultWidth : x+(c+1)*sheet.DefaultWidth] }178- if !every(col(0), true, func(c monoCell) bool { return c.reverse && c.bold }) {179- t.Errorf("the active column's header: %+v", col(0))180- }181- if !every(col(1), true, func(c monoCell) bool { return c.reverse && !c.bold }) {182- t.Errorf("a selected column's header: %+v", col(1))183- }184- if every(col(2), true, isReverse) {185- t.Error("an unselected column's header is reversed")186- }187- if !strings.Contains(cellText(monoLine(m, formulaLine)), "A1:B2") {188- t.Error("the name box doesn't name the selection")161+func TestMonochromeSelection(t *testing.T) {162+ m := newModel()163+ press(t, m, "<shift+right>", "<shift+down>")164+ for _, a := range []string{"A1", "B1", "A2", "B2"} {165+ if c := cellAt(m, addr(a)); !every(c, false, isReverse) {166+ t.Errorf("%s in the selection isn't in reverse video", a)189167 }190- })168+ }169+ if c := cellAt(m, addr("C3")); !every(c, false, plain) {170+ t.Error("a cell outside the selection has attributes")171+ }172+ // Column headers: the active cell's (the selection's anchor, A1)173+ // bold and reversed, another selected one reversed, the rest plain.174+ hdr := monoLine(m, headerLine)175+ x := m.hdrW()176+ col := func(c int) []monoCell { return hdr[x+c*sheet.DefaultWidth : x+(c+1)*sheet.DefaultWidth] }177+ if !every(col(0), true, func(c monoCell) bool { return c.reverse && c.bold }) {178+ t.Errorf("the active column's header: %+v", col(0))179+ }180+ if !every(col(1), true, func(c monoCell) bool { return c.reverse && !c.bold }) {181+ t.Errorf("a selected column's header: %+v", col(1))182+ }183+ if every(col(2), true, isReverse) {184+ t.Error("an unselected column's header is reversed")185+ }186+ if !strings.Contains(cellText(monoLine(m, formulaLine)), "A1:B2") {187+ t.Error("the name box doesn't name the selection")188+ }189+}191190 192- t.Run("error", func(t *testing.T) {193- m := newModel()194- m.sheet.Set(addr("B1"), "=1/0")195- c := cellAt(m, addr("B1"))196- if !strings.Contains(cellText(c), "#DIV/0!") || !every(c, true, func(c monoCell) bool { return c.underline == "3" }) {197- t.Errorf("an error cell: %q %+v", cellText(c), c)198- }199- })191+func TestMonochromeError(t *testing.T) {192+ m := newModel()193+ m.sheet.Set(addr("B1"), "=1/0")194+ c := cellAt(m, addr("B1"))195+ if !strings.Contains(cellText(c), "#DIV/0!") || !every(c, true, func(c monoCell) bool { return c.underline == "3" }) {196+ t.Errorf("an error cell: %q %+v", cellText(c), c)197+ }198+}200199 201- t.Run("invalid entry and its warning", func(t *testing.T) {202- m := newModel()203- mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"]}`)204- m.sheet.Set(addr("B1"), "42")205- c := cellAt(m, addr("B1"))206- if !every(c, true, func(c monoCell) bool { return c.underline == "4" }) {207- t.Errorf("an invalid cell isn't dotted: %+v", c)208- }209- m.cur = addr("B1")210- if l := cellText(monoLine(m, contextLine)); !strings.Contains(l, "Invalid:") {211- t.Errorf("no warning in words: %q", l)212- }213- })200+func TestMonochromeInvalid(t *testing.T) {201+ m := newModel()202+ mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"]}`)203+ m.sheet.Set(addr("B1"), "42")204+ c := cellAt(m, addr("B1"))205+ if !every(c, true, func(c monoCell) bool { return c.underline == "4" }) {206+ t.Errorf("an invalid cell isn't dotted: %+v", c)207+ }208+ m.cur = addr("B1")209+ if l := cellText(monoLine(m, contextLine)); !strings.Contains(l, "Invalid:") {210+ t.Errorf("no warning in words: %q", l)211+ }212+}214213 215- t.Run("circular reference", func(t *testing.T) {216- m := newModel()217- m.sheet.Set(addr("A1"), "=A1+1")218- if l := cellText(monoLine(m, m.height-1)); !strings.Contains(l, "Circular reference") {219- t.Errorf("status line: %q", l)220- }221- })214+func TestMonochromeCircular(t *testing.T) {215+ m := newModel()216+ m.sheet.Set(addr("A1"), "=A1+1")217+ if l := cellText(monoLine(m, m.height-1)); !strings.Contains(l, "Circular reference") {218+ t.Errorf("status line: %q", l)219+ }220+}222221 223- t.Run("spill", func(t *testing.T) {224- m := newModel()225- m.sheet.Set(addr("B1"), "=SEQUENCE(3)")226- m.cur = addr("D1")227- c := cellAt(m, addr("B2"))228- if strings.TrimSpace(cellText(c)) != "2" || !every(c, true, func(c monoCell) bool { return c.italic }) {229- t.Errorf("a spilled value isn't italic: %+v", c)230- }231- m.cur = addr("B2")232- if l := cellText(monoLine(m, contextLine)); !strings.Contains(l, "Spilled from B1") {233- t.Errorf("context line: %q", l)234- }235- })222+func TestMonochromeSpill(t *testing.T) {223+ m := newModel()224+ m.sheet.Set(addr("B1"), "=SEQUENCE(3)")225+ m.cur = addr("D1")226+ c := cellAt(m, addr("B2"))227+ if strings.TrimSpace(cellText(c)) != "2" || !every(c, true, func(c monoCell) bool { return c.italic }) {228+ t.Errorf("a spilled value isn't italic: %+v", c)229+ }230+ m.cur = addr("B2")231+ if l := cellText(monoLine(m, contextLine)); !strings.Contains(l, "Spilled from B1") {232+ t.Errorf("context line: %q", l)233+ }234+}236235 237- t.Run("note", func(t *testing.T) {238- m := newModel()239- m.sheet.Set(addr("B1"), "x")240- m.sheet.SetNote(addr("B1"), "Checked")241- if c := cellAt(m, addr("B1")); c[len(c)-1].text != "▝" {242- t.Errorf("no note mark: %q", cellText(c))243- }244- })236+func TestMonochromeNote(t *testing.T) {237+ m := newModel()238+ m.sheet.Set(addr("B1"), "x")239+ m.sheet.SetNote(addr("B1"), "Checked")240+ if c := cellAt(m, addr("B1")); c[len(c)-1].text != "▝" {241+ t.Errorf("no note mark: %q", cellText(c))242+ }243+}245244 246- t.Run("found and traced", func(t *testing.T) {247- m := traceModel(t)248- m.cur = addr("B1")249- press(t, m, "<alt+,>")250- if c := cellAt(m, addr("A2")); !every(c, false, isReverse) {251- t.Errorf("a traced cell isn't reversed: %+v", c)252- }253- m = newModel()254- m.sheet.Set(addr("B2"), "Rent")255- press(t, m, "<ctrl+f>", "Rent")256- if c := cellAt(m, addr("B2")); !every(c, false, isReverse) {257- t.Errorf("a match isn't reversed: %+v", c)258- }259- })245+func TestMonochromeFoundTraced(t *testing.T) {246+ m := traceModel(t)247+ m.cur = addr("B1")248+ press(t, m, "<alt+,>")249+ if c := cellAt(m, addr("A2")); !every(c, false, isReverse) {250+ t.Errorf("a traced cell isn't reversed: %+v", c)251+ }252+ m = newModel()253+ m.sheet.Set(addr("B2"), "Rent")254+ press(t, m, "<ctrl+f>", "Rent")255+ if c := cellAt(m, addr("B2")); !every(c, false, isReverse) {256+ t.Errorf("a match isn't reversed: %+v", c)257+ }258+}260259 261- t.Run("copied", func(t *testing.T) {262- m := newModel()263- m.sheet.Set(addr("A1"), "1")264- press(t, m, "<ctrl+c>", "<right>")265- c := cellAt(m, addr("A1"))266- if !every(c, false, func(c monoCell) bool { return c.underline != "" }) || !every(c, true, func(c monoCell) bool { return c.underline == "5" }) {267- t.Errorf("the copied range isn't underlined, dashed under its text: %+v", c)268- }269- })260+func TestMonochromeCopied(t *testing.T) {261+ m := newModel()262+ m.sheet.Set(addr("A1"), "1")263+ press(t, m, "<ctrl+c>", "<right>")264+ c := cellAt(m, addr("A1"))265+ if !every(c, false, func(c monoCell) bool { return c.underline != "" }) || !every(c, true, func(c monoCell) bool { return c.underline == "5" }) {266+ t.Errorf("the copied range isn't underlined, dashed under its text: %+v", c)267+ }270268 }271269 272270 // States told in words: the mode indicator, recording, protected ranges