File: csv_test.go

package info (click to toggle)
golang-github-olekukonko-tablewriter 1.0.9-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 1,380 kB
  • sloc: makefile: 4
file content (346 lines) | stat: -rw-r--r-- 12,666 bytes parent folder | download
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())
	}
}