1// Copyright 2014 Oleku Konko All rights reserved.
2// Use of this source code is governed by a MIT
3// license that can be found in the LICENSE file.
4
5// This module is a Table Writer  API for the Go Programming Language.
6// The protocols were written in pure Go and works on windows and unix systems
7
8package tablewriter
9
10import (
11	"bytes"
12	"fmt"
13	"io"
14	"os"
15	"strings"
16	"testing"
17
18	"github.com/mattn/go-runewidth"
19)
20
21func ExampleShort() {
22
23	data := [][]string{
24		[]string{"A", "The Good", "500"},
25		[]string{"B", "The Very very Bad Man", "288"},
26		[]string{"C", "The Ugly", "120"},
27		[]string{"D", "The Gopher", "800"},
28	}
29
30	table := NewWriter(os.Stdout)
31	table.SetHeader([]string{"Name", "Sign", "Rating"})
32
33	for _, v := range data {
34		table.Append(v)
35	}
36	table.Render()
37
38}
39
40func ExampleLong() {
41
42	data := [][]string{
43		[]string{"Learn East has computers with adapted keyboards with enlarged print etc", "  Some Data  ", " Another Data"},
44		[]string{"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"},
45	}
46
47	table := NewWriter(os.Stdout)
48	table.SetHeader([]string{"Name", "Sign", "Rating"})
49	table.SetCenterSeparator("*")
50	table.SetRowSeparator("=")
51
52	for _, v := range data {
53		table.Append(v)
54	}
55	table.Render()
56
57}
58
59func ExampleCSV() {
60	table, _ := NewCSV(os.Stdout, "test.csv", true)
61	table.SetCenterSeparator("*")
62	table.SetRowSeparator("=")
63
64	table.Render()
65}
66
67func TestCSVInfo(t *testing.T) {
68	table, err := NewCSV(os.Stdout, "test_info.csv", true)
69	if err != nil {
70		t.Error(err)
71		return
72	}
73	table.SetAlignment(ALIGN_LEFT)
74	table.SetBorder(false)
75	table.Render()
76}
77
78func TestCSVSeparator(t *testing.T) {
79	table, err := NewCSV(os.Stdout, "test.csv", true)
80	if err != nil {
81		t.Error(err)
82		return
83	}
84	table.SetRowLine(true)
85	if runewidth.IsEastAsian() {
86		table.SetCenterSeparator("*")
87		table.SetColumnSeparator("‡")
88	} else {
89		table.SetCenterSeparator("*")
90		table.SetColumnSeparator("‡")
91	}
92	table.SetRowSeparator("-")
93	table.SetAlignment(ALIGN_LEFT)
94	table.Render()
95}
96
97func TestNoBorder(t *testing.T) {
98	data := [][]string{
99		[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
100		[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
101		[]string{"", "    (empty)\n    (empty)", "", ""},
102		[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
103		[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
104		[]string{"1/4/2014", "    (Discount)", "2233", "-$1.00"},
105	}
106
107	var buf bytes.Buffer
108	table := NewWriter(&buf)
109	table.SetAutoWrapText(false)
110	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
111	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
112	table.SetBorder(false)                                // Set Border to false
113	table.AppendBulk(data)                                // Add Bulk Data
114	table.Render()
115
116	want := `    DATE   |       DESCRIPTION        |  CV2  | AMOUNT
117+----------+--------------------------+-------+---------+
118  1/1/2014 | Domain name              |  2233 | $10.98
119  1/1/2014 | January Hosting          |  2233 | $54.95
120           |     (empty)              |       |
121           |     (empty)              |       |
122  1/4/2014 | February Hosting         |  2233 | $51.00
123  1/4/2014 | February Extra Bandwidth |  2233 | $30.00
124  1/4/2014 |     (Discount)           |  2233 | -$1.00
125+----------+--------------------------+-------+---------+
126                                        TOTAL | $145 93
127                                      +-------+---------+
128`
129	got := buf.String()
130	if got != want {
131		t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
132	}
133}
134
135func TestWithBorder(t *testing.T) {
136	data := [][]string{
137		[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
138		[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
139		[]string{"", "    (empty)\n    (empty)", "", ""},
140		[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
141		[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
142		[]string{"1/4/2014", "    (Discount)", "2233", "-$1.00"},
143	}
144
145	var buf bytes.Buffer
146	table := NewWriter(&buf)
147	table.SetAutoWrapText(false)
148	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
149	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
150	table.AppendBulk(data)                                // Add Bulk Data
151	table.Render()
152
153	want := `+----------+--------------------------+-------+---------+
154|   DATE   |       DESCRIPTION        |  CV2  | AMOUNT  |
155+----------+--------------------------+-------+---------+
156| 1/1/2014 | Domain name              |  2233 | $10.98  |
157| 1/1/2014 | January Hosting          |  2233 | $54.95  |
158|          |     (empty)              |       |         |
159|          |     (empty)              |       |         |
160| 1/4/2014 | February Hosting         |  2233 | $51.00  |
161| 1/4/2014 | February Extra Bandwidth |  2233 | $30.00  |
162| 1/4/2014 |     (Discount)           |  2233 | -$1.00  |
163+----------+--------------------------+-------+---------+
164|                                       TOTAL | $145 93 |
165+----------+--------------------------+-------+---------+
166`
167	got := buf.String()
168	if got != want {
169		t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
170	}
171}
172
173func TestPrintingInMarkdown(t *testing.T) {
174	fmt.Println("TESTING")
175	data := [][]string{
176		[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
177		[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
178		[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
179		[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
180	}
181
182	var buf bytes.Buffer
183	table := NewWriter(&buf)
184	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
185	table.AppendBulk(data) // Add Bulk Data
186	table.SetBorders(Border{Left: true, Top: false, Right: true, Bottom: false})
187	table.SetCenterSeparator("|")
188	table.Render()
189
190	want := `|   DATE   |       DESCRIPTION        | CV2  | AMOUNT |
191|----------|--------------------------|------|--------|
192| 1/1/2014 | Domain name              | 2233 | $10.98 |
193| 1/1/2014 | January Hosting          | 2233 | $54.95 |
194| 1/4/2014 | February Hosting         | 2233 | $51.00 |
195| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
196`
197	got := buf.String()
198	if got != want {
199		t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
200	}
201}
202
203func TestPrintHeading(t *testing.T) {
204	var buf bytes.Buffer
205	table := NewWriter(&buf)
206	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
207	table.printHeading()
208	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
209+---+---+---+---+---+---+---+---+---+---+---+---+
210`
211	got := buf.String()
212	if got != want {
213		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
214	}
215}
216
217func TestPrintHeadingWithoutAutoFormat(t *testing.T) {
218	var buf bytes.Buffer
219	table := NewWriter(&buf)
220	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
221	table.SetAutoFormatHeaders(false)
222	table.printHeading()
223	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
224+---+---+---+---+---+---+---+---+---+---+---+---+
225`
226	got := buf.String()
227	if got != want {
228		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
229	}
230}
231
232func TestPrintFooter(t *testing.T) {
233	var buf bytes.Buffer
234	table := NewWriter(&buf)
235	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
236	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
237	table.printFooter()
238	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
239+---+---+---+---+---+---+---+---+---+---+---+---+
240`
241	got := buf.String()
242	if got != want {
243		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
244	}
245}
246
247func TestPrintFooterWithoutAutoFormat(t *testing.T) {
248	var buf bytes.Buffer
249	table := NewWriter(&buf)
250	table.SetAutoFormatHeaders(false)
251	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
252	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
253	table.printFooter()
254	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
255+---+---+---+---+---+---+---+---+---+---+---+---+
256`
257	got := buf.String()
258	if got != want {
259		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
260	}
261}
262
263func TestPrintTableWithAndWithoutAutoWrap(t *testing.T) {
264	var buf bytes.Buffer
265	var multiline = `A multiline
266string with some lines being really long.`
267
268	with := NewWriter(&buf)
269	with.Append([]string{multiline})
270	with.Render()
271	want := `+--------------------------------+
272| A multiline string with some   |
273| lines being really long.       |
274+--------------------------------+
275`
276	got := buf.String()
277	if got != want {
278		t.Errorf("multiline text rendering with wrapping failed\ngot:\n%s\nwant:\n%s\n", got, want)
279	}
280
281	buf.Truncate(0)
282	without := NewWriter(&buf)
283	without.SetAutoWrapText(false)
284	without.Append([]string{multiline})
285	without.Render()
286	want = `+-------------------------------------------+
287| A multiline                               |
288| string with some lines being really long. |
289+-------------------------------------------+
290`
291	got = buf.String()
292	if got != want {
293		t.Errorf("multiline text rendering without wrapping rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
294	}
295}
296
297func TestPrintLine(t *testing.T) {
298	header := make([]string, 12)
299	val := " "
300	want := ""
301	for i := range header {
302		header[i] = val
303		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
304		val = val + " "
305	}
306	want = want + "+"
307	var buf bytes.Buffer
308	table := NewWriter(&buf)
309	table.SetHeader(header)
310	table.printLine(false)
311	got := buf.String()
312	if got != want {
313		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
314	}
315}
316
317func TestAnsiStrip(t *testing.T) {
318	header := make([]string, 12)
319	val := " "
320	want := ""
321	for i := range header {
322		header[i] = "\033[43;30m" + val + "\033[00m"
323		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
324		val = val + " "
325	}
326	want = want + "+"
327	var buf bytes.Buffer
328	table := NewWriter(&buf)
329	table.SetHeader(header)
330	table.printLine(false)
331	got := buf.String()
332	if got != want {
333		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
334	}
335}
336
337func NewCustomizedTable(out io.Writer) *Table {
338	table := NewWriter(out)
339	table.SetCenterSeparator("")
340	table.SetColumnSeparator("")
341	table.SetRowSeparator("")
342	table.SetBorder(false)
343	table.SetAlignment(ALIGN_LEFT)
344	table.SetHeader([]string{})
345	return table
346}
347
348func TestSubclass(t *testing.T) {
349	buf := new(bytes.Buffer)
350	table := NewCustomizedTable(buf)
351
352	data := [][]string{
353		[]string{"A", "The Good", "500"},
354		[]string{"B", "The Very very Bad Man", "288"},
355		[]string{"C", "The Ugly", "120"},
356		[]string{"D", "The Gopher", "800"},
357	}
358
359	for _, v := range data {
360		table.Append(v)
361	}
362	table.Render()
363
364	output := string(buf.Bytes())
365	want := `  A  The Good               500
366  B  The Very very Bad Man  288
367  C  The Ugly               120
368  D  The Gopher             800
369`
370	if output != want {
371		t.Error(fmt.Sprintf("Unexpected output '%v' != '%v'", output, want))
372	}
373}
374
375func TestAutoMergeRows(t *testing.T) {
376	data := [][]string{
377		[]string{"A", "The Good", "500"},
378		[]string{"A", "The Very very Bad Man", "288"},
379		[]string{"B", "The Very very Bad Man", "120"},
380		[]string{"B", "The Very very Bad Man", "200"},
381	}
382	var buf bytes.Buffer
383	table := NewWriter(&buf)
384	table.SetHeader([]string{"Name", "Sign", "Rating"})
385
386	for _, v := range data {
387		table.Append(v)
388	}
389	table.SetAutoMergeCells(true)
390	table.Render()
391	want := `+------+-----------------------+--------+
392| NAME |         SIGN          | RATING |
393+------+-----------------------+--------+
394| A    | The Good              |    500 |
395|      | The Very very Bad Man |    288 |
396| B    |                       |    120 |
397|      |                       |    200 |
398+------+-----------------------+--------+
399`
400	got := buf.String()
401	if got != want {
402		t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want)
403	}
404
405	buf.Reset()
406	table = NewWriter(&buf)
407	table.SetHeader([]string{"Name", "Sign", "Rating"})
408
409	for _, v := range data {
410		table.Append(v)
411	}
412	table.SetAutoMergeCells(true)
413	table.SetRowLine(true)
414	table.Render()
415	want = `+------+-----------------------+--------+
416| NAME |         SIGN          | RATING |
417+------+-----------------------+--------+
418| A    | The Good              |    500 |
419+      +-----------------------+--------+
420|      | The Very very Bad Man |    288 |
421+------+                       +--------+
422| B    |                       |    120 |
423+      +                       +--------+
424|      |                       |    200 |
425+------+-----------------------+--------+
426`
427	got = buf.String()
428	if got != want {
429		t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want)
430	}
431
432	buf.Reset()
433	table = NewWriter(&buf)
434	table.SetHeader([]string{"Name", "Sign", "Rating"})
435
436	dataWithlongText := [][]string{
437		[]string{"A", "The Good", "500"},
438		[]string{"A", "The Very very very very very Bad Man", "288"},
439		[]string{"B", "The Very very very very very Bad Man", "120"},
440		[]string{"C", "The Very very Bad Man", "200"},
441	}
442	table.AppendBulk(dataWithlongText)
443	table.SetAutoMergeCells(true)
444	table.SetRowLine(true)
445	table.Render()
446	want = `+------+--------------------------------+--------+
447| NAME |              SIGN              | RATING |
448+------+--------------------------------+--------+
449| A    | The Good                       |    500 |
450+------+--------------------------------+--------+
451| A    | The Very very very very very   |    288 |
452|      | Bad Man                        |        |
453+------+                                +--------+
454| B    |                                |    120 |
455|      |                                |        |
456+------+--------------------------------+--------+
457| C    | The Very very Bad Man          |    200 |
458+------+--------------------------------+--------+
459`
460	got = buf.String()
461	if got != want {
462		t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want)
463	}
464}
465