aboutsummaryrefslogtreecommitdiff
path: root/internal/ui/table_test.go
blob: 170af5c4913aee3224a79b196d2d812b6dc31d89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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 TestTableLongTitleStaysWithinBorder(t *testing.T) {
	// A title longer than the column-derived interior must be truncated, not
	// allowed to overhang the border.
	tt := &Table{
		Title:   "What would you like to smelt?",
		Columns: []string{"", "Bronze bar"},
		Rows:    [][]string{{"1", "Bronze bar"}},
	}
	renderWidths(t, tt, true, 0)
}

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])
	}
}