internal/control/table_test.go

e2a32d5f8d59e4213571c602bd9009b6c8fa86ed
gitbay/internal/control/table_test.go history · blame · raw

110 lines · 3589 bytes

  1package control
  2
  3import (
  4	"bytes"
  5	"strings"
  6	"testing"
  7	"time"
  8)
  9
 10func fixtureTable(c *Ctx, w *bytes.Buffer) {
 11	tb := c.table(w, "#", "STATE", "TITLE", "AUTHOR")
 12	tb.row(cRef("#252"), cState("open"), cFlex("Dependency updates available for every module"), cText("gitbay-bot"))
 13	tb.row(cRef("#12"), cState("closed"), cFlex("Android app"), cText("cmc"))
 14	tb.flush()
 15}
 16
 17func TestTablePlainIsTabs(t *testing.T) {
 18	var b bytes.Buffer
 19	fixtureTable(&Ctx{}, &b)
 20	want := "#252\topen\tDependency updates available for every module\tgitbay-bot\n" +
 21		"#12\tclosed\tAndroid app\tcmc\n"
 22	if b.String() != want {
 23		t.Errorf("plain:\n%q\nwant\n%q", b.String(), want)
 24	}
 25}
 26
 27func TestTableTerminalFits(t *testing.T) {
 28	var b bytes.Buffer
 29	fixtureTable(&Ctx{Term: Term{Cols: 40}}, &b)
 30	want := "#     STATE   TITLE           AUTHOR\n" +
 31		"#252  open    Dependency up…  gitbay-bot\n" +
 32		"#12   closed  Android app     cmc\n"
 33	if b.String() != want {
 34		t.Errorf("terminal:\n%s\nwant\n%s", b.String(), want)
 35	}
 36}
 37
 38func TestTableColourOnlyAddsSGR(t *testing.T) {
 39	var mono, colour bytes.Buffer
 40	fixtureTable(&Ctx{Term: Term{Cols: 40}}, &mono)
 41	fixtureTable(&Ctx{Term: Term{Cols: 40, Color: true}}, &colour)
 42	if !strings.Contains(colour.String(), sgrGreen+"open"+sgrReset) {
 43		t.Errorf("open not green: %q", colour.String())
 44	}
 45	if !strings.HasPrefix(colour.String(), sgrDim) {
 46		t.Errorf("header not dim: %q", colour.String())
 47	}
 48	if stripSGR(colour.String()) != mono.String() {
 49		t.Errorf("colour changed the layout:\n%s\nvs\n%s", stripSGR(colour.String()), mono.String())
 50	}
 51}
 52
 53func TestTableAgesAndPlainStamps(t *testing.T) {
 54	termNow = func() time.Time { return time.Date(2026, 9, 23, 12, 0, 1, 0, time.UTC) }
 55	t.Cleanup(func() { termNow = time.Now })
 56	var plain, term bytes.Buffer
 57	for _, c := range []struct {
 58		ctx *Ctx
 59		w   *bytes.Buffer
 60	}{{&Ctx{}, &plain}, {&Ctx{Term: Term{Cols: 80}}, &term}} {
 61		tb := c.ctx.table(c.w, "#", "UPDATED")
 62		tb.row(cRef("#1"), cAge("2026-09-23T10:00:00.123Z"))
 63		tb.flush()
 64	}
 65	if plain.String() != "#1\t2026-09-23T10:00:00Z\n" {
 66		t.Errorf("plain = %q", plain.String())
 67	}
 68	if term.String() != "#   UPDATED\n#1  2h ago\n" {
 69		t.Errorf("term = %q", term.String())
 70	}
 71}
 72
 73// A row may carry a cell beyond what the header names — repo list's
 74// trailing [archived] marker, only present on some rows. flush must
 75// still render it, not silently drop it because it falls past
 76// len(header).
 77func TestTableKeepsCellsBeyondTheHeader(t *testing.T) {
 78	var b bytes.Buffer
 79	tb := (&Ctx{Term: Term{Cols: 80}}).table(&b, "PATH", "VISIBILITY", "DESCRIPTION")
 80	tb.row(cRef("a/x"), cState("public"), cFlex("one"))
 81	tb.row(cRef("a/y"), cState("public"), cFlex("two"), cText("[archived]"))
 82	tb.flush()
 83
 84	out := b.String()
 85	if !strings.Contains(out, "[archived]") {
 86		t.Fatalf("archived marker dropped:\n%s", out)
 87	}
 88	lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
 89	if len(lines) != 3 {
 90		t.Fatalf("want 3 lines, got %d:\n%s", len(lines), out)
 91	}
 92	header, row1, row2 := lines[0], lines[1], lines[2]
 93	descAt := strings.Index(header, "DESCRIPTION")
 94	oneAt := strings.Index(row1, "one")
 95	twoAt := strings.Index(row2, "two")
 96	if descAt < 0 || oneAt < 0 || twoAt < 0 {
 97		t.Fatalf("columns not found:\n%s", out)
 98	}
 99	if descAt != oneAt || descAt != twoAt {
100		t.Errorf("DESCRIPTION column not aligned: header at %d, row1 at %d, row2 at %d\n%s", descAt, oneAt, twoAt, out)
101	}
102}
103
104func TestTableEmptyPrintsNothing(t *testing.T) {
105	var b bytes.Buffer
106	(&Ctx{Term: Term{Cols: 80}}).table(&b, "#").flush()
107	if b.Len() != 0 {
108		t.Errorf("empty table printed %q", b.String())
109	}
110}