github.com/olekukonko/tablewriter@v0.0.5/table_test.go (about) 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 8 package tablewriter 9 10 import ( 11 "bytes" 12 "fmt" 13 "io" 14 "os" 15 "reflect" 16 "strings" 17 "testing" 18 ) 19 20 func checkEqual(t *testing.T, got, want interface{}, msgs ...interface{}) { 21 if !reflect.DeepEqual(got, want) { 22 buf := bytes.Buffer{} 23 buf.WriteString("got:\n[%v]\nwant:\n[%v]\n") 24 for _, v := range msgs { 25 buf.WriteString(v.(string)) 26 } 27 t.Errorf(buf.String(), got, want) 28 } 29 } 30 31 func ExampleShort() { 32 data := [][]string{ 33 {"A", "The Good", "500"}, 34 {"B", "The Very very Bad Man", "288"}, 35 {"C", "The Ugly", "120"}, 36 {"D", "The Gopher", "800"}, 37 } 38 39 table := NewWriter(os.Stdout) 40 table.SetHeader([]string{"Name", "Sign", "Rating"}) 41 42 for _, v := range data { 43 table.Append(v) 44 } 45 table.Render() 46 47 // Output: +------+-----------------------+--------+ 48 // | NAME | SIGN | RATING | 49 // +------+-----------------------+--------+ 50 // | A | The Good | 500 | 51 // | B | The Very very Bad Man | 288 | 52 // | C | The Ugly | 120 | 53 // | D | The Gopher | 800 | 54 // +------+-----------------------+--------+ 55 } 56 57 func ExampleLong() { 58 data := [][]string{ 59 {"Learn East has computers with adapted keyboards with enlarged print etc", " Some Data ", " Another Data"}, 60 {"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"}, 61 } 62 63 table := NewWriter(os.Stdout) 64 table.SetHeader([]string{"Name", "Sign", "Rating"}) 65 table.SetCenterSeparator("*") 66 table.SetRowSeparator("=") 67 68 for _, v := range data { 69 table.Append(v) 70 } 71 table.Render() 72 73 // Output: *================================*================================*===============================*==========* 74 // | NAME | SIGN | RATING | | 75 // *================================*================================*===============================*==========* 76 // | Learn East has computers | Some Data | Another Data | 77 // | with adapted keyboards with | | | 78 // | enlarged print etc | | | 79 // | Instead of lining up the | the way across, he splits the | Like most ergonomic keyboards | See Data | 80 // | letters all | keyboard in two | | | 81 // *================================*================================*===============================*==========* 82 } 83 84 func ExampleCSV() { 85 table, _ := NewCSV(os.Stdout, "testdata/test.csv", true) 86 table.SetCenterSeparator("*") 87 table.SetRowSeparator("=") 88 89 table.Render() 90 91 // Output: *============*===========*=========* 92 // | FIRST NAME | LAST NAME | SSN | 93 // *============*===========*=========* 94 // | John | Barry | 123456 | 95 // | Kathy | Smith | 687987 | 96 // | Bob | McCornick | 3979870 | 97 // *============*===========*=========* 98 } 99 100 // TestNumLines to test the numbers of lines 101 func TestNumLines(t *testing.T) { 102 data := [][]string{ 103 {"A", "The Good", "500"}, 104 {"B", "The Very very Bad Man", "288"}, 105 {"C", "The Ugly", "120"}, 106 {"D", "The Gopher", "800"}, 107 } 108 109 buf := &bytes.Buffer{} 110 table := NewWriter(buf) 111 table.SetHeader([]string{"Name", "Sign", "Rating"}) 112 113 for i, v := range data { 114 table.Append(v) 115 checkEqual(t, table.NumLines(), i+1, "Number of lines failed") 116 } 117 118 checkEqual(t, table.NumLines(), len(data), "Number of lines failed") 119 } 120 121 func TestCSVInfo(t *testing.T) { 122 buf := &bytes.Buffer{} 123 table, err := NewCSV(buf, "testdata/test_info.csv", true) 124 if err != nil { 125 t.Error(err) 126 return 127 } 128 table.SetAlignment(ALIGN_LEFT) 129 table.SetBorder(false) 130 table.Render() 131 132 got := buf.String() 133 want := ` FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA 134 -----------+--------------+------+-----+---------+----------------- 135 user_id | smallint(5) | NO | PRI | NULL | auto_increment 136 username | varchar(10) | NO | | NULL | 137 password | varchar(100) | NO | | NULL | 138 ` 139 checkEqual(t, got, want, "CSV info failed") 140 } 141 142 func TestCSVSeparator(t *testing.T) { 143 buf := &bytes.Buffer{} 144 table, err := NewCSV(buf, "testdata/test.csv", true) 145 if err != nil { 146 t.Error(err) 147 return 148 } 149 table.SetRowLine(true) 150 table.SetCenterSeparator("+") 151 table.SetColumnSeparator("|") 152 table.SetRowSeparator("-") 153 table.SetAlignment(ALIGN_LEFT) 154 table.Render() 155 156 want := `+------------+-----------+---------+ 157 | FIRST NAME | LAST NAME | SSN | 158 +------------+-----------+---------+ 159 | John | Barry | 123456 | 160 +------------+-----------+---------+ 161 | Kathy | Smith | 687987 | 162 +------------+-----------+---------+ 163 | Bob | McCornick | 3979870 | 164 +------------+-----------+---------+ 165 ` 166 167 checkEqual(t, buf.String(), want, "CSV info failed") 168 } 169 170 func TestNoBorder(t *testing.T) { 171 data := [][]string{ 172 {"1/1/2014", "Domain name", "2233", "$10.98"}, 173 {"1/1/2014", "January Hosting", "2233", "$54.95"}, 174 {"", " (empty)\n (empty)", "", ""}, 175 {"1/4/2014", "February Hosting", "2233", "$51.00"}, 176 {"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, 177 {"1/4/2014", " (Discount)", "2233", "-$1.00"}, 178 } 179 180 var buf bytes.Buffer 181 table := NewWriter(&buf) 182 table.SetAutoWrapText(false) 183 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 184 table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer 185 table.SetBorder(false) // Set Border to false 186 table.AppendBulk(data) // Add Bulk Data 187 table.Render() 188 189 want := ` DATE | DESCRIPTION | CV2 | AMOUNT 190 -----------+--------------------------+-------+---------- 191 1/1/2014 | Domain name | 2233 | $10.98 192 1/1/2014 | January Hosting | 2233 | $54.95 193 | (empty) | | 194 | (empty) | | 195 1/4/2014 | February Hosting | 2233 | $51.00 196 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 197 1/4/2014 | (Discount) | 2233 | -$1.00 198 -----------+--------------------------+-------+---------- 199 TOTAL | $145.93 200 --------+---------- 201 ` 202 203 checkEqual(t, buf.String(), want, "border table rendering failed") 204 } 205 206 func TestWithBorder(t *testing.T) { 207 data := [][]string{ 208 {"1/1/2014", "Domain name", "2233", "$10.98"}, 209 {"1/1/2014", "January Hosting", "2233", "$54.95"}, 210 {"", " (empty)\n (empty)", "", ""}, 211 {"1/4/2014", "February Hosting", "2233", "$51.00"}, 212 {"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, 213 {"1/4/2014", " (Discount)", "2233", "-$1.00"}, 214 } 215 216 var buf bytes.Buffer 217 table := NewWriter(&buf) 218 table.SetAutoWrapText(false) 219 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 220 table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer 221 table.AppendBulk(data) // Add Bulk Data 222 table.Render() 223 224 want := `+----------+--------------------------+-------+---------+ 225 | DATE | DESCRIPTION | CV2 | AMOUNT | 226 +----------+--------------------------+-------+---------+ 227 | 1/1/2014 | Domain name | 2233 | $10.98 | 228 | 1/1/2014 | January Hosting | 2233 | $54.95 | 229 | | (empty) | | | 230 | | (empty) | | | 231 | 1/4/2014 | February Hosting | 2233 | $51.00 | 232 | 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | 233 | 1/4/2014 | (Discount) | 2233 | -$1.00 | 234 +----------+--------------------------+-------+---------+ 235 | TOTAL | $145.93 | 236 +----------+--------------------------+-------+---------+ 237 ` 238 239 checkEqual(t, buf.String(), want, "border table rendering failed") 240 } 241 242 func TestPrintingInMarkdown(t *testing.T) { 243 data := [][]string{ 244 {"1/1/2014", "Domain name", "2233", "$10.98"}, 245 {"1/1/2014", "January Hosting", "2233", "$54.95"}, 246 {"1/4/2014", "February Hosting", "2233", "$51.00"}, 247 {"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, 248 } 249 250 var buf bytes.Buffer 251 table := NewWriter(&buf) 252 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 253 table.AppendBulk(data) // Add Bulk Data 254 table.SetBorders(Border{Left: true, Top: false, Right: true, Bottom: false}) 255 table.SetCenterSeparator("|") 256 table.Render() 257 258 want := `| DATE | DESCRIPTION | CV2 | AMOUNT | 259 |----------|--------------------------|------|--------| 260 | 1/1/2014 | Domain name | 2233 | $10.98 | 261 | 1/1/2014 | January Hosting | 2233 | $54.95 | 262 | 1/4/2014 | February Hosting | 2233 | $51.00 | 263 | 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | 264 ` 265 checkEqual(t, buf.String(), want, "border table rendering failed") 266 } 267 268 func TestPrintHeading(t *testing.T) { 269 var buf bytes.Buffer 270 table := NewWriter(&buf) 271 table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 272 table.printHeading() 273 want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | 274 +---+---+---+---+---+---+---+---+---+---+---+---+ 275 ` 276 checkEqual(t, buf.String(), want, "header rendering failed") 277 } 278 279 func TestPrintHeadingWithoutAutoFormat(t *testing.T) { 280 var buf bytes.Buffer 281 table := NewWriter(&buf) 282 table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 283 table.SetAutoFormatHeaders(false) 284 table.printHeading() 285 want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | 286 +---+---+---+---+---+---+---+---+---+---+---+---+ 287 ` 288 checkEqual(t, buf.String(), want, "header rendering failed") 289 } 290 291 func TestPrintFooter(t *testing.T) { 292 var buf bytes.Buffer 293 table := NewWriter(&buf) 294 table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 295 table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 296 table.printFooter() 297 want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | 298 +---+---+---+---+---+---+---+---+---+---+---+---+ 299 ` 300 checkEqual(t, buf.String(), want, "footer rendering failed") 301 } 302 303 func TestPrintFooterWithoutAutoFormat(t *testing.T) { 304 var buf bytes.Buffer 305 table := NewWriter(&buf) 306 table.SetAutoFormatHeaders(false) 307 table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 308 table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) 309 table.printFooter() 310 want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | 311 +---+---+---+---+---+---+---+---+---+---+---+---+ 312 ` 313 checkEqual(t, buf.String(), want, "footer rendering failed") 314 } 315 316 func TestPrintShortCaption(t *testing.T) { 317 var buf bytes.Buffer 318 data := [][]string{ 319 {"A", "The Good", "500"}, 320 {"B", "The Very very Bad Man", "288"}, 321 {"C", "The Ugly", "120"}, 322 {"D", "The Gopher", "800"}, 323 } 324 325 table := NewWriter(&buf) 326 table.SetHeader([]string{"Name", "Sign", "Rating"}) 327 table.SetCaption(true, "Short caption.") 328 329 for _, v := range data { 330 table.Append(v) 331 } 332 table.Render() 333 334 want := `+------+-----------------------+--------+ 335 | NAME | SIGN | RATING | 336 +------+-----------------------+--------+ 337 | A | The Good | 500 | 338 | B | The Very very Bad Man | 288 | 339 | C | The Ugly | 120 | 340 | D | The Gopher | 800 | 341 +------+-----------------------+--------+ 342 Short caption. 343 ` 344 checkEqual(t, buf.String(), want, "long caption for short example rendering failed") 345 } 346 347 func TestPrintLongCaptionWithShortExample(t *testing.T) { 348 var buf bytes.Buffer 349 data := [][]string{ 350 {"A", "The Good", "500"}, 351 {"B", "The Very very Bad Man", "288"}, 352 {"C", "The Ugly", "120"}, 353 {"D", "The Gopher", "800"}, 354 } 355 356 table := NewWriter(&buf) 357 table.SetHeader([]string{"Name", "Sign", "Rating"}) 358 table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.") 359 360 for _, v := range data { 361 table.Append(v) 362 } 363 table.Render() 364 365 want := `+------+-----------------------+--------+ 366 | NAME | SIGN | RATING | 367 +------+-----------------------+--------+ 368 | A | The Good | 500 | 369 | B | The Very very Bad Man | 288 | 370 | C | The Ugly | 120 | 371 | D | The Gopher | 800 | 372 +------+-----------------------+--------+ 373 This is a very long caption. The text 374 should wrap. If not, we have a problem 375 that needs to be solved. 376 ` 377 checkEqual(t, buf.String(), want, "long caption for short example rendering failed") 378 } 379 380 func TestPrintCaptionWithFooter(t *testing.T) { 381 data := [][]string{ 382 {"1/1/2014", "Domain name", "2233", "$10.98"}, 383 {"1/1/2014", "January Hosting", "2233", "$54.95"}, 384 {"1/4/2014", "February Hosting", "2233", "$51.00"}, 385 {"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, 386 } 387 388 var buf bytes.Buffer 389 table := NewWriter(&buf) 390 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 391 table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer 392 table.SetCaption(true, "This is a very long caption. The text should wrap to the width of the table.") // Add caption 393 table.SetBorder(false) // Set Border to false 394 table.AppendBulk(data) // Add Bulk Data 395 table.Render() 396 397 want := ` DATE | DESCRIPTION | CV2 | AMOUNT 398 -----------+--------------------------+-------+---------- 399 1/1/2014 | Domain name | 2233 | $10.98 400 1/1/2014 | January Hosting | 2233 | $54.95 401 1/4/2014 | February Hosting | 2233 | $51.00 402 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 403 -----------+--------------------------+-------+---------- 404 TOTAL | $146.93 405 --------+---------- 406 This is a very long caption. The text should wrap to the 407 width of the table. 408 ` 409 checkEqual(t, buf.String(), want, "border table rendering failed") 410 } 411 412 func TestPrintLongCaptionWithLongExample(t *testing.T) { 413 var buf bytes.Buffer 414 data := [][]string{ 415 {"Learn East has computers with adapted keyboards with enlarged print etc", "Some Data", "Another Data"}, 416 {"Instead of lining up the letters all", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards"}, 417 } 418 419 table := NewWriter(&buf) 420 table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.") 421 table.SetHeader([]string{"Name", "Sign", "Rating"}) 422 423 for _, v := range data { 424 table.Append(v) 425 } 426 table.Render() 427 428 want := `+--------------------------------+--------------------------------+-------------------------------+ 429 | NAME | SIGN | RATING | 430 +--------------------------------+--------------------------------+-------------------------------+ 431 | Learn East has computers | Some Data | Another Data | 432 | with adapted keyboards with | | | 433 | enlarged print etc | | | 434 | Instead of lining up the | the way across, he splits the | Like most ergonomic keyboards | 435 | letters all | keyboard in two | | 436 +--------------------------------+--------------------------------+-------------------------------+ 437 This is a very long caption. The text should wrap. If not, we have a problem that needs to be 438 solved. 439 ` 440 checkEqual(t, buf.String(), want, "long caption for long example rendering failed") 441 } 442 443 func Example_autowrap() { 444 var multiline = `A multiline 445 string with some lines being really long.` 446 447 const ( 448 testRow = iota 449 testHeader 450 testFooter 451 testFooter2 452 ) 453 for mode := testRow; mode <= testFooter2; mode++ { 454 for _, autoFmt := range []bool{false, true} { 455 if mode == testRow && autoFmt { 456 // Nothing special to test, skip 457 continue 458 } 459 for _, autoWrap := range []bool{false, true} { 460 for _, reflow := range []bool{false, true} { 461 if !autoWrap && reflow { 462 // Invalid configuration, skip 463 continue 464 } 465 fmt.Println("mode", mode, "autoFmt", autoFmt, "autoWrap", autoWrap, "reflow", reflow) 466 t := NewWriter(os.Stdout) 467 t.SetAutoFormatHeaders(autoFmt) 468 t.SetAutoWrapText(autoWrap) 469 t.SetReflowDuringAutoWrap(reflow) 470 if mode == testHeader { 471 t.SetHeader([]string{"woo", multiline}) 472 } else { 473 t.SetHeader([]string{"woo", "waa"}) 474 } 475 if mode == testRow { 476 t.Append([]string{"woo", multiline}) 477 } else { 478 t.Append([]string{"woo", "waa"}) 479 } 480 if mode == testFooter { 481 t.SetFooter([]string{"woo", multiline}) 482 } else if mode == testFooter2 { 483 t.SetFooter([]string{"", multiline}) 484 } else { 485 t.SetFooter([]string{"woo", "waa"}) 486 } 487 t.Render() 488 } 489 } 490 } 491 fmt.Println() 492 } 493 494 // Output: 495 // mode 0 autoFmt false autoWrap false reflow false 496 // +-----+-------------------------------------------+ 497 // | woo | waa | 498 // +-----+-------------------------------------------+ 499 // | woo | A multiline | 500 // | | string with some lines being really long. | 501 // +-----+-------------------------------------------+ 502 // | woo | waa | 503 // +-----+-------------------------------------------+ 504 // mode 0 autoFmt false autoWrap true reflow false 505 // +-----+--------------------------------+ 506 // | woo | waa | 507 // +-----+--------------------------------+ 508 // | woo | A multiline | 509 // | | | 510 // | | string with some lines being | 511 // | | really long. | 512 // +-----+--------------------------------+ 513 // | woo | waa | 514 // +-----+--------------------------------+ 515 // mode 0 autoFmt false autoWrap true reflow true 516 // +-----+--------------------------------+ 517 // | woo | waa | 518 // +-----+--------------------------------+ 519 // | woo | A multiline string with some | 520 // | | lines being really long. | 521 // +-----+--------------------------------+ 522 // | woo | waa | 523 // +-----+--------------------------------+ 524 // 525 // mode 1 autoFmt false autoWrap false reflow false 526 // +-----+-------------------------------------------+ 527 // | woo | A multiline | 528 // | | string with some lines being really long. | 529 // +-----+-------------------------------------------+ 530 // | woo | waa | 531 // +-----+-------------------------------------------+ 532 // | woo | waa | 533 // +-----+-------------------------------------------+ 534 // mode 1 autoFmt false autoWrap true reflow false 535 // +-----+--------------------------------+ 536 // | woo | A multiline | 537 // | | | 538 // | | string with some lines being | 539 // | | really long. | 540 // +-----+--------------------------------+ 541 // | woo | waa | 542 // +-----+--------------------------------+ 543 // | woo | waa | 544 // +-----+--------------------------------+ 545 // mode 1 autoFmt false autoWrap true reflow true 546 // +-----+--------------------------------+ 547 // | woo | A multiline string with some | 548 // | | lines being really long. | 549 // +-----+--------------------------------+ 550 // | woo | waa | 551 // +-----+--------------------------------+ 552 // | woo | waa | 553 // +-----+--------------------------------+ 554 // mode 1 autoFmt true autoWrap false reflow false 555 // +-----+-------------------------------------------+ 556 // | WOO | A MULTILINE | 557 // | | STRING WITH SOME LINES BEING REALLY LONG | 558 // +-----+-------------------------------------------+ 559 // | woo | waa | 560 // +-----+-------------------------------------------+ 561 // | WOO | WAA | 562 // +-----+-------------------------------------------+ 563 // mode 1 autoFmt true autoWrap true reflow false 564 // +-----+--------------------------------+ 565 // | WOO | A MULTILINE | 566 // | | | 567 // | | STRING WITH SOME LINES BEING | 568 // | | REALLY LONG | 569 // +-----+--------------------------------+ 570 // | woo | waa | 571 // +-----+--------------------------------+ 572 // | WOO | WAA | 573 // +-----+--------------------------------+ 574 // mode 1 autoFmt true autoWrap true reflow true 575 // +-----+--------------------------------+ 576 // | WOO | A MULTILINE STRING WITH SOME | 577 // | | LINES BEING REALLY LONG | 578 // +-----+--------------------------------+ 579 // | woo | waa | 580 // +-----+--------------------------------+ 581 // | WOO | WAA | 582 // +-----+--------------------------------+ 583 // 584 // mode 2 autoFmt false autoWrap false reflow false 585 // +-----+-------------------------------------------+ 586 // | woo | waa | 587 // +-----+-------------------------------------------+ 588 // | woo | waa | 589 // +-----+-------------------------------------------+ 590 // | woo | A multiline | 591 // | | string with some lines being really long. | 592 // +-----+-------------------------------------------+ 593 // mode 2 autoFmt false autoWrap true reflow false 594 // +-----+--------------------------------+ 595 // | woo | waa | 596 // +-----+--------------------------------+ 597 // | woo | waa | 598 // +-----+--------------------------------+ 599 // | woo | A multiline | 600 // | | | 601 // | | string with some lines being | 602 // | | really long. | 603 // +-----+--------------------------------+ 604 // mode 2 autoFmt false autoWrap true reflow true 605 // +-----+--------------------------------+ 606 // | woo | waa | 607 // +-----+--------------------------------+ 608 // | woo | waa | 609 // +-----+--------------------------------+ 610 // | woo | A multiline string with some | 611 // | | lines being really long. | 612 // +-----+--------------------------------+ 613 // mode 2 autoFmt true autoWrap false reflow false 614 // +-----+-------------------------------------------+ 615 // | WOO | WAA | 616 // +-----+-------------------------------------------+ 617 // | woo | waa | 618 // +-----+-------------------------------------------+ 619 // | WOO | A MULTILINE | 620 // | | STRING WITH SOME LINES BEING REALLY LONG | 621 // +-----+-------------------------------------------+ 622 // mode 2 autoFmt true autoWrap true reflow false 623 // +-----+--------------------------------+ 624 // | WOO | WAA | 625 // +-----+--------------------------------+ 626 // | woo | waa | 627 // +-----+--------------------------------+ 628 // | WOO | A MULTILINE | 629 // | | | 630 // | | STRING WITH SOME LINES BEING | 631 // | | REALLY LONG | 632 // +-----+--------------------------------+ 633 // mode 2 autoFmt true autoWrap true reflow true 634 // +-----+--------------------------------+ 635 // | WOO | WAA | 636 // +-----+--------------------------------+ 637 // | woo | waa | 638 // +-----+--------------------------------+ 639 // | WOO | A MULTILINE STRING WITH SOME | 640 // | | LINES BEING REALLY LONG | 641 // +-----+--------------------------------+ 642 // 643 // mode 3 autoFmt false autoWrap false reflow false 644 // +-----+-------------------------------------------+ 645 // | woo | waa | 646 // +-----+-------------------------------------------+ 647 // | woo | waa | 648 // +-----+-------------------------------------------+ 649 // | A multiline | 650 // | string with some lines being really long. | 651 // +-----+-------------------------------------------+ 652 // mode 3 autoFmt false autoWrap true reflow false 653 // +-----+--------------------------------+ 654 // | woo | waa | 655 // +-----+--------------------------------+ 656 // | woo | waa | 657 // +-----+--------------------------------+ 658 // | A multiline | 659 // | | 660 // | string with some lines being | 661 // | really long. | 662 // +-----+--------------------------------+ 663 // mode 3 autoFmt false autoWrap true reflow true 664 // +-----+--------------------------------+ 665 // | woo | waa | 666 // +-----+--------------------------------+ 667 // | woo | waa | 668 // +-----+--------------------------------+ 669 // | A multiline string with some | 670 // | lines being really long. | 671 // +-----+--------------------------------+ 672 // mode 3 autoFmt true autoWrap false reflow false 673 // +-----+-------------------------------------------+ 674 // | WOO | WAA | 675 // +-----+-------------------------------------------+ 676 // | woo | waa | 677 // +-----+-------------------------------------------+ 678 // | A MULTILINE | 679 // | STRING WITH SOME LINES BEING REALLY LONG | 680 // +-----+-------------------------------------------+ 681 // mode 3 autoFmt true autoWrap true reflow false 682 // +-----+--------------------------------+ 683 // | WOO | WAA | 684 // +-----+--------------------------------+ 685 // | woo | waa | 686 // +-----+--------------------------------+ 687 // | A MULTILINE | 688 // | | 689 // | STRING WITH SOME LINES BEING | 690 // | REALLY LONG | 691 // +-----+--------------------------------+ 692 // mode 3 autoFmt true autoWrap true reflow true 693 // +-----+--------------------------------+ 694 // | WOO | WAA | 695 // +-----+--------------------------------+ 696 // | woo | waa | 697 // +-----+--------------------------------+ 698 // | A MULTILINE STRING WITH SOME | 699 // | LINES BEING REALLY LONG | 700 // +-----+--------------------------------+ 701 } 702 703 func TestPrintLine(t *testing.T) { 704 header := make([]string, 12) 705 val := " " 706 want := "" 707 for i := range header { 708 header[i] = val 709 want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1)) 710 val = val + " " 711 } 712 want = want + "+" 713 var buf bytes.Buffer 714 table := NewWriter(&buf) 715 table.SetHeader(header) 716 table.printLine(false) 717 checkEqual(t, buf.String(), want, "line rendering failed") 718 } 719 720 func TestAnsiStrip(t *testing.T) { 721 header := make([]string, 12) 722 val := " " 723 want := "" 724 for i := range header { 725 header[i] = "\033[43;30m" + val + "\033[00m" 726 want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1)) 727 val = val + " " 728 } 729 want = want + "+" 730 var buf bytes.Buffer 731 table := NewWriter(&buf) 732 table.SetHeader(header) 733 table.printLine(false) 734 checkEqual(t, buf.String(), want, "line rendering failed") 735 } 736 737 func NewCustomizedTable(out io.Writer) *Table { 738 table := NewWriter(out) 739 table.SetCenterSeparator("") 740 table.SetColumnSeparator("") 741 table.SetRowSeparator("") 742 table.SetBorder(false) 743 table.SetAlignment(ALIGN_LEFT) 744 table.SetHeader([]string{}) 745 return table 746 } 747 748 func TestSubclass(t *testing.T) { 749 buf := new(bytes.Buffer) 750 table := NewCustomizedTable(buf) 751 752 data := [][]string{ 753 {"A", "The Good", "500"}, 754 {"B", "The Very very Bad Man", "288"}, 755 {"C", "The Ugly", "120"}, 756 {"D", "The Gopher", "800"}, 757 } 758 759 for _, v := range data { 760 table.Append(v) 761 } 762 table.Render() 763 764 want := ` A The Good 500 765 B The Very very Bad Man 288 766 C The Ugly 120 767 D The Gopher 800 768 ` 769 checkEqual(t, buf.String(), want, "test subclass failed") 770 } 771 772 func TestAutoMergeRows(t *testing.T) { 773 data := [][]string{ 774 {"A", "The Good", "500"}, 775 {"A", "The Very very Bad Man", "288"}, 776 {"B", "The Very very Bad Man", "120"}, 777 {"B", "The Very very Bad Man", "200"}, 778 } 779 var buf bytes.Buffer 780 table := NewWriter(&buf) 781 table.SetHeader([]string{"Name", "Sign", "Rating"}) 782 783 for _, v := range data { 784 table.Append(v) 785 } 786 table.SetAutoMergeCells(true) 787 table.Render() 788 want := `+------+-----------------------+--------+ 789 | NAME | SIGN | RATING | 790 +------+-----------------------+--------+ 791 | A | The Good | 500 | 792 | | The Very very Bad Man | 288 | 793 | B | | 120 | 794 | | | 200 | 795 +------+-----------------------+--------+ 796 ` 797 got := buf.String() 798 if got != want { 799 t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) 800 } 801 802 buf.Reset() 803 table = NewWriter(&buf) 804 table.SetHeader([]string{"Name", "Sign", "Rating"}) 805 806 for _, v := range data { 807 table.Append(v) 808 } 809 table.SetAutoMergeCells(true) 810 table.SetRowLine(true) 811 table.Render() 812 want = `+------+-----------------------+--------+ 813 | NAME | SIGN | RATING | 814 +------+-----------------------+--------+ 815 | A | The Good | 500 | 816 + +-----------------------+--------+ 817 | | The Very very Bad Man | 288 | 818 +------+ +--------+ 819 | B | | 120 | 820 + + +--------+ 821 | | | 200 | 822 +------+-----------------------+--------+ 823 ` 824 checkEqual(t, buf.String(), want) 825 826 buf.Reset() 827 table = NewWriter(&buf) 828 table.SetHeader([]string{"Name", "Sign", "Rating"}) 829 830 dataWithlongText := [][]string{ 831 {"A", "The Good", "500"}, 832 {"A", "The Very very very very very Bad Man", "288"}, 833 {"B", "The Very very very very very Bad Man", "120"}, 834 {"C", "The Very very Bad Man", "200"}, 835 } 836 table.AppendBulk(dataWithlongText) 837 table.SetAutoMergeCells(true) 838 table.SetRowLine(true) 839 table.Render() 840 want = `+------+--------------------------------+--------+ 841 | NAME | SIGN | RATING | 842 +------+--------------------------------+--------+ 843 | A | The Good | 500 | 844 + +--------------------------------+--------+ 845 | | The Very very very very very | 288 | 846 | | Bad Man | | 847 +------+ +--------+ 848 | B | | 120 | 849 | | | | 850 +------+--------------------------------+--------+ 851 | C | The Very very Bad Man | 200 | 852 +------+--------------------------------+--------+ 853 ` 854 checkEqual(t, buf.String(), want) 855 856 buf.Reset() 857 table = NewWriter(&buf) 858 table.SetHeader([]string{"Name", "Sign", "Rating"}) 859 860 dataWithlongText2 := [][]string{ 861 {"A", "The Good", "500"}, 862 {"A", "The Very very very very very Bad Man", "288"}, 863 {"B", "The Very very Bad Man", "120"}, 864 } 865 table.AppendBulk(dataWithlongText2) 866 table.SetAutoMergeCells(true) 867 table.SetRowLine(true) 868 table.Render() 869 want = `+------+--------------------------------+--------+ 870 | NAME | SIGN | RATING | 871 +------+--------------------------------+--------+ 872 | A | The Good | 500 | 873 + +--------------------------------+--------+ 874 | | The Very very very very very | 288 | 875 | | Bad Man | | 876 +------+--------------------------------+--------+ 877 | B | The Very very Bad Man | 120 | 878 +------+--------------------------------+--------+ 879 ` 880 checkEqual(t, buf.String(), want) 881 } 882 883 func TestClearRows(t *testing.T) { 884 data := [][]string{ 885 {"1/1/2014", "Domain name", "2233", "$10.98"}, 886 } 887 888 var buf bytes.Buffer 889 table := NewWriter(&buf) 890 table.SetAutoWrapText(false) 891 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 892 table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer 893 table.AppendBulk(data) // Add Bulk Data 894 table.Render() 895 896 originalWant := `+----------+-------------+-------+---------+ 897 | DATE | DESCRIPTION | CV2 | AMOUNT | 898 +----------+-------------+-------+---------+ 899 | 1/1/2014 | Domain name | 2233 | $10.98 | 900 +----------+-------------+-------+---------+ 901 | TOTAL | $145.93 | 902 +----------+-------------+-------+---------+ 903 ` 904 want := originalWant 905 906 checkEqual(t, buf.String(), want, "table clear rows failed") 907 908 buf.Reset() 909 table.ClearRows() 910 table.Render() 911 912 want = `+----------+-------------+-------+---------+ 913 | DATE | DESCRIPTION | CV2 | AMOUNT | 914 +----------+-------------+-------+---------+ 915 +----------+-------------+-------+---------+ 916 | TOTAL | $145.93 | 917 +----------+-------------+-------+---------+ 918 ` 919 920 checkEqual(t, buf.String(), want, "table clear rows failed") 921 922 buf.Reset() 923 table.AppendBulk(data) // Add Bulk Data 924 table.Render() 925 926 want = `+----------+-------------+-------+---------+ 927 | DATE | DESCRIPTION | CV2 | AMOUNT | 928 +----------+-------------+-------+---------+ 929 | 1/1/2014 | Domain name | 2233 | $10.98 | 930 +----------+-------------+-------+---------+ 931 | TOTAL | $145.93 | 932 +----------+-------------+-------+---------+ 933 ` 934 935 checkEqual(t, buf.String(), want, "table clear rows failed") 936 } 937 938 func TestClearFooters(t *testing.T) { 939 data := [][]string{ 940 {"1/1/2014", "Domain name", "2233", "$10.98"}, 941 } 942 943 var buf bytes.Buffer 944 table := NewWriter(&buf) 945 table.SetAutoWrapText(false) 946 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 947 table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer 948 table.AppendBulk(data) // Add Bulk Data 949 table.Render() 950 951 buf.Reset() 952 table.ClearFooter() 953 table.Render() 954 955 want := `+----------+-------------+-------+---------+ 956 | DATE | DESCRIPTION | CV2 | AMOUNT | 957 +----------+-------------+-------+---------+ 958 | 1/1/2014 | Domain name | 2233 | $10.98 | 959 +----------+-------------+-------+---------+ 960 ` 961 962 checkEqual(t, buf.String(), want) 963 } 964 965 func TestMoreDataColumnsThanHeaders(t *testing.T) { 966 var ( 967 buf = &bytes.Buffer{} 968 table = NewWriter(buf) 969 header = []string{"A", "B", "C"} 970 data = [][]string{ 971 {"a", "b", "c", "d"}, 972 {"1", "2", "3", "4"}, 973 } 974 want = `+---+---+---+---+ 975 | A | B | C | | 976 +---+---+---+---+ 977 | a | b | c | d | 978 | 1 | 2 | 3 | 4 | 979 +---+---+---+---+ 980 ` 981 ) 982 table.SetHeader(header) 983 // table.SetFooter(ctx.tableCtx.footer) 984 table.AppendBulk(data) 985 table.Render() 986 987 checkEqual(t, buf.String(), want) 988 } 989 990 func TestMoreFooterColumnsThanHeaders(t *testing.T) { 991 var ( 992 buf = &bytes.Buffer{} 993 table = NewWriter(buf) 994 header = []string{"A", "B", "C"} 995 data = [][]string{ 996 {"a", "b", "c", "d"}, 997 {"1", "2", "3", "4"}, 998 } 999 footer = []string{"a", "b", "c", "d", "e"} 1000 want = `+---+---+---+---+---+ 1001 | A | B | C | | | 1002 +---+---+---+---+---+ 1003 | a | b | c | d | 1004 | 1 | 2 | 3 | 4 | 1005 +---+---+---+---+---+ 1006 | A | B | C | D | E | 1007 +---+---+---+---+---+ 1008 ` 1009 ) 1010 table.SetHeader(header) 1011 table.SetFooter(footer) 1012 table.AppendBulk(data) 1013 table.Render() 1014 1015 checkEqual(t, buf.String(), want) 1016 } 1017 1018 func TestSetColMinWidth(t *testing.T) { 1019 var ( 1020 buf = &bytes.Buffer{} 1021 table = NewWriter(buf) 1022 header = []string{"AAA", "BBB", "CCC"} 1023 data = [][]string{ 1024 {"a", "b", "c"}, 1025 {"1", "2", "3"}, 1026 } 1027 footer = []string{"a", "b", "cccc"} 1028 want = `+-----+-----+-------+ 1029 | AAA | BBB | CCC | 1030 +-----+-----+-------+ 1031 | a | b | c | 1032 | 1 | 2 | 3 | 1033 +-----+-----+-------+ 1034 | A | B | CCCC | 1035 +-----+-----+-------+ 1036 ` 1037 ) 1038 table.SetHeader(header) 1039 table.SetFooter(footer) 1040 table.AppendBulk(data) 1041 table.SetColMinWidth(2, 5) 1042 table.Render() 1043 1044 checkEqual(t, buf.String(), want) 1045 } 1046 1047 func TestWrapString(t *testing.T) { 1048 want := []string{"ああああああああああああああああああああああああ", "あああああああ"} 1049 got, _ := WrapString("ああああああああああああああああああああああああ あああああああ", 55) 1050 checkEqual(t, got, want) 1051 } 1052 1053 func TestNumberAlign(t *testing.T) { 1054 var ( 1055 buf = &bytes.Buffer{} 1056 table = NewWriter(buf) 1057 data = [][]string{ 1058 {"AAAAAAAAAAAAA", "BBBBBBBBBBBBB", "CCCCCCCCCCCCCC"}, 1059 {"A", "B", "C"}, 1060 {"123456789", "2", "3"}, 1061 {"1", "2", "123,456,789"}, 1062 {"1", "123,456.789", "3"}, 1063 {"-123,456", "-2", "-3"}, 1064 } 1065 want = `+---------------+---------------+----------------+ 1066 | AAAAAAAAAAAAA | BBBBBBBBBBBBB | CCCCCCCCCCCCCC | 1067 | A | B | C | 1068 | 123456789 | 2 | 3 | 1069 | 1 | 2 | 123,456,789 | 1070 | 1 | 123,456.789 | 3 | 1071 | -123,456 | -2 | -3 | 1072 +---------------+---------------+----------------+ 1073 ` 1074 ) 1075 table.AppendBulk(data) 1076 table.Render() 1077 1078 checkEqual(t, buf.String(), want) 1079 } 1080 1081 func TestCustomAlign(t *testing.T) { 1082 var ( 1083 buf = &bytes.Buffer{} 1084 table = NewWriter(buf) 1085 header = []string{"AAA", "BBB", "CCC"} 1086 data = [][]string{ 1087 {"a", "b", "c"}, 1088 {"1", "2", "3"}, 1089 } 1090 footer = []string{"a", "b", "cccc"} 1091 want = `+-----+-----+-------+ 1092 | AAA | BBB | CCC | 1093 +-----+-----+-------+ 1094 | a | b | c | 1095 | 1 | 2 | 3 | 1096 +-----+-----+-------+ 1097 | A | B | CCCC | 1098 +-----+-----+-------+ 1099 ` 1100 ) 1101 table.SetHeader(header) 1102 table.SetFooter(footer) 1103 table.AppendBulk(data) 1104 table.SetColMinWidth(2, 5) 1105 table.SetColumnAlignment([]int{ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT}) 1106 table.Render() 1107 1108 checkEqual(t, buf.String(), want) 1109 } 1110 1111 func TestTitle(t *testing.T) { 1112 ts := []struct { 1113 text string 1114 want string 1115 }{ 1116 {"", ""}, 1117 {"foo", "FOO"}, 1118 {"Foo", "FOO"}, 1119 {"foO", "FOO"}, 1120 {".foo", "FOO"}, 1121 {"foo.", "FOO"}, 1122 {".foo.", "FOO"}, 1123 {".foo.bar.", "FOO BAR"}, 1124 {"_foo", "FOO"}, 1125 {"foo_", "FOO"}, 1126 {"_foo_", "FOO"}, 1127 {"_foo_bar_", "FOO BAR"}, 1128 {" foo", "FOO"}, 1129 {"foo ", "FOO"}, 1130 {" foo ", "FOO"}, 1131 {" foo bar ", "FOO BAR"}, 1132 {"0.1", "0.1"}, 1133 {"FOO 0.1", "FOO 0.1"}, 1134 {".1 0.1", ".1 0.1"}, 1135 {"1. 0.1", "1. 0.1"}, 1136 {"1. 0.", "1. 0."}, 1137 {".1. 0.", ".1. 0."}, 1138 {".$ . $.", "$ . $"}, 1139 {".$. $.", "$ $"}, 1140 } 1141 for _, tt := range ts { 1142 got := Title(tt.text) 1143 if got != tt.want { 1144 t.Errorf("want %q, bot got %q", tt.want, got) 1145 } 1146 } 1147 } 1148 1149 func TestKubeFormat(t *testing.T) { 1150 data := [][]string{ 1151 {"1/1/2014", "jan_hosting", "2233", "$10.98"}, 1152 {"1/1/2014", "feb_hosting", "2233", "$54.95"}, 1153 {"1/4/2014", "feb_extra_bandwidth", "2233", "$51.00"}, 1154 {"1/4/2014", "mar_hosting", "2233", "$30.00"}, 1155 } 1156 1157 var buf bytes.Buffer 1158 table := NewWriter(&buf) 1159 table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) 1160 table.SetAutoWrapText(false) 1161 table.SetAutoFormatHeaders(true) 1162 table.SetHeaderAlignment(ALIGN_LEFT) 1163 table.SetAlignment(ALIGN_LEFT) 1164 table.SetCenterSeparator("") 1165 table.SetColumnSeparator("") 1166 table.SetRowSeparator("") 1167 table.SetHeaderLine(false) 1168 table.SetBorder(false) 1169 table.SetTablePadding("\t") // pad with tabs 1170 table.SetNoWhiteSpace(true) 1171 table.AppendBulk(data) // Add Bulk Data 1172 table.Render() 1173 1174 want := `DATE DESCRIPTION CV2 AMOUNT 1175 1/1/2014 jan_hosting 2233 $10.98 1176 1/1/2014 feb_hosting 2233 $54.95 1177 1/4/2014 feb_extra_bandwidth 2233 $51.00 1178 1/4/2014 mar_hosting 2233 $30.00 1179 ` 1180 1181 checkEqual(t, buf.String(), want, "kube format rendering failed") 1182 }