package ui import ( "strings" "testing" "thehouseoficarus/internal/color" ) func renderWidths(t *testing.T, tt *Table, unicode bool, maxWidth int) []string { t.Helper() lines := tt.Render(unicode, maxWidth) for _, l := range lines { if vl := color.VisibleLen(l); maxWidth > 0 && vl > maxWidth+1 { t.Errorf("line exceeds maxWidth %d: %d (%q)", maxWidth, vl, l) } } // All border/content lines must be the same visible width so the box // lines up. widths := map[int]bool{} for _, l := range lines { widths[color.VisibleLen(l)] = true } if len(widths) > 1 { var ws []string for w := range widths { ws = append(ws, string(rune('0'+w))) } t.Errorf("inconsistent line widths: %v", strings.Join(ws, ",")) } return lines } func TestTableFitsWithinWidth(t *testing.T) { tt := &Table{ Title: "Commands", Columns: []string{"Command", "Type", "Description"}, Rows: [][]string{ {"north / south / east / west / ne / nw / se / sw / up / down", "Active", "Move in a direction"}, {"bet", "Active", "Place a casino wager (e.g. bet 100 on banker)"}, {"attack / kill", "Active", "Attack a mob"}, }, } for _, w := range []int{0, 40, 80, 100} { lines := renderWidths(t, tt, true, w) // Description column must never be crushed: with three columns the // description always keeps at least a fair share of the budget. if w == 80 { // lines: border, title, sep, header, sep, rows... if !strings.Contains(lines[5], "Move i") { t.Errorf("description column crushed at width 80: %q", lines[5]) } } } } func TestTableNoMutation(t *testing.T) { tt := &Table{ Title: "T", Columns: []string{"A", "B"}, Rows: [][]string{{"very long cell content here", "x"}}, } before := tt.Columns[0] _ = tt.Render(true, 20) if tt.Columns[0] != before { t.Errorf("Render mutated table columns: %q", tt.Columns[0]) } } func TestTableASCIIFallback(t *testing.T) { tt := &Table{Title: "T", Columns: []string{"A", "B"}, Rows: [][]string{{"1", "2"}}} lines := renderWidths(t, tt, false, 0) if !strings.Contains(lines[0], "+") { t.Errorf("expected ASCII border, got %q", lines[0]) } } func TestTableColoredCellWidth(t *testing.T) { // ANSI-colored cells must be measured and padded by visible length. red := "\x1b[31m" reset := "\x1b[0m" tt := &Table{Columns: []string{"A", "B"}, Rows: [][]string{{red + "hi" + reset, "x"}}} lines := renderWidths(t, tt, true, 0) // lines: top border, header, sep, data row, bottom border if len(lines) < 5 { t.Fatalf("expected 5 lines, got %d: %q", len(lines), lines) } if !strings.Contains(lines[3], "hi") || !strings.Contains(lines[3], "x") { t.Errorf("unexpected row: %q", lines[3]) } }