1 package table
2
3 import (
4 "fmt"
5 "sort"
6 "strings"
7 "testing"
8
9 "github.com/jedib0t/go-pretty/v6/text"
10 "github.com/stretchr/testify/assert"
11 )
12
13 func compareOutput(t *testing.T, out string, expectedOut string) {
14 assert.Equal(t, expectedOut, out)
15 if out != expectedOut {
16 fmt.Println(out)
17 }
18 }
19
20 func generateColumnConfigsWithHiddenColumns(colsToHide []int) []ColumnConfig {
21 cc := []ColumnConfig{
22 {
23 Name: "#",
24 Transformer: func(val interface{}) string {
25 return fmt.Sprint(val.(int) + 7)
26 },
27 }, {
28 Name: "First Name",
29 Transformer: func(val interface{}) string {
30 return fmt.Sprintf(">>%s", val)
31 },
32 }, {
33 Name: "Last Name",
34 Transformer: func(val interface{}) string {
35 return fmt.Sprintf("%s<<", val)
36 },
37 }, {
38 Name: "Salary",
39 Transformer: func(val interface{}) string {
40 return fmt.Sprint(val.(int) + 13)
41 },
42 }, {
43 Number: 5,
44 Transformer: func(val interface{}) string {
45 return fmt.Sprintf("~%s~", val)
46 },
47 },
48 }
49 for _, colToHide := range colsToHide {
50 cc[colToHide].Hidden = true
51 }
52 return cc
53 }
54
55 func TestTable_Render(t *testing.T) {
56 tw := NewWriter()
57 tw.AppendHeader(testHeader)
58 tw.AppendRows(testRows)
59 tw.AppendRow(testRowMultiLine)
60 tw.AppendFooter(testFooter)
61 tw.SetCaption(testCaption)
62 tw.SetStyle(styleTest)
63 tw.SetTitle(testTitle2)
64
65 compareOutput(t, tw.Render(), `(---------------------------------------------------------------------)
66 [<When you play the Game of Thrones, you win or you die. There is no >]
67 [<middle ground. >]
68 {-----^------------^-----------^--------^-----------------------------}
69 [< #>|<FIRST NAME>|<LAST NAME>|<SALARY>|< >]
70 {-----+------------+-----------+--------+-----------------------------}
71 [< 1>|<Arya >|<Stark >|< 3000>|< >]
72 [< 20>|<Jon >|<Snow >|< 2000>|<You know nothing, Jon Snow!>]
73 [<300>|<Tyrion >|<Lannister>|< 5000>|< >]
74 [< 0>|<Winter >|<Is >|< 0>|<Coming. >]
75 [< >|< >|< >|< >|<The North Remembers! >]
76 [< >|< >|< >|< >|<This is known. >]
77 {-----+------------+-----------+--------+-----------------------------}
78 [< >|< >|<TOTAL >|< 10000>|< >]
79 \-----v------------v-----------v--------v-----------------------------/
80 A Song of Ice and Fire`)
81 }
82
83 func TestTable_Render_AutoIndex(t *testing.T) {
84 tw := NewWriter()
85 for rowIdx := 0; rowIdx < 10; rowIdx++ {
86 row := make(Row, 10)
87 for colIdx := 0; colIdx < 10; colIdx++ {
88 row[colIdx] = fmt.Sprintf("%s%d", AutoIndexColumnID(colIdx), rowIdx+1)
89 }
90 tw.AppendRow(row)
91 }
92 tw.SetAutoIndex(true)
93 tw.SetStyle(StyleLight)
94
95 compareOutput(t, tw.Render(), `┌────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
96 │ │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │
97 ├────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤
98 │ 1 │ A1 │ B1 │ C1 │ D1 │ E1 │ F1 │ G1 │ H1 │ I1 │ J1 │
99 │ 2 │ A2 │ B2 │ C2 │ D2 │ E2 │ F2 │ G2 │ H2 │ I2 │ J2 │
100 │ 3 │ A3 │ B3 │ C3 │ D3 │ E3 │ F3 │ G3 │ H3 │ I3 │ J3 │
101 │ 4 │ A4 │ B4 │ C4 │ D4 │ E4 │ F4 │ G4 │ H4 │ I4 │ J4 │
102 │ 5 │ A5 │ B5 │ C5 │ D5 │ E5 │ F5 │ G5 │ H5 │ I5 │ J5 │
103 │ 6 │ A6 │ B6 │ C6 │ D6 │ E6 │ F6 │ G6 │ H6 │ I6 │ J6 │
104 │ 7 │ A7 │ B7 │ C7 │ D7 │ E7 │ F7 │ G7 │ H7 │ I7 │ J7 │
105 │ 8 │ A8 │ B8 │ C8 │ D8 │ E8 │ F8 │ G8 │ H8 │ I8 │ J8 │
106 │ 9 │ A9 │ B9 │ C9 │ D9 │ E9 │ F9 │ G9 │ H9 │ I9 │ J9 │
107 │ 10 │ A10 │ B10 │ C10 │ D10 │ E10 │ F10 │ G10 │ H10 │ I10 │ J10 │
108 └────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘`)
109 }
110
111 func TestTable_Render_AutoMerge(t *testing.T) {
112 rowConfigAutoMerge := RowConfig{AutoMerge: true}
113
114 tw := NewWriter()
115 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rowConfigAutoMerge)
116 tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"})
117 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rowConfigAutoMerge)
118 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rowConfigAutoMerge)
119 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rowConfigAutoMerge)
120 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rowConfigAutoMerge)
121 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rowConfigAutoMerge)
122 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rowConfigAutoMerge)
123 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rowConfigAutoMerge)
124 tw.AppendFooter(Row{"", "", "", 7, 5, 3})
125 tw.SetAutoIndex(true)
126 tw.SetColumnConfigs([]ColumnConfig{
127 {Number: 1, AutoMerge: true},
128 {Number: 2, AutoMerge: true},
129 {Number: 3, AutoMerge: true},
130 {Number: 4, AutoMerge: true},
131 {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
132 {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
133 })
134 tw.SetStyle(StyleLight)
135 tw.Style().Options.SeparateRows = true
136
137 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────────┬───────────┬───────────┐
138 │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
139 │ │ │ │ │ ├─────┬─────┤
140 │ │ │ │ │ │ EXE │ RUN │
141 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
142 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
143 ├───┤ │ │ ├───────────┼─────┬─────┤
144 │ 2 │ │ │ │ C 2 │ Y │ N │
145 ├───┤ │ ├───────────┼───────────┼─────┴─────┤
146 │ 3 │ │ │ NS 1B │ C 3 │ N │
147 ├───┤ ├────────┼───────────┼───────────┼───────────┤
148 │ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │
149 ├───┤ │ │ ├───────────┼─────┬─────┤
150 │ 5 │ │ │ │ C 5 │ Y │ N │
151 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
152 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
153 ├───┤ │ │ ├───────────┼───────────┤
154 │ 7 │ │ │ │ C 7 │ Y │
155 ├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
156 │ │ │ │ │ 7 │ 5 │ 3 │
157 └───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`)
158 }
159
160 func TestTable_Render_AutoMerge_Complex(t *testing.T) {
161 tw := NewWriter()
162 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE", "ID"}, RowConfig{AutoMerge: true})
163 tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN", ""})
164 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y", 123}, RowConfig{AutoMerge: true})
165 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N", 234})
166 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N", 345})
167 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N", 456}, RowConfig{AutoMerge: true})
168 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N", 567})
169 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y", 678}, RowConfig{AutoMerge: true})
170 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y", 789}, RowConfig{AutoMerge: true})
171 tw.AppendFooter(Row{"", "", "", 7, 5, 5}, RowConfig{AutoMerge: true})
172 tw.AppendFooter(Row{"", "", "", 7, 5, 3}, RowConfig{AutoMerge: true})
173 tw.AppendFooter(Row{"", "", "", 7, 5, 5}, RowConfig{AutoMerge: true})
174 tw.AppendFooter(Row{"", "", "", 7, 5, 3}, RowConfig{AutoMerge: true})
175 tw.AppendFooter(Row{"", "", "", 7, 5, 5}, RowConfig{AutoMerge: true})
176 tw.SetAutoIndex(true)
177 tw.SetColumnConfigs([]ColumnConfig{
178 {Number: 1, AutoMerge: true},
179 {Number: 2, AutoMerge: true},
180 {Number: 3, AutoMerge: true},
181 {Number: 4, AutoMerge: true},
182 {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
183 {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
184 })
185 tw.SetStyle(StyleLight)
186 tw.Style().Options.SeparateRows = true
187
188 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────────┬───────────┬───────────┬─────┐
189 │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ ID │
190 │ │ │ │ │ ├─────┬─────┼─────┤
191 │ │ │ │ │ │ EXE │ RUN │ │
192 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤
193 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ 123 │
194 ├───┤ │ │ ├───────────┼─────┬─────┼─────┤
195 │ 2 │ │ │ │ C 2 │ Y │ N │ 234 │
196 ├───┤ │ ├───────────┼───────────┼─────┼─────┼─────┤
197 │ 3 │ │ │ NS 1B │ C 3 │ N │ N │ 345 │
198 ├───┤ ├────────┼───────────┼───────────┼─────┴─────┼─────┤
199 │ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ 456 │
200 ├───┤ │ │ ├───────────┼─────┬─────┼─────┤
201 │ 5 │ │ │ │ C 5 │ Y │ N │ 567 │
202 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤
203 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ 678 │
204 ├───┤ │ │ ├───────────┼───────────┼─────┤
205 │ 7 │ │ │ │ C 7 │ Y │ 789 │
206 ├───┼─────────┴────────┴───────────┼───────────┼───────────┼─────┤
207 │ │ │ 7 │ 5 │ │
208 │ │ │ ├─────┬─────┼─────┤
209 │ │ │ │ 5 │ 3 │ │
210 │ │ │ ├─────┴─────┼─────┤
211 │ │ │ │ 5 │ │
212 │ │ │ ├─────┬─────┼─────┤
213 │ │ │ │ 5 │ 3 │ │
214 │ │ │ ├─────┴─────┼─────┤
215 │ │ │ │ 5 │ │
216 └───┴──────────────────────────────┴───────────┴───────────┴─────┘`)
217 }
218
219 func TestTable_Render_AutoMerge_Complex2(t *testing.T) {
220 tw := NewWriter()
221 tw.AppendRow(Row{"1.1\n1.1", "1.2\n1.2", "1.3\n1.3", " ", "2.1\n2.1", "2.2\n2.2", "2.3\n2.3"})
222 tw.AppendRow(Row{"1.4\n1.4", "1.5\n1.5", "1.6\n1.6", " ", "2.4\n2.4", "2.5\n2.5", "2.6\n2.6"})
223 tw.AppendRow(Row{"1.7\n1.7", "1.8\n1.8", "1.9\n0.1", "0.2\n0.2", "2.7\n0.3", "2.8\n2.8", "2.9\n2.9"})
224 tw.AppendRow(Row{" ", " ", "0.4\n0.4", "0.5\n0.5", "0.6\n0.6", " ", " "}, RowConfig{AutoMerge: true})
225 tw.AppendRow(Row{"3.1\n3.1", "3.2\n3.2", "3.3\n0.7", "0.8\n0.8", "4.1\n0.9", "4.2\n4.2", "4.3\n4.3"})
226 tw.AppendRow(Row{"3.4\n3.4", "3.5\n3.5", "3.6\n3.6", " ", "4.4\n4.4", "4.5\n4.5", "4.6\n4.6"})
227 tw.AppendRow(Row{"3.7\n3.7", "3.8\n3.8", "3.9\n3.9", " ", "4.7\n4.7", "4.8\n4.8", "4.9\n4.9"})
228 tw.SetColumnConfigs([]ColumnConfig{
229 {Number: 4, AutoMerge: true},
230 })
231 tw.SetStyle(StyleLight)
232 tw.Style().Box.PaddingLeft = ""
233 tw.Style().Box.PaddingRight = ""
234 tw.Style().Options.DrawBorder = true
235 tw.Style().Options.SeparateRows = true
236 tw.Style().Options.SeparateColumns = true
237
238 compareOutput(t, tw.Render(), `┌───┬───┬───┬───┬───┬───┬───┐
239 │1.1│1.2│1.3│ │2.1│2.2│2.3│
240 │1.1│1.2│1.3│ │2.1│2.2│2.3│
241 ├───┼───┼───┤ ├───┼───┼───┤
242 │1.4│1.5│1.6│ │2.4│2.5│2.6│
243 │1.4│1.5│1.6│ │2.4│2.5│2.6│
244 ├───┼───┼───┼───┼───┼───┼───┤
245 │1.7│1.8│1.9│0.2│2.7│2.8│2.9│
246 │1.7│1.8│0.1│0.2│0.3│2.8│2.9│
247 ├───┴───┼───┼───┼───┼───┴───┤
248 │ │0.4│0.5│0.6│ │
249 │ │0.4│0.5│0.6│ │
250 ├───┬───┼───┼───┼───┼───┬───┤
251 │3.1│3.2│3.3│0.8│4.1│4.2│4.3│
252 │3.1│3.2│0.7│0.8│0.9│4.2│4.3│
253 ├───┼───┼───┼───┼───┼───┼───┤
254 │3.4│3.5│3.6│ │4.4│4.5│4.6│
255 │3.4│3.5│3.6│ │4.4│4.5│4.6│
256 ├───┼───┼───┤ ├───┼───┼───┤
257 │3.7│3.8│3.9│ │4.7│4.8│4.9│
258 │3.7│3.8│3.9│ │4.7│4.8│4.9│
259 └───┴───┴───┴───┴───┴───┴───┘`)
260 }
261
262 func TestTable_Render_AutoMerge_ColumnsOnly(t *testing.T) {
263 tw := NewWriter()
264 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"})
265 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"})
266 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
267 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
268 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"})
269 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
270 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"})
271 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"})
272 tw.AppendFooter(Row{"", "", "", 7, 5, 3})
273 tw.SetAutoIndex(true)
274 tw.SetColumnConfigs([]ColumnConfig{
275 {Number: 1, AutoMerge: true},
276 {Number: 2, AutoMerge: true},
277 {Number: 3, AutoMerge: true},
278 {Number: 4, AutoMerge: true},
279 {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
280 {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
281 })
282 tw.SetStyle(StyleLight)
283 tw.Style().Options.SeparateRows = true
284
285 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────────┬───────────┬─────┬─────┐
286 │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ RCE │
287 │ │ │ │ │ │ EXE │ RUN │
288 ├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤
289 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ Y │
290 ├───┤ │ │ ├───────────┼─────┼─────┤
291 │ 2 │ │ │ │ C 2 │ Y │ N │
292 ├───┤ │ ├───────────┼───────────┼─────┼─────┤
293 │ 3 │ │ │ NS 1B │ C 3 │ N │ N │
294 ├───┤ ├────────┼───────────┼───────────┼─────┼─────┤
295 │ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ N │
296 ├───┤ │ │ ├───────────┼─────┼─────┤
297 │ 5 │ │ │ │ C 5 │ Y │ N │
298 ├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤
299 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ Y │
300 ├───┤ │ │ ├───────────┼─────┼─────┤
301 │ 7 │ │ │ │ C 7 │ Y │ Y │
302 ├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤
303 │ │ │ │ │ 7 │ 5 │ 3 │
304 └───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`)
305 }
306
307 func TestTable_Render_AutoMerge_RowsOnly(t *testing.T) {
308 tw := NewWriter()
309 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, RowConfig{AutoMerge: true})
310 tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"})
311 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, RowConfig{AutoMerge: true})
312 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
313 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
314 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, RowConfig{AutoMerge: true})
315 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
316 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, RowConfig{AutoMerge: true})
317 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight})
318 tw.AppendFooter(Row{"", "", "", 7, 5, 3})
319 tw.SetAutoIndex(true)
320 tw.SetColumnConfigs([]ColumnConfig{
321 {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
322 {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
323 })
324 tw.SetStyle(StyleLight)
325 tw.Style().Options.SeparateRows = true
326
327 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────────┬───────────┬───────────┐
328 │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
329 │ ├─────────┼────────┼───────────┼───────────┼─────┬─────┤
330 │ │ │ │ │ │ EXE │ RUN │
331 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
332 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
333 ├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
334 │ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │
335 ├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤
336 │ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │
337 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
338 │ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │
339 ├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
340 │ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │
341 ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
342 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
343 ├───┼─────────┼────────┼───────────┼───────────┼───────────┤
344 │ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │
345 ├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
346 │ │ │ │ │ 7 │ 5 │ 3 │
347 └───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`)
348 }
349
350 func TestTable_Render_AutoMerge_NoHeaderFooter(t *testing.T) {
351 tw := NewWriter()
352 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, RowConfig{AutoMerge: true})
353 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
354 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
355 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, RowConfig{AutoMerge: true})
356 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
357 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, RowConfig{AutoMerge: true})
358 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight})
359 tw.SetColumnConfigs([]ColumnConfig{
360 {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter},
361 {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter},
362 })
363 tw.SetStyle(StyleLight)
364 tw.Style().Options.SeparateRows = true
365
366 compareOutput(t, tw.Render(), `┌─────────┬────────┬───────┬─────┬───────┐
367 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
368 ├─────────┼────────┼───────┼─────┼───┬───┤
369 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │
370 ├─────────┼────────┼───────┼─────┼───┼───┤
371 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │
372 ├─────────┼────────┼───────┼─────┼───┴───┤
373 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │
374 ├─────────┼────────┼───────┼─────┼───┬───┤
375 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │
376 ├─────────┼────────┼───────┼─────┼───┴───┤
377 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
378 ├─────────┼────────┼───────┼─────┼───────┤
379 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │
380 └─────────┴────────┴───────┴─────┴───────┘`)
381 }
382
383 func TestTable_Render_AutoMerge_NoHeaderFooter_AutoIndex(t *testing.T) {
384 tw := NewWriter()
385 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, RowConfig{AutoMerge: true})
386 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
387 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
388 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, RowConfig{AutoMerge: true})
389 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
390 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, RowConfig{AutoMerge: true})
391 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight})
392 tw.SetAutoIndex(true)
393 tw.SetColumnConfigs([]ColumnConfig{
394 {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter},
395 {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter},
396 })
397 tw.SetStyle(StyleLight)
398 tw.Style().Options.SeparateRows = true
399
400 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────┬─────┬───┬───┐
401 │ │ A │ B │ C │ D │ E │ F │
402 ├───┼─────────┼────────┼───────┼─────┼───┴───┤
403 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
404 ├───┼─────────┼────────┼───────┼─────┼───┬───┤
405 │ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │
406 ├───┼─────────┼────────┼───────┼─────┼───┼───┤
407 │ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │
408 ├───┼─────────┼────────┼───────┼─────┼───┴───┤
409 │ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │
410 ├───┼─────────┼────────┼───────┼─────┼───┬───┤
411 │ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │
412 ├───┼─────────┼────────┼───────┼─────┼───┴───┤
413 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
414 ├───┼─────────┼────────┼───────┼─────┼───────┤
415 │ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │
416 └───┴─────────┴────────┴───────┴─────┴───────┘`)
417 }
418
419 func TestTable_Render_AutoMerge_WithHiddenRows(t *testing.T) {
420 tw := NewWriter()
421 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"})
422 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"})
423 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
424 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
425 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "Y", "Y"})
426 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
427 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"})
428 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "N"})
429 tw.AppendFooter(Row{"", "", "", 7, 5, 3})
430 tw.SetColumnConfigs([]ColumnConfig{
431 {Number: 1, AutoMerge: true},
432 {Number: 2, AutoMerge: true},
433 {Number: 3, AutoMerge: true},
434 {Number: 4, Hidden: true},
435 {Number: 5, Hidden: true, Align: text.AlignCenter},
436 {Number: 6, Hidden: true, Align: text.AlignCenter},
437 })
438 tw.SetStyle(StyleLight)
439 tw.Style().Options.SeparateRows = true
440
441 compareOutput(t, tw.Render(), `┌─────────┬────────┬───────────┐
442 │ NODE IP │ PODS │ NAMESPACE │
443 ├─────────┼────────┼───────────┤
444 │ 1.1.1.1 │ Pod 1A │ NS 1A │
445 │ │ │ │
446 │ │ │ │
447 │ │ ├───────────┤
448 │ │ │ NS 1B │
449 │ ├────────┼───────────┤
450 │ │ Pod 1B │ NS 2 │
451 │ │ │ │
452 │ │ │ │
453 ├─────────┼────────┼───────────┤
454 │ 2.2.2.2 │ Pod 2 │ NS 3 │
455 │ │ │ │
456 │ │ │ │
457 ├─────────┼────────┼───────────┤
458 │ │ │ │
459 └─────────┴────────┴───────────┘`)
460 }
461
462 func TestTable_Render_AutoMerge_Wrapped(t *testing.T) {
463 tw := NewWriter()
464 tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, RowConfig{AutoMerge: true})
465 tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE"}, RowConfig{AutoMerge: true})
466 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, RowConfig{AutoMerge: true})
467 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"})
468 tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"})
469 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, RowConfig{AutoMerge: true})
470 tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"})
471 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, RowConfig{AutoMerge: true})
472 tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true})
473 tw.AppendFooter(Row{"", "", "", 7, 5, 3}, RowConfig{AutoMerge: true})
474 tw.AppendFooter(Row{"", "", "", 6, 4, 4}, RowConfig{AutoMerge: true})
475 tw.SetAutoIndex(true)
476 tw.SetColumnConfigs([]ColumnConfig{
477 {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard},
478 {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard},
479 })
480 tw.SetStyle(StyleLight)
481 tw.Style().Options.SeparateRows = true
482
483 compareOutput(t, tw.Render(), `┌───┬─────────┬────────┬───────────┬───────────┬───────────────────┐
484 │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
485 │ ├─────────┴────────┴───────────┴───────────┼───────────────────┤
486 │ │ │ EXE EXE │
487 │ │ │ EXE │
488 ├───┼─────────┬────────┬───────────┬───────────┼───────────────────┤
489 │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
490 ├───┼─────────┼────────┼───────────┼───────────┼─────────┬─────────┤
491 │ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │
492 ├───┼─────────┼────────┼───────────┼───────────┼─────────┼─────────┤
493 │ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │
494 ├───┼─────────┼────────┼───────────┼───────────┼─────────┴─────────┤
495 │ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │
496 ├───┼─────────┼────────┼───────────┼───────────┼─────────┬─────────┤
497 │ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │
498 ├───┼─────────┼────────┼───────────┼───────────┼─────────┴─────────┤
499 │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
500 ├───┼─────────┼────────┼───────────┼───────────┼───────────────────┤
501 │ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │
502 ├───┼─────────┴────────┴───────────┼───────────┼─────────┬─────────┤
503 │ │ │ 7 │ 5 │ 3 │
504 │ ├──────────────────────────────┼───────────┼─────────┴─────────┤
505 │ │ │ 6 │ 4 │
506 └───┴──────────────────────────────┴───────────┴───────────────────┘`)
507 }
508
509 func TestTable_Render_BiDiText(t *testing.T) {
510 table := Table{}
511 table.AppendHeader(Row{"תאריך", "סכום", "מחלקה", "תגים"})
512 table.AppendRow(Row{"2020-01-01", 5.0, "מחלקה1", []string{"תג1", "תג2"}})
513 table.AppendRow(Row{"2021-02-01", 5.0, "מחלקה1", []string{"תג1"}})
514 table.AppendRow(Row{"2022-03-01", 5.0, "מחלקה2", []string{"תג1"}})
515 table.AppendFooter(Row{"סהכ", 30})
516 table.SetAutoIndex(true)
517
518
519 compareOutput(t, table.Render(), `+---+------------+------+--------+-----------+
520 | | תאריך | סכום | מחלקה | תגים |
521 +---+------------+------+--------+-----------+
522 | 1 | 2020-01-01 | 5 | מחלקה1 | [תג1 תג2] |
523 | 2 | 2021-02-01 | 5 | מחלקה1 | [תג1] |
524 | 3 | 2022-03-01 | 5 | מחלקה2 | [תג1] |
525 +---+------------+------+--------+-----------+
526 | | סהכ | 30 | | |
527 +---+------------+------+--------+-----------+`)
528
529 table.Style().Format.Direction = text.LeftToRight
530 compareOutput(t, table.Render(), `+---+------------+------+--------+-----------+
531 | | תאריך | סכום | מחלקה | תגים |
532 +---+------------+------+--------+-----------+
533 | 1 | 2020-01-01 | 5 | מחלקה1 | [תג1 תג2] |
534 | 2 | 2021-02-01 | 5 | מחלקה1 | [תג1] |
535 | 3 | 2022-03-01 | 5 | מחלקה2 | [תג1] |
536 +---+------------+------+--------+-----------+
537 | | סהכ | 30 | | |
538 +---+------------+------+--------+-----------+`)
539
540 table.Style().Format.Direction = text.RightToLeft
541 compareOutput(t, table.Render(), `+---+------------+------+--------+-----------+
542 | | תאריך | סכום | מחלקה | תגים |
543 +---+------------+------+--------+-----------+
544 | 1 | 2020-01-01 | 5 | מחלקה1 | [תג1 תג2] |
545 | 2 | 2021-02-01 | 5 | מחלקה1 | [תג1] |
546 | 3 | 2022-03-01 | 5 | מחלקה2 | [תג1] |
547 +---+------------+------+--------+-----------+
548 | | סהכ | 30 | | |
549 +---+------------+------+--------+-----------+`)
550 }
551
552 func TestTable_Render_BorderAndSeparators(t *testing.T) {
553 table := Table{}
554 table.AppendHeader(testHeader)
555 table.AppendRows(testRows)
556 table.AppendFooter(testFooter)
557 compareOutput(t, table.Render(), `+-----+------------+-----------+--------+-----------------------------+
558 | # | FIRST NAME | LAST NAME | SALARY | |
559 +-----+------------+-----------+--------+-----------------------------+
560 | 1 | Arya | Stark | 3000 | |
561 | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
562 | 300 | Tyrion | Lannister | 5000 | |
563 +-----+------------+-----------+--------+-----------------------------+
564 | | | TOTAL | 10000 | |
565 +-----+------------+-----------+--------+-----------------------------+`)
566
567 table.Style().Options = OptionsNoBorders
568 compareOutput(t, table.Render(), ` # | FIRST NAME | LAST NAME | SALARY |
569 -----+------------+-----------+--------+-----------------------------
570 1 | Arya | Stark | 3000 |
571 20 | Jon | Snow | 2000 | You know nothing, Jon Snow!
572 300 | Tyrion | Lannister | 5000 |
573 -----+------------+-----------+--------+-----------------------------
574 | | TOTAL | 10000 | `)
575
576 table.Style().Options.SeparateColumns = false
577 compareOutput(t, table.Render(), ` # FIRST NAME LAST NAME SALARY
578 -----------------------------------------------------------------
579 1 Arya Stark 3000
580 20 Jon Snow 2000 You know nothing, Jon Snow!
581 300 Tyrion Lannister 5000
582 -----------------------------------------------------------------
583 TOTAL 10000 `)
584
585 table.Style().Options.SeparateFooter = false
586 compareOutput(t, table.Render(), ` # FIRST NAME LAST NAME SALARY
587 -----------------------------------------------------------------
588 1 Arya Stark 3000
589 20 Jon Snow 2000 You know nothing, Jon Snow!
590 300 Tyrion Lannister 5000
591 TOTAL 10000 `)
592
593 table.Style().Options = OptionsNoBordersAndSeparators
594 compareOutput(t, table.Render(), ` # FIRST NAME LAST NAME SALARY
595 1 Arya Stark 3000
596 20 Jon Snow 2000 You know nothing, Jon Snow!
597 300 Tyrion Lannister 5000
598 TOTAL 10000 `)
599
600 table.Style().Options.DrawBorder = true
601 compareOutput(t, table.Render(), `+-----------------------------------------------------------------+
602 | # FIRST NAME LAST NAME SALARY |
603 | 1 Arya Stark 3000 |
604 | 20 Jon Snow 2000 You know nothing, Jon Snow! |
605 | 300 Tyrion Lannister 5000 |
606 | TOTAL 10000 |
607 +-----------------------------------------------------------------+`)
608
609 table.Style().Options.SeparateFooter = true
610 compareOutput(t, table.Render(), `+-----------------------------------------------------------------+
611 | # FIRST NAME LAST NAME SALARY |
612 | 1 Arya Stark 3000 |
613 | 20 Jon Snow 2000 You know nothing, Jon Snow! |
614 | 300 Tyrion Lannister 5000 |
615 +-----------------------------------------------------------------+
616 | TOTAL 10000 |
617 +-----------------------------------------------------------------+`)
618
619 table.Style().Options.SeparateHeader = true
620 compareOutput(t, table.Render(), `+-----------------------------------------------------------------+
621 | # FIRST NAME LAST NAME SALARY |
622 +-----------------------------------------------------------------+
623 | 1 Arya Stark 3000 |
624 | 20 Jon Snow 2000 You know nothing, Jon Snow! |
625 | 300 Tyrion Lannister 5000 |
626 +-----------------------------------------------------------------+
627 | TOTAL 10000 |
628 +-----------------------------------------------------------------+`)
629
630 table.Style().Options.SeparateRows = true
631 compareOutput(t, table.Render(), `+-----------------------------------------------------------------+
632 | # FIRST NAME LAST NAME SALARY |
633 +-----------------------------------------------------------------+
634 | 1 Arya Stark 3000 |
635 +-----------------------------------------------------------------+
636 | 20 Jon Snow 2000 You know nothing, Jon Snow! |
637 +-----------------------------------------------------------------+
638 | 300 Tyrion Lannister 5000 |
639 +-----------------------------------------------------------------+
640 | TOTAL 10000 |
641 +-----------------------------------------------------------------+`)
642
643 table.Style().Options.SeparateColumns = true
644 compareOutput(t, table.Render(), `+-----+------------+-----------+--------+-----------------------------+
645 | # | FIRST NAME | LAST NAME | SALARY | |
646 +-----+------------+-----------+--------+-----------------------------+
647 | 1 | Arya | Stark | 3000 | |
648 +-----+------------+-----------+--------+-----------------------------+
649 | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
650 +-----+------------+-----------+--------+-----------------------------+
651 | 300 | Tyrion | Lannister | 5000 | |
652 +-----+------------+-----------+--------+-----------------------------+
653 | | | TOTAL | 10000 | |
654 +-----+------------+-----------+--------+-----------------------------+`)
655 }
656
657 func TestTable_Render_Colored(t *testing.T) {
658 tw := NewWriter()
659 tw.AppendHeader(testHeader)
660 tw.AppendRows(testRows)
661 tw.AppendRow(testRowMultiLine)
662 tw.AppendFooter(testFooter)
663 tw.SetAutoIndex(true)
664 tw.SetStyle(StyleColoredBright)
665 tw.Style().Options.DrawBorder = true
666 tw.Style().Options.SeparateColumns = true
667 tw.Style().Options.SeparateFooter = true
668 tw.Style().Options.SeparateHeader = true
669 tw.Style().Options.SeparateRows = true
670
671 expectedOut := []string{
672 "\x1b[106;30m+\x1b[0m\x1b[106;30m---\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m------------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m--------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----------------------------\x1b[0m\x1b[106;30m+\x1b[0m",
673 "\x1b[106;30m|\x1b[0m\x1b[106;30m \x1b[0m\x1b[106;30m|\x1b[0m\x1b[106;30m # \x1b[0m\x1b[106;30m|\x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m|\x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m|\x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m|\x1b[0m\x1b[106;30m \x1b[0m\x1b[106;30m|\x1b[0m",
674 "\x1b[106;30m+\x1b[0m\x1b[106;30m---\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m------------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m--------\x1b[0m\x1b[106;30m+\x1b[0m\x1b[106;30m-----------------------------\x1b[0m\x1b[106;30m+\x1b[0m",
675 "\x1b[106;30m|\x1b[0m\x1b[106;30m 1 \x1b[0m\x1b[106;30m|\x1b[0m\x1b[107;30m 1 \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m \x1b[0m\x1b[106;30m|\x1b[0m",
676 "\x1b[106;30m+\x1b[0m\x1b[106;30m---\x1b[0m\x1b[106;30m+\x1b[0m\x1b[107;30m-----\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m------------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m-----------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m--------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m-----------------------------\x1b[0m\x1b[106;30m+\x1b[0m",
677 "\x1b[106;30m|\x1b[0m\x1b[106;30m 2 \x1b[0m\x1b[106;30m|\x1b[0m\x1b[47;30m 20 \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\x1b[106;30m|\x1b[0m",
678 "\x1b[106;30m+\x1b[0m\x1b[106;30m---\x1b[0m\x1b[106;30m+\x1b[0m\x1b[47;30m-----\x1b[0m\x1b[47;30m+\x1b[0m\x1b[47;30m------------\x1b[0m\x1b[47;30m+\x1b[0m\x1b[47;30m-----------\x1b[0m\x1b[47;30m+\x1b[0m\x1b[47;30m--------\x1b[0m\x1b[47;30m+\x1b[0m\x1b[47;30m-----------------------------\x1b[0m\x1b[106;30m+\x1b[0m",
679 "\x1b[106;30m|\x1b[0m\x1b[106;30m 3 \x1b[0m\x1b[106;30m|\x1b[0m\x1b[107;30m 300 \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m|\x1b[0m\x1b[107;30m \x1b[0m\x1b[106;30m|\x1b[0m",
680 "\x1b[106;30m+\x1b[0m\x1b[106;30m---\x1b[0m\x1b[106;30m+\x1b[0m\x1b[107;30m-----\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m------------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m-----------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m--------\x1b[0m\x1b[107;30m+\x1b[0m\x1b[107;30m-----------------------------\x1b[0m\x1b[106;30m+\x1b[0m",
681 "\x1b[106;30m|\x1b[0m\x1b[106;30m 4 \x1b[0m\x1b[106;30m|\x1b[0m\x1b[47;30m 0 \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m Winter \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m Is \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m 0 \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m Coming. \x1b[0m\x1b[106;30m|\x1b[0m",
682 "\x1b[106;30m|\x1b[0m\x1b[106;30m \x1b[0m\x1b[106;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m The North Remembers! \x1b[0m\x1b[106;30m|\x1b[0m",
683 "\x1b[106;30m|\x1b[0m\x1b[106;30m \x1b[0m\x1b[106;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m \x1b[0m\x1b[47;30m|\x1b[0m\x1b[47;30m This is known. \x1b[0m\x1b[106;30m|\x1b[0m",
684 "\x1b[46;30m+\x1b[0m\x1b[46;30m---\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m------------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m--------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----------------------------\x1b[0m\x1b[46;30m+\x1b[0m",
685 "\x1b[46;30m|\x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m|\x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m|\x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m|\x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m|\x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m|\x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m|\x1b[0m",
686 "\x1b[46;30m+\x1b[0m\x1b[46;30m---\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m------------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m--------\x1b[0m\x1b[46;30m+\x1b[0m\x1b[46;30m-----------------------------\x1b[0m\x1b[46;30m+\x1b[0m",
687 }
688 out := tw.Render()
689 assert.Equal(t, strings.Join(expectedOut, "\n"), out)
690 if strings.Join(expectedOut, "\n") != out {
691 for _, line := range strings.Split(out, "\n") {
692 fmt.Printf("%#v,\n", line)
693 }
694 }
695 }
696
697 func TestTable_Render_ColoredCustom(t *testing.T) {
698 tw := NewWriter()
699 tw.AppendHeader(testHeader)
700 tw.AppendRows(testRows)
701 tw.AppendRow(testRowMultiLine)
702 tw.AppendFooter(testFooter)
703 tw.SetCaption(testCaption)
704 tw.SetColumnConfigs([]ColumnConfig{
705 {
706 Name: "#",
707 Colors: testColor,
708 ColorsHeader: testColorHiRedBold,
709 }, {
710 Name: "First Name",
711 Colors: testColor,
712 ColorsHeader: testColorHiRedBold,
713 }, {
714 Name: "Last Name",
715 Colors: testColor,
716 ColorsHeader: testColorHiRedBold,
717 ColorsFooter: testColorHiBlueBold,
718 }, {
719 Name: "Salary",
720 Colors: testColor,
721 ColorsHeader: testColorHiRedBold,
722 ColorsFooter: testColorHiBlueBold,
723 }, {
724 Number: 5,
725 Colors: text.Colors{text.FgCyan},
726 },
727 })
728 tw.SetStyle(StyleRounded)
729
730 expectedOut := []string{
731 "╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮",
732 "│\x1b[91;1m # \x1b[0m│\x1b[91;1m FIRST NAME \x1b[0m│\x1b[91;1m LAST NAME \x1b[0m│\x1b[91;1m SALARY \x1b[0m│ │",
733 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
734 "│\x1b[32m 1 \x1b[0m│\x1b[32m Arya \x1b[0m│\x1b[32m Stark \x1b[0m│\x1b[32m 3000 \x1b[0m│\x1b[36m \x1b[0m│",
735 "│\x1b[32m 20 \x1b[0m│\x1b[32m Jon \x1b[0m│\x1b[32m Snow \x1b[0m│\x1b[32m 2000 \x1b[0m│\x1b[36m You know nothing, Jon Snow! \x1b[0m│",
736 "│\x1b[32m 300 \x1b[0m│\x1b[32m Tyrion \x1b[0m│\x1b[32m Lannister \x1b[0m│\x1b[32m 5000 \x1b[0m│\x1b[36m \x1b[0m│",
737 "│\x1b[32m 0 \x1b[0m│\x1b[32m Winter \x1b[0m│\x1b[32m Is \x1b[0m│\x1b[32m 0 \x1b[0m│\x1b[36m Coming. \x1b[0m│",
738 "│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[36m The North Remembers! \x1b[0m│",
739 "│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[32m \x1b[0m│\x1b[36m This is known. \x1b[0m│",
740 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
741 "│ │ │\x1b[94;1m TOTAL \x1b[0m│\x1b[94;1m 10000 \x1b[0m│ │",
742 "╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯",
743 "A Song of Ice and Fire",
744 }
745 assert.Equal(t, strings.Join(expectedOut, "\n"), tw.Render())
746 }
747
748 func TestTable_Render_ColoredTableWithinATable(t *testing.T) {
749 table := Table{}
750 table.AppendHeader(testHeader)
751 table.AppendRows(testRows)
752 table.AppendFooter(testFooter)
753 table.SetStyle(StyleColoredBright)
754 table.SetIndexColumn(1)
755
756
757 tableOuter := Table{}
758 tableOuter.AppendRow(Row{table.Render()})
759 tableOuter.SetStyle(StyleRounded)
760
761 expectedOut := strings.Join([]string{
762 "╭───────────────────────────────────────────────────────────────────╮",
763 "│ \x1b[106;30m # \x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m \x1b[0m │",
764 "│ \x1b[106;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m │",
765 "│ \x1b[106;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m │",
766 "│ \x1b[106;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m │",
767 "│ \x1b[46;30m \x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m \x1b[0m │",
768 "╰───────────────────────────────────────────────────────────────────╯",
769 }, "\n")
770 out := tableOuter.Render()
771 assert.Equal(t, expectedOut, out)
772
773
774
775 if expectedOut != out {
776 for _, line := range strings.Split(out, "\n") {
777 fmt.Printf("%#v,\n", line)
778 }
779 fmt.Println()
780 }
781 }
782
783 func TestTable_Render_ColoredTableWithinAColoredTable(t *testing.T) {
784 table := Table{}
785 table.AppendHeader(testHeader)
786 table.AppendRows(testRows)
787 table.AppendFooter(testFooter)
788 table.SetStyle(StyleColoredBright)
789 table.SetIndexColumn(1)
790
791
792 tableOuter := Table{}
793 tableOuter.AppendHeader(Row{"Colored Table within a Colored Table"})
794 tableOuter.AppendRow(Row{"\n" + table.Render() + "\n"})
795 tableOuter.SetColumnConfigs([]ColumnConfig{{Number: 1, AlignHeader: text.AlignCenter}})
796 tableOuter.SetStyle(StyleColoredBright)
797
798 expectedOut := strings.Join([]string{
799 "\x1b[106;30m COLORED TABLE WITHIN A COLORED TABLE \x1b[0m",
800 "\x1b[107;30m \x1b[0m",
801 "\x1b[107;30m \x1b[106;30m # \x1b[0m\x1b[107;30m\x1b[106;30m FIRST NAME \x1b[0m\x1b[107;30m\x1b[106;30m LAST NAME \x1b[0m\x1b[107;30m\x1b[106;30m SALARY \x1b[0m\x1b[107;30m\x1b[106;30m \x1b[0m\x1b[107;30m \x1b[0m",
802 "\x1b[107;30m \x1b[106;30m 1 \x1b[0m\x1b[107;30m\x1b[107;30m Arya \x1b[0m\x1b[107;30m\x1b[107;30m Stark \x1b[0m\x1b[107;30m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m\x1b[107;30m \x1b[0m\x1b[107;30m \x1b[0m",
803 "\x1b[107;30m \x1b[106;30m 20 \x1b[0m\x1b[107;30m\x1b[47;30m Jon \x1b[0m\x1b[107;30m\x1b[47;30m Snow \x1b[0m\x1b[107;30m\x1b[47;30m 2000 \x1b[0m\x1b[107;30m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\x1b[107;30m \x1b[0m",
804 "\x1b[107;30m \x1b[106;30m 300 \x1b[0m\x1b[107;30m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m\x1b[107;30m \x1b[0m\x1b[107;30m \x1b[0m",
805 "\x1b[107;30m \x1b[46;30m \x1b[0m\x1b[107;30m\x1b[46;30m \x1b[0m\x1b[107;30m\x1b[46;30m TOTAL \x1b[0m\x1b[107;30m\x1b[46;30m 10000 \x1b[0m\x1b[107;30m\x1b[46;30m \x1b[0m\x1b[107;30m \x1b[0m",
806 "\x1b[107;30m \x1b[0m",
807 }, "\n")
808 out := tableOuter.Render()
809 assert.Equal(t, expectedOut, out)
810
811
812
813 if expectedOut != out {
814 for _, line := range strings.Split(out, "\n") {
815 fmt.Printf("%#v,\n", line)
816 }
817 fmt.Println()
818 }
819 }
820
821 func TestTable_Render_ColoredStyleAutoIndex(t *testing.T) {
822 table := Table{}
823 table.AppendHeader(testHeader)
824 table.AppendRows(testRows)
825 table.AppendFooter(testFooter)
826 table.SetAutoIndex(true)
827 table.SetStyle(StyleColoredDark)
828 table.SetTitle(testTitle2)
829
830 expectedOut := strings.Join([]string{
831 "\x1b[106;30;1m When you play the Game of Thrones, you win or you die. There is no \x1b[0m",
832 "\x1b[106;30;1m middle ground. \x1b[0m",
833 "\x1b[96;100m \x1b[0m\x1b[96;100m # \x1b[0m\x1b[96;100m FIRST NAME \x1b[0m\x1b[96;100m LAST NAME \x1b[0m\x1b[96;100m SALARY \x1b[0m\x1b[96;100m \x1b[0m",
834 "\x1b[96;100m 1 \x1b[0m\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m",
835 "\x1b[96;100m 2 \x1b[0m\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m",
836 "\x1b[96;100m 3 \x1b[0m\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m",
837 "\x1b[36;100m \x1b[0m\x1b[36;100m \x1b[0m\x1b[36;100m \x1b[0m\x1b[36;100m TOTAL \x1b[0m\x1b[36;100m 10000 \x1b[0m\x1b[36;100m \x1b[0m",
838 }, "\n")
839 out := table.Render()
840 assert.Equal(t, expectedOut, out)
841
842
843
844 if expectedOut != out {
845 for _, line := range strings.Split(out, "\n") {
846 fmt.Printf("%#v,\n", line)
847 }
848 fmt.Println()
849 }
850 }
851
852 func TestTable_Render_ColumnConfigs(t *testing.T) {
853 generatePrefixTransformer := func(prefix string) text.Transformer {
854 return func(val interface{}) string {
855 return fmt.Sprintf("%s%v", prefix, val)
856 }
857 }
858 generateSuffixTransformer := func(suffix string) text.Transformer {
859 return func(val interface{}) string {
860 return fmt.Sprintf("%v%s", val, suffix)
861 }
862 }
863 salaryTransformer := text.Transformer(func(val interface{}) string {
864 if valInt, ok := val.(int); ok {
865 return fmt.Sprintf("$ %.2f", float64(valInt)+0.03)
866 }
867 return strings.Replace(fmt.Sprint(val), "ry", "riii", -1)
868 })
869
870 tw := NewWriter()
871 tw.AppendHeader(testHeaderMultiLine)
872 tw.AppendRows(testRows)
873 tw.AppendRow(testRowMultiLine)
874 tw.AppendFooter(testFooterMultiLine)
875 tw.SetAutoIndex(true)
876 tw.SetColumnConfigs([]ColumnConfig{
877 {
878 Name: fmt.Sprint(testHeaderMultiLine[1]),
879 Align: text.AlignRight,
880 AlignFooter: text.AlignRight,
881 AlignHeader: text.AlignRight,
882 Colors: text.Colors{text.BgBlack, text.FgRed},
883 ColorsHeader: text.Colors{text.BgRed, text.FgBlack, text.Bold},
884 ColorsFooter: text.Colors{text.BgRed, text.FgBlack},
885 Transformer: generatePrefixTransformer("(r_"),
886 TransformerFooter: generatePrefixTransformer("(f_"),
887 TransformerHeader: generatePrefixTransformer("(h_"),
888 VAlign: text.VAlignTop,
889 VAlignFooter: text.VAlignTop,
890 VAlignHeader: text.VAlignTop,
891 WidthMax: 10,
892 }, {
893 Name: fmt.Sprint(testHeaderMultiLine[2]),
894 Align: text.AlignLeft,
895 AlignFooter: text.AlignLeft,
896 AlignHeader: text.AlignLeft,
897 Colors: text.Colors{text.BgBlack, text.FgGreen},
898 ColorsHeader: text.Colors{text.BgGreen, text.FgBlack, text.Bold},
899 ColorsFooter: text.Colors{text.BgGreen, text.FgBlack},
900 Transformer: generateSuffixTransformer("_r)"),
901 TransformerFooter: generateSuffixTransformer("_f)"),
902 TransformerHeader: generateSuffixTransformer("_h)"),
903 VAlign: text.VAlignMiddle,
904 VAlignFooter: text.VAlignMiddle,
905 VAlignHeader: text.VAlignMiddle,
906 WidthMax: 10,
907 }, {
908 Number: 4,
909 Colors: text.Colors{text.BgBlack, text.FgBlue},
910 ColorsHeader: text.Colors{text.BgBlue, text.FgBlack, text.Bold},
911 ColorsFooter: text.Colors{text.BgBlue, text.FgBlack},
912 Transformer: salaryTransformer,
913 TransformerFooter: salaryTransformer,
914 TransformerHeader: salaryTransformer,
915 VAlign: text.VAlignBottom,
916 VAlignFooter: text.VAlignBottom,
917 VAlignHeader: text.VAlignBottom,
918 WidthMin: 16,
919 }, {
920 Name: "Non-existent Column",
921 Colors: text.Colors{text.BgYellow, text.FgHiRed},
922 },
923 })
924 tw.SetStyle(styleTest)
925
926 expectedOutLines := []string{
927 "(---^-----^-----------^------------^------------------^-----------------------------)",
928 "[< >|< #>|\x1b[41;30;1m< (H_FIRST>\x1b[0m|\x1b[42;30;1m<LAST >\x1b[0m|\x1b[44;30;1m< >\x1b[0m|< >]",
929 "[< >|< >|\x1b[41;30;1m< NAME>\x1b[0m|\x1b[42;30;1m<NAME_H) >\x1b[0m|\x1b[44;30;1m< SALARIII>\x1b[0m|< >]",
930 "{---+-----+-----------+------------+------------------+-----------------------------}",
931 "[<1>|< 1>|\x1b[40;31m< (r_Arya>\x1b[0m|\x1b[40;32m<Stark_r) >\x1b[0m|\x1b[40;34m< $ 3000.03>\x1b[0m|< >]",
932 "[<2>|< 20>|\x1b[40;31m< (r_Jon>\x1b[0m|\x1b[40;32m<Snow_r) >\x1b[0m|\x1b[40;34m< $ 2000.03>\x1b[0m|<You know nothing, Jon Snow!>]",
933 "[<3>|<300>|\x1b[40;31m<(r_Tyrion>\x1b[0m|\x1b[40;32m<Lannister_>\x1b[0m|\x1b[40;34m< >\x1b[0m|< >]",
934 "[< >|< >|\x1b[40;31m< >\x1b[0m|\x1b[40;32m<r) >\x1b[0m|\x1b[40;34m< $ 5000.03>\x1b[0m|< >]",
935 "[<4>|< 0>|\x1b[40;31m<(r_Winter>\x1b[0m|\x1b[40;32m< >\x1b[0m|\x1b[40;34m< >\x1b[0m|<Coming. >]",
936 "[< >|< >|\x1b[40;31m< >\x1b[0m|\x1b[40;32m<Is_r) >\x1b[0m|\x1b[40;34m< >\x1b[0m|<The North Remembers! >]",
937 "[< >|< >|\x1b[40;31m< >\x1b[0m|\x1b[40;32m< >\x1b[0m|\x1b[40;34m< $ 0.03>\x1b[0m|<This is known. >]",
938 "{---+-----+-----------+------------+------------------+-----------------------------}",
939 "[< >|< >|\x1b[41;30m< (F_>\x1b[0m|\x1b[42;30m<TOTAL >\x1b[0m|\x1b[44;30m< >\x1b[0m|< >]",
940 "[< >|< >|\x1b[41;30m< >\x1b[0m|\x1b[42;30m<SALARY_F) >\x1b[0m|\x1b[44;30m< $ 10000.03>\x1b[0m|< >]",
941 "\\---v-----v-----------v------------v------------------v-----------------------------/",
942 }
943 expectedOut := strings.Join(expectedOutLines, "\n")
944 assert.Equal(t, expectedOut, tw.Render())
945 }
946
947 func TestTable_Render_Empty(t *testing.T) {
948 tw := NewWriter()
949 assert.Empty(t, tw.Render())
950 }
951
952 func TestTable_Render_HiddenColumns(t *testing.T) {
953 tw := NewWriter()
954 tw.AppendHeader(testHeader)
955 tw.AppendRows(testRows)
956 tw.AppendFooter(testFooter)
957
958
959 tw.SortBy([]SortBy{
960 {Name: "Salary", Mode: DscNumeric},
961 })
962
963 t.Run("no columns hidden", func(t *testing.T) {
964 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns(nil))
965
966 compareOutput(t, tw.Render(), `+-----+------------+-------------+--------+-------------------------------+
967 | # | FIRST NAME | LAST NAME | SALARY | |
968 +-----+------------+-------------+--------+-------------------------------+
969 | 307 | >>Tyrion | Lannister<< | 5013 | |
970 | 8 | >>Arya | Stark<< | 3013 | |
971 | 27 | >>Jon | Snow<< | 2013 | ~You know nothing, Jon Snow!~ |
972 +-----+------------+-------------+--------+-------------------------------+
973 | | | TOTAL | 10000 | |
974 +-----+------------+-------------+--------+-------------------------------+`)
975 })
976
977 t.Run("every column hidden", func(t *testing.T) {
978 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{0, 1, 2, 3, 4}))
979
980 compareOutput(t, tw.Render(), "")
981 })
982
983 t.Run("some columns hidden (1)", func(t *testing.T) {
984 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{1, 2, 3, 4}))
985
986 compareOutput(t, tw.Render(), `+-----+
987 | # |
988 +-----+
989 | 307 |
990 | 8 |
991 | 27 |
992 +-----+
993 | |
994 +-----+`)
995 })
996
997 t.Run("some columns hidden (2)", func(t *testing.T) {
998 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{1, 2, 3}))
999
1000 compareOutput(t, tw.Render(), `+-----+-------------------------------+
1001 | # | |
1002 +-----+-------------------------------+
1003 | 307 | |
1004 | 8 | |
1005 | 27 | ~You know nothing, Jon Snow!~ |
1006 +-----+-------------------------------+
1007 | | |
1008 +-----+-------------------------------+`)
1009 })
1010
1011 t.Run("some columns hidden (3)", func(t *testing.T) {
1012 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{0, 4}))
1013
1014 compareOutput(t, tw.Render(), `+------------+-------------+--------+
1015 | FIRST NAME | LAST NAME | SALARY |
1016 +------------+-------------+--------+
1017 | >>Tyrion | Lannister<< | 5013 |
1018 | >>Arya | Stark<< | 3013 |
1019 | >>Jon | Snow<< | 2013 |
1020 +------------+-------------+--------+
1021 | | TOTAL | 10000 |
1022 +------------+-------------+--------+`)
1023 })
1024
1025 t.Run("first column hidden", func(t *testing.T) {
1026 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{0}))
1027
1028 compareOutput(t, tw.Render(), `+------------+-------------+--------+-------------------------------+
1029 | FIRST NAME | LAST NAME | SALARY | |
1030 +------------+-------------+--------+-------------------------------+
1031 | >>Tyrion | Lannister<< | 5013 | |
1032 | >>Arya | Stark<< | 3013 | |
1033 | >>Jon | Snow<< | 2013 | ~You know nothing, Jon Snow!~ |
1034 +------------+-------------+--------+-------------------------------+
1035 | | TOTAL | 10000 | |
1036 +------------+-------------+--------+-------------------------------+`)
1037 })
1038
1039 t.Run("column hidden in the middle", func(t *testing.T) {
1040 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{1}))
1041
1042 compareOutput(t, tw.Render(), `+-----+-------------+--------+-------------------------------+
1043 | # | LAST NAME | SALARY | |
1044 +-----+-------------+--------+-------------------------------+
1045 | 307 | Lannister<< | 5013 | |
1046 | 8 | Stark<< | 3013 | |
1047 | 27 | Snow<< | 2013 | ~You know nothing, Jon Snow!~ |
1048 +-----+-------------+--------+-------------------------------+
1049 | | TOTAL | 10000 | |
1050 +-----+-------------+--------+-------------------------------+`)
1051 })
1052
1053 t.Run("last column hidden", func(t *testing.T) {
1054 tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{4}))
1055
1056 compareOutput(t, tw.Render(), `+-----+------------+-------------+--------+
1057 | # | FIRST NAME | LAST NAME | SALARY |
1058 +-----+------------+-------------+--------+
1059 | 307 | >>Tyrion | Lannister<< | 5013 |
1060 | 8 | >>Arya | Stark<< | 3013 |
1061 | 27 | >>Jon | Snow<< | 2013 |
1062 +-----+------------+-------------+--------+
1063 | | | TOTAL | 10000 |
1064 +-----+------------+-------------+--------+`)
1065 })
1066 }
1067
1068 func TestTable_Render_Paged(t *testing.T) {
1069 tw := NewWriter()
1070 tw.AppendHeader(testHeader)
1071 tw.AppendRows(testRows)
1072 tw.AppendRow(testRowMultiLine)
1073 tw.AppendFooter(Row{"", "", "Total", 10000})
1074 tw.SetPageSize(1)
1075
1076 compareOutput(t, tw.Render(), `+-----+------------+-----------+--------+-----------------------------+
1077 | # | FIRST NAME | LAST NAME | SALARY | |
1078 +-----+------------+-----------+--------+-----------------------------+
1079 | 1 | Arya | Stark | 3000 | |
1080 +-----+------------+-----------+--------+-----------------------------+
1081 | | | TOTAL | 10000 | |
1082 +-----+------------+-----------+--------+-----------------------------+
1083
1084 +-----+------------+-----------+--------+-----------------------------+
1085 | # | FIRST NAME | LAST NAME | SALARY | |
1086 +-----+------------+-----------+--------+-----------------------------+
1087 | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
1088 +-----+------------+-----------+--------+-----------------------------+
1089 | | | TOTAL | 10000 | |
1090 +-----+------------+-----------+--------+-----------------------------+
1091
1092 +-----+------------+-----------+--------+-----------------------------+
1093 | # | FIRST NAME | LAST NAME | SALARY | |
1094 +-----+------------+-----------+--------+-----------------------------+
1095 | 300 | Tyrion | Lannister | 5000 | |
1096 +-----+------------+-----------+--------+-----------------------------+
1097 | | | TOTAL | 10000 | |
1098 +-----+------------+-----------+--------+-----------------------------+
1099
1100 +-----+------------+-----------+--------+-----------------------------+
1101 | # | FIRST NAME | LAST NAME | SALARY | |
1102 +-----+------------+-----------+--------+-----------------------------+
1103 | 0 | Winter | Is | 0 | Coming. |
1104 +-----+------------+-----------+--------+-----------------------------+
1105 | | | TOTAL | 10000 | |
1106 +-----+------------+-----------+--------+-----------------------------+
1107
1108 +-----+------------+-----------+--------+-----------------------------+
1109 | # | FIRST NAME | LAST NAME | SALARY | |
1110 +-----+------------+-----------+--------+-----------------------------+
1111 | | | | | The North Remembers! |
1112 +-----+------------+-----------+--------+-----------------------------+
1113 | | | TOTAL | 10000 | |
1114 +-----+------------+-----------+--------+-----------------------------+
1115
1116 +-----+------------+-----------+--------+-----------------------------+
1117 | # | FIRST NAME | LAST NAME | SALARY | |
1118 +-----+------------+-----------+--------+-----------------------------+
1119 | | | | | This is known. |
1120 +-----+------------+-----------+--------+-----------------------------+
1121 | | | TOTAL | 10000 | |
1122 +-----+------------+-----------+--------+-----------------------------+`)
1123 }
1124
1125 func TestTable_Render_Reset(t *testing.T) {
1126 tw := NewWriter()
1127 tw.AppendHeader(testHeader)
1128 tw.AppendRows(testRows)
1129 tw.AppendFooter(testFooter)
1130 tw.SetStyle(StyleLight)
1131
1132 compareOutput(t, tw.Render(), `┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
1133 │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
1134 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1135 │ 1 │ Arya │ Stark │ 3000 │ │
1136 │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
1137 │ 300 │ Tyrion │ Lannister │ 5000 │ │
1138 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1139 │ │ │ TOTAL │ 10000 │ │
1140 └─────┴────────────┴───────────┴────────┴─────────────────────────────┘`)
1141
1142 tw.ResetFooters()
1143 compareOutput(t, tw.Render(), `┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
1144 │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
1145 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1146 │ 1 │ Arya │ Stark │ 3000 │ │
1147 │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
1148 │ 300 │ Tyrion │ Lannister │ 5000 │ │
1149 └─────┴────────────┴───────────┴────────┴─────────────────────────────┘`)
1150
1151 tw.ResetHeaders()
1152 compareOutput(t, tw.Render(), `┌─────┬────────┬───────────┬──────┬─────────────────────────────┐
1153 │ 1 │ Arya │ Stark │ 3000 │ │
1154 │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
1155 │ 300 │ Tyrion │ Lannister │ 5000 │ │
1156 └─────┴────────┴───────────┴──────┴─────────────────────────────┘`)
1157
1158 tw.ResetRows()
1159 assert.Empty(t, tw.Render())
1160 }
1161
1162 func TestTable_Render_RowPainter(t *testing.T) {
1163 tw := NewWriter()
1164 tw.AppendHeader(testHeader)
1165 tw.AppendRows(testRows)
1166 tw.AppendRow(testRowMultiLine)
1167 tw.AppendFooter(testFooter)
1168 tw.SetIndexColumn(1)
1169 tw.SetRowPainter(RowPainter(func(row Row) text.Colors {
1170 if salary, ok := row[3].(int); ok {
1171 if salary > 3000 {
1172 return text.Colors{text.BgYellow, text.FgBlack}
1173 } else if salary < 2000 {
1174 return text.Colors{text.BgRed, text.FgBlack}
1175 }
1176 }
1177 return nil
1178 }))
1179 tw.SetStyle(StyleLight)
1180 tw.SortBy([]SortBy{{Name: "Salary", Mode: AscNumeric}})
1181
1182 expectedOutLines := []string{
1183 "┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐",
1184 "│ # │ FIRST NAME │ LAST NAME │ SALARY │ │",
1185 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
1186 "│ 0 │\x1b[41;30m Winter \x1b[0m│\x1b[41;30m Is \x1b[0m│\x1b[41;30m 0 \x1b[0m│\x1b[41;30m Coming. \x1b[0m│",
1187 "│ │\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m The North Remembers! \x1b[0m│",
1188 "│ │\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m This is known. \x1b[0m│",
1189 "│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │",
1190 "│ 1 │ Arya │ Stark │ 3000 │ │",
1191 "│ 300 │\x1b[43;30m Tyrion \x1b[0m│\x1b[43;30m Lannister \x1b[0m│\x1b[43;30m 5000 \x1b[0m│\x1b[43;30m \x1b[0m│",
1192 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
1193 "│ │ │ TOTAL │ 10000 │ │",
1194 "└─────┴────────────┴───────────┴────────┴─────────────────────────────┘",
1195 }
1196 expectedOut := strings.Join(expectedOutLines, "\n")
1197 assert.Equal(t, expectedOut, tw.Render())
1198
1199 tw.SetStyle(StyleColoredBright)
1200 tw.Style().Color.RowAlternate = tw.Style().Color.Row
1201 expectedOutLines = []string{
1202 "\x1b[106;30m # \x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m \x1b[0m",
1203 "\x1b[106;30m 0 \x1b[0m\x1b[41;30m Winter \x1b[0m\x1b[41;30m Is \x1b[0m\x1b[41;30m 0 \x1b[0m\x1b[41;30m Coming. \x1b[0m",
1204 "\x1b[106;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m The North Remembers! \x1b[0m",
1205 "\x1b[106;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m This is known. \x1b[0m",
1206 "\x1b[106;30m 20 \x1b[0m\x1b[107;30m Jon \x1b[0m\x1b[107;30m Snow \x1b[0m\x1b[107;30m 2000 \x1b[0m\x1b[107;30m You know nothing, Jon Snow! \x1b[0m",
1207 "\x1b[106;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m",
1208 "\x1b[106;30m 300 \x1b[0m\x1b[43;30m Tyrion \x1b[0m\x1b[43;30m Lannister \x1b[0m\x1b[43;30m 5000 \x1b[0m\x1b[43;30m \x1b[0m",
1209 "\x1b[46;30m \x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m \x1b[0m",
1210 }
1211 expectedOut = strings.Join(expectedOutLines, "\n")
1212 assert.Equal(t, expectedOut, tw.Render())
1213 }
1214
1215 func TestTable_Render_Sorted(t *testing.T) {
1216 tw := NewWriter()
1217 tw.AppendHeader(testHeader)
1218 tw.AppendRows(testRows)
1219 tw.AppendRow(Row{11, "Sansa", "Stark", 6000})
1220 tw.AppendFooter(testFooter)
1221 tw.SetStyle(StyleLight)
1222 tw.SortBy([]SortBy{{Name: "Last Name", Mode: Asc}, {Name: "First Name", Mode: Asc}})
1223
1224 compareOutput(t, tw.Render(), `┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
1225 │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
1226 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1227 │ 300 │ Tyrion │ Lannister │ 5000 │ │
1228 │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
1229 │ 1 │ Arya │ Stark │ 3000 │ │
1230 │ 11 │ Sansa │ Stark │ 6000 │ │
1231 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1232 │ │ │ TOTAL │ 10000 │ │
1233 └─────┴────────────┴───────────┴────────┴─────────────────────────────┘`)
1234 }
1235
1236 func TestTable_Render_Separator(t *testing.T) {
1237 tw := NewWriter()
1238 tw.AppendHeader(testHeader)
1239 tw.AppendSeparator()
1240 tw.AppendRows(testRows)
1241 tw.AppendSeparator()
1242 tw.AppendSeparator()
1243 tw.AppendRow(testRowMultiLine)
1244 tw.AppendSeparator()
1245 tw.AppendSeparator()
1246 tw.AppendSeparator()
1247 tw.AppendRow(Row{11, "Sansa", "Stark", 6000})
1248 tw.AppendSeparator()
1249 tw.AppendSeparator()
1250 tw.AppendSeparator()
1251 tw.AppendSeparator()
1252 tw.AppendFooter(testFooter)
1253 tw.SetStyle(StyleLight)
1254
1255 compareOutput(t, tw.Render(), `┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
1256 │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
1257 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1258 │ 1 │ Arya │ Stark │ 3000 │ │
1259 │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
1260 │ 300 │ Tyrion │ Lannister │ 5000 │ │
1261 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1262 │ 0 │ Winter │ Is │ 0 │ Coming. │
1263 │ │ │ │ │ The North Remembers! │
1264 │ │ │ │ │ This is known. │
1265 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1266 │ 11 │ Sansa │ Stark │ 6000 │ │
1267 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1268 │ │ │ TOTAL │ 10000 │ │
1269 └─────┴────────────┴───────────┴────────┴─────────────────────────────┘`)
1270 }
1271
1272 func TestTable_Render_Styles(t *testing.T) {
1273 tw := NewWriter()
1274 tw.AppendHeader(testHeader)
1275 tw.AppendRows(testRows)
1276 tw.AppendFooter(testFooter)
1277 tw.SetStyle(StyleLight)
1278
1279 styles := map[*Style]string{
1280 &StyleDefault: "+-----+------------+-----------+--------+-----------------------------+\n| # | FIRST NAME | LAST NAME | SALARY | |\n+-----+------------+-----------+--------+-----------------------------+\n| 1 | Arya | Stark | 3000 | |\n| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |\n| 300 | Tyrion | Lannister | 5000 | |\n+-----+------------+-----------+--------+-----------------------------+\n| | | TOTAL | 10000 | |\n+-----+------------+-----------+--------+-----------------------------+",
1281 &StyleBold: "┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃\n┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃\n┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃\n┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃\n┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃\n┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛",
1282 &StyleColoredBlackOnBlueWhite: "\x1b[104;30m # \x1b[0m\x1b[104;30m FIRST NAME \x1b[0m\x1b[104;30m LAST NAME \x1b[0m\x1b[104;30m SALARY \x1b[0m\x1b[104;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[44;30m \x1b[0m\x1b[44;30m \x1b[0m\x1b[44;30m TOTAL \x1b[0m\x1b[44;30m 10000 \x1b[0m\x1b[44;30m \x1b[0m",
1283 &StyleColoredBlackOnCyanWhite: "\x1b[106;30m # \x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[46;30m \x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m \x1b[0m",
1284 &StyleColoredBlackOnGreenWhite: "\x1b[102;30m # \x1b[0m\x1b[102;30m FIRST NAME \x1b[0m\x1b[102;30m LAST NAME \x1b[0m\x1b[102;30m SALARY \x1b[0m\x1b[102;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[42;30m \x1b[0m\x1b[42;30m \x1b[0m\x1b[42;30m TOTAL \x1b[0m\x1b[42;30m 10000 \x1b[0m\x1b[42;30m \x1b[0m",
1285 &StyleColoredBlackOnMagentaWhite: "\x1b[105;30m # \x1b[0m\x1b[105;30m FIRST NAME \x1b[0m\x1b[105;30m LAST NAME \x1b[0m\x1b[105;30m SALARY \x1b[0m\x1b[105;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[45;30m \x1b[0m\x1b[45;30m \x1b[0m\x1b[45;30m TOTAL \x1b[0m\x1b[45;30m 10000 \x1b[0m\x1b[45;30m \x1b[0m",
1286 &StyleColoredBlackOnRedWhite: "\x1b[101;30m # \x1b[0m\x1b[101;30m FIRST NAME \x1b[0m\x1b[101;30m LAST NAME \x1b[0m\x1b[101;30m SALARY \x1b[0m\x1b[101;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m TOTAL \x1b[0m\x1b[41;30m 10000 \x1b[0m\x1b[41;30m \x1b[0m",
1287 &StyleColoredBlackOnYellowWhite: "\x1b[103;30m # \x1b[0m\x1b[103;30m FIRST NAME \x1b[0m\x1b[103;30m LAST NAME \x1b[0m\x1b[103;30m SALARY \x1b[0m\x1b[103;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[43;30m \x1b[0m\x1b[43;30m \x1b[0m\x1b[43;30m TOTAL \x1b[0m\x1b[43;30m 10000 \x1b[0m\x1b[43;30m \x1b[0m",
1288 &StyleColoredBlueWhiteOnBlack: "\x1b[94;100m # \x1b[0m\x1b[94;100m FIRST NAME \x1b[0m\x1b[94;100m LAST NAME \x1b[0m\x1b[94;100m SALARY \x1b[0m\x1b[94;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[34;100m \x1b[0m\x1b[34;100m \x1b[0m\x1b[34;100m TOTAL \x1b[0m\x1b[34;100m 10000 \x1b[0m\x1b[34;100m \x1b[0m",
1289 &StyleColoredBright: "\x1b[106;30m # \x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m \x1b[0m\n\x1b[107;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[47;30m 20 \x1b[0m\x1b[47;30m Jon \x1b[0m\x1b[47;30m Snow \x1b[0m\x1b[47;30m 2000 \x1b[0m\x1b[47;30m You know nothing, Jon Snow! \x1b[0m\n\x1b[107;30m 300 \x1b[0m\x1b[107;30m Tyrion \x1b[0m\x1b[107;30m Lannister \x1b[0m\x1b[107;30m 5000 \x1b[0m\x1b[107;30m \x1b[0m\n\x1b[46;30m \x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m \x1b[0m",
1290 &StyleColoredCyanWhiteOnBlack: "\x1b[96;100m # \x1b[0m\x1b[96;100m FIRST NAME \x1b[0m\x1b[96;100m LAST NAME \x1b[0m\x1b[96;100m SALARY \x1b[0m\x1b[96;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[36;100m \x1b[0m\x1b[36;100m \x1b[0m\x1b[36;100m TOTAL \x1b[0m\x1b[36;100m 10000 \x1b[0m\x1b[36;100m \x1b[0m",
1291 &StyleColoredDark: "\x1b[96;100m # \x1b[0m\x1b[96;100m FIRST NAME \x1b[0m\x1b[96;100m LAST NAME \x1b[0m\x1b[96;100m SALARY \x1b[0m\x1b[96;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[36;100m \x1b[0m\x1b[36;100m \x1b[0m\x1b[36;100m TOTAL \x1b[0m\x1b[36;100m 10000 \x1b[0m\x1b[36;100m \x1b[0m",
1292 &StyleColoredGreenWhiteOnBlack: "\x1b[92;100m # \x1b[0m\x1b[92;100m FIRST NAME \x1b[0m\x1b[92;100m LAST NAME \x1b[0m\x1b[92;100m SALARY \x1b[0m\x1b[92;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[32;100m \x1b[0m\x1b[32;100m \x1b[0m\x1b[32;100m TOTAL \x1b[0m\x1b[32;100m 10000 \x1b[0m\x1b[32;100m \x1b[0m",
1293 &StyleColoredMagentaWhiteOnBlack: "\x1b[95;100m # \x1b[0m\x1b[95;100m FIRST NAME \x1b[0m\x1b[95;100m LAST NAME \x1b[0m\x1b[95;100m SALARY \x1b[0m\x1b[95;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[35;100m \x1b[0m\x1b[35;100m \x1b[0m\x1b[35;100m TOTAL \x1b[0m\x1b[35;100m 10000 \x1b[0m\x1b[35;100m \x1b[0m",
1294 &StyleColoredRedWhiteOnBlack: "\x1b[91;100m # \x1b[0m\x1b[91;100m FIRST NAME \x1b[0m\x1b[91;100m LAST NAME \x1b[0m\x1b[91;100m SALARY \x1b[0m\x1b[91;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[31;100m \x1b[0m\x1b[31;100m \x1b[0m\x1b[31;100m TOTAL \x1b[0m\x1b[31;100m 10000 \x1b[0m\x1b[31;100m \x1b[0m",
1295 &StyleColoredYellowWhiteOnBlack: "\x1b[93;100m # \x1b[0m\x1b[93;100m FIRST NAME \x1b[0m\x1b[93;100m LAST NAME \x1b[0m\x1b[93;100m SALARY \x1b[0m\x1b[93;100m \x1b[0m\n\x1b[97;40m 1 \x1b[0m\x1b[97;40m Arya \x1b[0m\x1b[97;40m Stark \x1b[0m\x1b[97;40m 3000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[37;40m 20 \x1b[0m\x1b[37;40m Jon \x1b[0m\x1b[37;40m Snow \x1b[0m\x1b[37;40m 2000 \x1b[0m\x1b[37;40m You know nothing, Jon Snow! \x1b[0m\n\x1b[97;40m 300 \x1b[0m\x1b[97;40m Tyrion \x1b[0m\x1b[97;40m Lannister \x1b[0m\x1b[97;40m 5000 \x1b[0m\x1b[97;40m \x1b[0m\n\x1b[33;100m \x1b[0m\x1b[33;100m \x1b[0m\x1b[33;100m TOTAL \x1b[0m\x1b[33;100m 10000 \x1b[0m\x1b[33;100m \x1b[0m",
1296 &StyleDouble: "╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗\n║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║\n╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣\n║ 1 ║ Arya ║ Stark ║ 3000 ║ ║\n║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║\n║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║\n╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣\n║ ║ ║ TOTAL ║ 10000 ║ ║\n╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝",
1297 &StyleLight: "┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐\n│ # │ FIRST NAME │ LAST NAME │ SALARY │ │\n├─────┼────────────┼───────────┼────────┼─────────────────────────────┤\n│ 1 │ Arya │ Stark │ 3000 │ │\n│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │\n│ 300 │ Tyrion │ Lannister │ 5000 │ │\n├─────┼────────────┼───────────┼────────┼─────────────────────────────┤\n│ │ │ TOTAL │ 10000 │ │\n└─────┴────────────┴───────────┴────────┴─────────────────────────────┘",
1298 &StyleRounded: "╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮\n│ # │ FIRST NAME │ LAST NAME │ SALARY │ │\n├─────┼────────────┼───────────┼────────┼─────────────────────────────┤\n│ 1 │ Arya │ Stark │ 3000 │ │\n│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │\n│ 300 │ Tyrion │ Lannister │ 5000 │ │\n├─────┼────────────┼───────────┼────────┼─────────────────────────────┤\n│ │ │ TOTAL │ 10000 │ │\n╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯",
1299 &styleTest: "(-----^------------^-----------^--------^-----------------------------)\n[< #>|<FIRST NAME>|<LAST NAME>|<SALARY>|< >]\n{-----+------------+-----------+--------+-----------------------------}\n[< 1>|<Arya >|<Stark >|< 3000>|< >]\n[< 20>|<Jon >|<Snow >|< 2000>|<You know nothing, Jon Snow!>]\n[<300>|<Tyrion >|<Lannister>|< 5000>|< >]\n{-----+------------+-----------+--------+-----------------------------}\n[< >|< >|<TOTAL >|< 10000>|< >]\n\\-----v------------v-----------v--------v-----------------------------/",
1300 }
1301 var mismatches []string
1302 for style, expectedOut := range styles {
1303 tw.SetStyle(*style)
1304 out := tw.Render()
1305 assert.Equal(t, expectedOut, out)
1306 if expectedOut != out {
1307 mismatches = append(mismatches, fmt.Sprintf("&%s: %#v,", style.Name, out))
1308 fmt.Printf("// %s renders a Table like below:\n", style.Name)
1309 for _, line := range strings.Split(out, "\n") {
1310 fmt.Printf("// %s\n", line)
1311 }
1312 fmt.Println()
1313 }
1314 }
1315 sort.Strings(mismatches)
1316 for _, mismatch := range mismatches {
1317 fmt.Println(mismatch)
1318 }
1319 }
1320
1321 func TestTable_Render_SuppressEmptyColumns(t *testing.T) {
1322 tw := NewWriter()
1323 tw.AppendHeader(testHeader)
1324 tw.AppendRows([]Row{
1325 {1, "Arya", "", 3000},
1326 {20, "Jon", "", 2000, "You know nothing, Jon Snow!"},
1327 {300, "Tyrion", "", 5000},
1328 })
1329 tw.AppendRow(Row{11, "Sansa", "", 6000})
1330 tw.AppendFooter(Row{"", "", "TOTAL", 10000})
1331 tw.SetStyle(StyleLight)
1332
1333 compareOutput(t, tw.Render(), `┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
1334 │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
1335 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1336 │ 1 │ Arya │ │ 3000 │ │
1337 │ 20 │ Jon │ │ 2000 │ You know nothing, Jon Snow! │
1338 │ 300 │ Tyrion │ │ 5000 │ │
1339 │ 11 │ Sansa │ │ 6000 │ │
1340 ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
1341 │ │ │ TOTAL │ 10000 │ │
1342 └─────┴────────────┴───────────┴────────┴─────────────────────────────┘`)
1343
1344 tw.SuppressEmptyColumns()
1345 compareOutput(t, tw.Render(), `┌─────┬────────────┬────────┬─────────────────────────────┐
1346 │ # │ FIRST NAME │ SALARY │ │
1347 ├─────┼────────────┼────────┼─────────────────────────────┤
1348 │ 1 │ Arya │ 3000 │ │
1349 │ 20 │ Jon │ 2000 │ You know nothing, Jon Snow! │
1350 │ 300 │ Tyrion │ 5000 │ │
1351 │ 11 │ Sansa │ 6000 │ │
1352 ├─────┼────────────┼────────┼─────────────────────────────┤
1353 │ │ │ 10000 │ │
1354 └─────┴────────────┴────────┴─────────────────────────────┘`)
1355 }
1356
1357 func TestTable_Render_TableWithinTable(t *testing.T) {
1358 twInner := NewWriter()
1359 twInner.AppendHeader(testHeader)
1360 twInner.AppendRows(testRows)
1361 twInner.AppendFooter(testFooter)
1362 twInner.SetStyle(StyleLight)
1363
1364 twOuter := NewWriter()
1365 twOuter.AppendHeader(Row{"Table within a Table"})
1366 twOuter.AppendRow(Row{twInner.Render()})
1367 twOuter.SetColumnConfigs([]ColumnConfig{{Number: 1, AlignHeader: text.AlignCenter}})
1368 twOuter.SetStyle(StyleDouble)
1369
1370 compareOutput(t, twOuter.Render(), `╔═════════════════════════════════════════════════════════════════════════╗
1371 ║ TABLE WITHIN A TABLE ║
1372 ╠═════════════════════════════════════════════════════════════════════════╣
1373 ║ ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ ║
1374 ║ │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ ║
1375 ║ ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ ║
1376 ║ │ 1 │ Arya │ Stark │ 3000 │ │ ║
1377 ║ │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ ║
1378 ║ │ 300 │ Tyrion │ Lannister │ 5000 │ │ ║
1379 ║ ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ ║
1380 ║ │ │ │ TOTAL │ 10000 │ │ ║
1381 ║ └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ ║
1382 ╚═════════════════════════════════════════════════════════════════════════╝`)
1383 }
1384
1385 func TestTable_Render_TableWithTransformers(t *testing.T) {
1386 bolden := func(val interface{}) string {
1387 return text.Bold.Sprint(val)
1388 }
1389 tw := NewWriter()
1390 tw.AppendHeader(testHeader)
1391 tw.AppendRows(testRows)
1392 tw.AppendFooter(testFooter)
1393 tw.SetColumnConfigs([]ColumnConfig{{
1394 Name: "Salary",
1395 Transformer: bolden,
1396 TransformerFooter: bolden,
1397 TransformerHeader: bolden,
1398 }})
1399 tw.SetStyle(StyleLight)
1400
1401 expectedOut := []string{
1402 "┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐",
1403 "│ # │ FIRST NAME │ LAST NAME │ \x1b[1mSALARY\x1b[0m │ │",
1404 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
1405 "│ 1 │ Arya │ Stark │ \x1b[1m3000\x1b[0m │ │",
1406 "│ 20 │ Jon │ Snow │ \x1b[1m2000\x1b[0m │ You know nothing, Jon Snow! │",
1407 "│ 300 │ Tyrion │ Lannister │ \x1b[1m5000\x1b[0m │ │",
1408 "├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
1409 "│ │ │ TOTAL │ \x1b[1m10000\x1b[0m │ │",
1410 "└─────┴────────────┴───────────┴────────┴─────────────────────────────┘",
1411 }
1412 out := tw.Render()
1413 assert.Equal(t, strings.Join(expectedOut, "\n"), out)
1414 if strings.Join(expectedOut, "\n") != out {
1415 for _, line := range strings.Split(out, "\n") {
1416 fmt.Printf("%#v,\n", line)
1417 }
1418 }
1419 }
1420
1421 func TestTable_Render_SetWidth_Title(t *testing.T) {
1422 tw := NewWriter()
1423 tw.AppendHeader(testHeader)
1424 tw.AppendRows(testRows)
1425 tw.AppendFooter(testFooter)
1426 tw.SetTitle("Game Of Thrones")
1427
1428 t.Run("length 20", func(t *testing.T) {
1429 tw.SetAllowedRowLength(20)
1430
1431 expectedOut := []string{
1432 "+------------------+",
1433 "| Game Of Thrones |",
1434 "+-----+----------- ~",
1435 "| # | FIRST NAME ~",
1436 "+-----+----------- ~",
1437 "| 1 | Arya ~",
1438 "| 20 | Jon ~",
1439 "| 300 | Tyrion ~",
1440 "+-----+----------- ~",
1441 "| | ~",
1442 "+-----+----------- ~",
1443 }
1444
1445 assert.Equal(t, strings.Join(expectedOut, "\n"), tw.Render())
1446 })
1447
1448 t.Run("length 30", func(t *testing.T) {
1449 tw.SetAllowedRowLength(30)
1450
1451 expectedOut := []string{
1452 "+----------------------------+",
1453 "| Game Of Thrones |",
1454 "+-----+------------+-------- ~",
1455 "| # | FIRST NAME | LAST NA ~",
1456 "+-----+------------+-------- ~",
1457 "| 1 | Arya | Stark ~",
1458 "| 20 | Jon | Snow ~",
1459 "| 300 | Tyrion | Lannist ~",
1460 "+-----+------------+-------- ~",
1461 "| | | TOTAL ~",
1462 "+-----+------------+-------- ~",
1463 }
1464
1465 assert.Equal(t, strings.Join(expectedOut, "\n"), tw.Render())
1466 })
1467 }
1468
1469 func TestTable_Render_WidthEnforcer(t *testing.T) {
1470 tw := NewWriter()
1471 tw.AppendRows([]Row{
1472 {"U2", "Hey", "2021-04-19 13:37", "Yuh yuh yuh"},
1473 {"S12", "Uhhhh", "2021-04-19 13:37", "Some dummy data here"},
1474 {"R123", "Lobsters", "2021-04-19 13:37", "I like lobsters"},
1475 {"R123", "Some big name here and it's pretty big", "2021-04-19 13:37", "Abcdefghijklmnopqrstuvwxyz"},
1476 {"R123", "Small name", "2021-04-19 13:37", "Abcdefghijklmnopqrstuvwxyz"},
1477 })
1478 tw.SetColumnConfigs([]ColumnConfig{
1479 {Number: 2, WidthMax: 20, WidthMaxEnforcer: text.Trim},
1480 })
1481
1482 expectedOut := `+------+----------------------+------------------+----------------------------+
1483 | U2 | Hey | 2021-04-19 13:37 | Yuh yuh yuh |
1484 | S12 | Uhhhh | 2021-04-19 13:37 | Some dummy data here |
1485 | R123 | Lobsters | 2021-04-19 13:37 | I like lobsters |
1486 | R123 | Some big name here a | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz |
1487 | R123 | Small name | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz |
1488 +------+----------------------+------------------+----------------------------+`
1489 actualOut := tw.Render()
1490 assert.Equal(t, expectedOut, actualOut)
1491 if expectedOut != actualOut {
1492 fmt.Println(actualOut)
1493 }
1494 }
1495
View as plain text