...

Source file src/github.com/jedib0t/go-pretty/v6/table/render_test.go

Documentation: github.com/jedib0t/go-pretty/v6/table

     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  	//table.Style().Format.Direction = text.Default
   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  	// colored is simple; render the colored table into another table
   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  	// dump it out in a easy way to update the test if things are meant to
   774  	// change due to some other feature
   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  	// colored is simple; render the colored table into another colored table
   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  	// dump it out in a easy way to update the test if things are meant to
   812  	// change due to some other feature
   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  	// dump it out in a easy way to update the test if things are meant to
   843  	// change due to some other feature
   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]), // First Name
   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]), // Last Name
   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, // Salary
   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  	// ensure sorting is done before hiding the columns
   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() // doesn't make any difference
  1240  	tw.AppendRows(testRows)
  1241  	tw.AppendSeparator()
  1242  	tw.AppendSeparator() // doesn't make any difference
  1243  	tw.AppendRow(testRowMultiLine)
  1244  	tw.AppendSeparator()
  1245  	tw.AppendSeparator() // doesn't make any difference
  1246  	tw.AppendSeparator() // doesn't make any difference
  1247  	tw.AppendRow(Row{11, "Sansa", "Stark", 6000})
  1248  	tw.AppendSeparator() // doesn't make any difference
  1249  	tw.AppendSeparator() // doesn't make any difference
  1250  	tw.AppendSeparator() // doesn't make any difference
  1251  	tw.AppendSeparator() // doesn't make any difference
  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