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