1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
|
package tests // Use _test package to test as a user
import (
"bytes"
"encoding/csv"
"strings"
"testing"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/renderer" // For direct renderer use if needed
"github.com/olekukonko/tablewriter/tw"
)
const csvTestData = `Name,Department,Salary
Alice,Engineering,120000
Bob,Marketing,85000
Charlie,Engineering,135000
Diana,HR,70000
`
func getCSVReaderFromString(data string) *csv.Reader {
stringReader := strings.NewReader(data)
return csv.NewReader(stringReader)
}
func TestTable_Configure_Basic(t *testing.T) {
var buf bytes.Buffer
csvReader := getCSVReaderFromString(csvTestData)
table, err := tablewriter.NewCSVReader(&buf, csvReader, true)
if err != nil {
t.Fatalf("NewCSVReader failed: %v", err)
}
// Check initial default config values (examples)
if table.Config().Header.Alignment.Global != tw.AlignCenter {
t.Errorf("Expected initial header alignment to be Center, got %s", table.Config().Header.Alignment.Global)
}
if table.Config().Behavior.TrimSpace != tw.On { // Default from defaultConfig()
t.Errorf("Expected initial TrimSpace to be On, got %s", table.Config().Behavior.TrimSpace)
}
table.Configure(func(cfg *tablewriter.Config) {
cfg.Header.Alignment.Global = tw.AlignLeft
cfg.Row.Alignment.Global = tw.AlignRight
cfg.Behavior.TrimSpace = tw.Off
cfg.Debug = true // This should enable the logger
})
// Check that Table.config was updated
if table.Config().Header.Alignment.Global != tw.AlignLeft {
t.Errorf("Expected configured header alignment to be Left, got %s", table.Config().Header.Alignment.Global)
}
if table.Config().Row.Alignment.Global != tw.AlignRight {
t.Errorf("Expected configured row alignment to be Right, got %s", table.Config().Row.Alignment.Global)
}
if table.Config().Behavior.TrimSpace != tw.Off {
t.Errorf("Expected configured TrimSpace to be Off, got %s", table.Config().Behavior.TrimSpace)
}
if !table.Config().Debug {
t.Errorf("Expected configured Debug to be true")
}
// Render and check output (visual check will confirm alignment and trimming)
err = table.Render()
if err != nil {
t.Fatalf("Render failed: %v", err)
}
// What visualCheck will see (assuming Blueprint respects CellContext.Align passed from table.config):
expectedAfterConfigure := `
┌─────────┬─────────────┬────────┐
│ NAME │ DEPARTMENT │ SALARY │
├─────────┼─────────────┼────────┤
│ Alice │ Engineering │ 120000 │
│ Bob │ Marketing │ 85000 │
│ Charlie │ Engineering │ 135000 │
│ Diana │ HR │ 70000 │
└─────────┴─────────────┴────────┘
`
if !visualCheck(t, "TestTable_Configure_Basic", buf.String(), expectedAfterConfigure) {
t.Logf("Debug trace from table:\n%s", table.Debug().String())
}
}
func TestTable_Options_WithRendition_Borderless(t *testing.T) {
var buf bytes.Buffer
csvReader := getCSVReaderFromString(csvTestData) // Ensure csvTestData is defined
table, err := tablewriter.NewCSVReader(&buf, csvReader, true, tablewriter.WithDebug(true))
if err != nil {
t.Fatalf("NewCSVReader failed: %v", err)
}
// Initially, it should have default borders
table.Render()
initialOutputWithBorders := buf.String()
buf.Reset() // Clear buffer for next render
if !strings.Contains(initialOutputWithBorders, "┌───") { // Basic check for default border
t.Errorf("Expected initial render to have borders, but got:\n%s", initialOutputWithBorders)
}
// Define a TRULY borderless and line-less rendition
borderlessRendition := tw.Rendition{
Borders: tw.Border{ // Explicitly set all borders to Off
Left: tw.Off,
Right: tw.Off,
Top: tw.Off,
Bottom: tw.Off,
},
// Using StyleNone for symbols means no visible characters for borders/lines if they were on.
// For a "markdown-like but no lines" look, you might use StyleMarkdown and then turn off lines/separators.
// For true "no visual structure", StyleNone is good.
Symbols: tw.NewSymbols(tw.StyleNone),
Settings: tw.Settings{
Lines: tw.Lines{ // Explicitly set all line drawing to Off
ShowTop: tw.Off,
ShowBottom: tw.Off,
ShowHeaderLine: tw.Off,
ShowFooterLine: tw.Off,
},
Separators: tw.Separators{ // Explicitly set all separators to Off
ShowHeader: tw.Off,
ShowFooter: tw.Off,
BetweenRows: tw.Off,
BetweenColumns: tw.Off,
},
},
}
table.Options(
tablewriter.WithRendition(borderlessRendition),
)
// Render again
err = table.Render()
if err != nil {
t.Fatalf("Render after WithRendition failed: %v", err)
}
// Expected output: Plain text, no borders, no lines, no separators.
// Content alignment will be default (Header:Center, Row:Left) because
// Table.config was not modified for alignments in this test.
expectedOutputBorderless := `
NAME DEPARTMENT SALARY
Alice Engineering 120000
Bob Marketing 85000
Charlie Engineering 135000
Diana HR 70000
`
if !visualCheck(t, "TestTable_Options_WithRendition_Borderless", buf.String(), expectedOutputBorderless) {
t.Logf("Initial output with borders was:\n%s", initialOutputWithBorders) // For context
t.Logf("Debug trace from table after borderless rendition:\n%s", table.Debug().String())
}
// Verify renderer's internal config was changed
if bp, ok := table.Renderer().(*renderer.Blueprint); ok {
currentRendererCfg := bp.Config()
if currentRendererCfg.Borders.Left.Enabled() {
t.Errorf("Blueprint Borders.Left should be OFF, but is ON")
}
if currentRendererCfg.Borders.Right.Enabled() {
t.Errorf("Blueprint Borders.Right should be OFF, but is ON")
}
if currentRendererCfg.Borders.Top.Enabled() {
t.Errorf("Blueprint Borders.Top should be OFF, but is ON")
}
if currentRendererCfg.Borders.Bottom.Enabled() {
t.Errorf("Blueprint Borders.Bottom should be OFF, but is ON")
}
if currentRendererCfg.Settings.Lines.ShowHeaderLine.Enabled() {
t.Errorf("Blueprint Settings.Lines.ShowHeaderLine should be OFF, but is ON")
}
if currentRendererCfg.Settings.Lines.ShowTop.Enabled() {
t.Errorf("Blueprint Settings.Lines.ShowTop should be OFF, but is ON")
}
if currentRendererCfg.Settings.Separators.BetweenColumns.Enabled() {
t.Errorf("Blueprint Settings.Separators.BetweenColumns should be OFF, but is ON")
}
// Check symbols if relevant (StyleNone should have empty symbols)
if currentRendererCfg.Symbols.Column() != "" {
t.Errorf("Blueprint Symbols.Column should be empty for StyleNone, got '%s'", currentRendererCfg.Symbols.Column())
}
} else {
t.Logf("Renderer is not *renderer.Blueprint, skipping detailed internal config check. Type is %T", table.Renderer())
}
}
// Assume csvTestData and getCSVReaderFromString are defined as in previous examples:
const csvTestDataForPartial = `Name,Department,Salary
Alice,Engineering,120000
Bob,Marketing,85000
`
func getCSVReaderFromStringForPartial(data string) *csv.Reader {
stringReader := strings.NewReader(data)
return csv.NewReader(stringReader)
}
// Assume csvTestDataForPartial and getCSVReaderFromStringForPartial are defined
const csvTestDataForPartialUpdate = `Name,Department,Salary
Alice,Engineering,120000
Bob,Marketing,85000
`
func getCSVReaderFromStringForPartialUpdate(data string) *csv.Reader {
stringReader := strings.NewReader(data)
return csv.NewReader(stringReader)
}
func TestTable_Options_WithRendition_PartialUpdate(t *testing.T) {
var buf bytes.Buffer
csvReader := getCSVReaderFromStringForPartialUpdate(csvTestDataForPartialUpdate)
// 1. Define an explicitly borderless and line-less initial rendition
initiallyAllOffRendition := tw.Rendition{
Borders: tw.Border{
Left: tw.Off,
Right: tw.Off,
Top: tw.Off,
Bottom: tw.Off,
},
Symbols: tw.NewSymbols(tw.StyleNone), // StyleNone should render no visible symbols
Settings: tw.Settings{
Lines: tw.Lines{
ShowTop: tw.Off,
ShowBottom: tw.Off,
ShowHeaderLine: tw.Off,
ShowFooterLine: tw.Off,
},
Separators: tw.Separators{
ShowHeader: tw.Off,
ShowFooter: tw.Off,
BetweenRows: tw.Off,
BetweenColumns: tw.Off,
},
},
}
// Create table with this explicitly "all off" rendition
table, err := tablewriter.NewCSVReader(&buf, csvReader, true,
tablewriter.WithDebug(true),
tablewriter.WithRenderer(renderer.NewBlueprint(initiallyAllOffRendition)),
)
if err != nil {
t.Fatalf("NewCSVReader with initial 'all off' rendition failed: %v", err)
}
// Render to confirm initial state (should be very plain)
table.Render()
outputAfterInitialAllOff := buf.String()
buf.Reset() // Clear buffer for the next render
// Check the initial plain output (content only, no borders/lines)
expectedInitialPlainOutput := `
NAME DEPARTMENT SALARY
Alice Engineering 120000
Bob Marketing 85000
`
if !visualCheck(t, "TestTable_Options_WithRendition_PartialUpdate_InitialState", outputAfterInitialAllOff, expectedInitialPlainOutput) {
t.Errorf("Initial render was not plain as expected.")
t.Logf("Initial 'all off' output was:\n%s", outputAfterInitialAllOff)
}
partialRenditionUpdate := tw.Rendition{
Borders: tw.Border{Top: tw.On, Bottom: tw.On}, // Left/Right are 0 (unspecified in this struct literal)
Symbols: tw.NewSymbols(tw.StyleHeavy),
Settings: tw.Settings{
Lines: tw.Lines{ShowTop: tw.On, ShowBottom: tw.On}, // Enable drawing of these lines
// Separators are zero-value, so they will remain Off from 'initiallyAllOffRendition'
},
}
// Apply the partial update using Options
table.Options(
tablewriter.WithRendition(partialRenditionUpdate),
)
// Render again
err = table.Render()
if err != nil {
t.Fatalf("Render after partial WithRendition failed: %v", err)
}
outputAfterPartialUpdate := buf.String()
expectedOutputPartialBorders := `
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NAME DEPARTMENT SALARY
Alice Engineering 120000
Bob Marketing 85000
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
`
if !visualCheck(t, "TestTable_Options_WithRendition_PartialUpdate_FinalState", outputAfterPartialUpdate, expectedOutputPartialBorders) {
t.Logf("Initial 'all off' output was:\n%s", outputAfterInitialAllOff) // For context
t.Logf("Debug trace from table after partial update:\n%s", table.Debug().String())
}
// 3. Verify the renderer's internal configuration reflects the partial update correctly.
if bp, ok := table.Renderer().(*renderer.Blueprint); ok {
currentRendererCfg := bp.Config()
if !currentRendererCfg.Borders.Top.Enabled() {
t.Errorf("Blueprint Borders.Top should be ON, but is OFF")
}
if !currentRendererCfg.Borders.Bottom.Enabled() {
t.Errorf("Blueprint Borders.Bottom should be ON, but is OFF")
}
if currentRendererCfg.Borders.Left.Enabled() {
t.Errorf("Blueprint Borders.Left should remain OFF, but is ON")
}
if currentRendererCfg.Borders.Right.Enabled() {
t.Errorf("Blueprint Borders.Right should remain OFF, but is ON")
}
if currentRendererCfg.Symbols.Row() != "━" { // From StyleHeavy
t.Errorf("Blueprint Symbols.Row is not '━' (Heavy), got '%s'", currentRendererCfg.Symbols.Row())
}
// Column symbol check might be less relevant if BetweenColumns is Off, but good for completeness.
if currentRendererCfg.Symbols.Column() != "┃" { // From StyleHeavy
t.Errorf("Blueprint Symbols.Column is not '┃' (Heavy), got '%s'", currentRendererCfg.Symbols.Column())
}
// Check Settings.Lines
if !currentRendererCfg.Settings.Lines.ShowTop.Enabled() {
t.Errorf("Blueprint Settings.Lines.ShowTop should be ON, but is OFF")
}
if !currentRendererCfg.Settings.Lines.ShowBottom.Enabled() {
t.Errorf("Blueprint Settings.Lines.ShowBottom should be ON, but is OFF")
}
if currentRendererCfg.Settings.Lines.ShowHeaderLine.Enabled() {
t.Errorf("Blueprint Settings.Lines.ShowHeaderLine should remain OFF, but is ON")
}
// Check Settings.Separators
if currentRendererCfg.Settings.Separators.BetweenColumns.Enabled() {
t.Errorf("Blueprint Settings.Separators.BetweenColumns should remain OFF, but is ON")
}
} else {
t.Logf("Renderer is not *renderer.Blueprint, skipping detailed internal config check. Type is %T", table.Renderer())
}
}
|