Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions LEARNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,21 @@ The table system goes beyond simple formatting to provide hardware-aware renderi
- **Font A Enforcement**: Consistent 12-dot character width for predictable layouts
- **Paper Support**: 58mm (32 chars) and 80mm (48 chars) at 203 DPI

### 🧪 Quality Assurance & Testing

- **Table-Driven Testing**: Implemented comprehensive table-driven unit tests for the `TableEngine` to verify rendering logic, ensuring support for:
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section refers to a TableEngine, but the implementation in pkg/tables is TabEngine (constructed via tables.NewEngine). Updating the name here would avoid confusion for readers trying to map the docs to the actual code.

Suggested change
- **Table-Driven Testing**: Implemented comprehensive table-driven unit tests for the `TableEngine` to verify rendering logic, ensuring support for:
- **Table-Driven Testing**: Implemented comprehensive table-driven unit tests for the `TabEngine` to verify rendering logic, ensuring support for:

Copilot uses AI. Check for mistakes.
- Header visibility toggling (explicit vs. data-driven).
- Column alignment (Left, Center, Right) with precise padding calculations.
- Word wrapping functionality for long text.
- Edge cases like nil data, invalid definitions, and empty rows.
Comment on lines +167 to +170
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bullets here overstate what the added unit tests currently cover (e.g., “explicit vs. data-driven” header toggling and “empty rows”). Either add test cases that exercise those scenarios (like opts.ShowHeaders=true with data.ShowHeaders=false, and Rows: nil/[]) or adjust the wording to match the actual coverage.

Suggested change
- Header visibility toggling (explicit vs. data-driven).
- Column alignment (Left, Center, Right) with precise padding calculations.
- Word wrapping functionality for long text.
- Edge cases like nil data, invalid definitions, and empty rows.
- Header visibility toggling based on configuration.
- Column alignment (Left, Center, Right) with precise padding calculations.
- Word wrapping functionality for long text.
- Edge cases like nil data and invalid definitions.

Copilot uses AI. Check for mistakes.
- **Mocking & Buffering**: Utilized `bytes.Buffer` as an `io.Writer` to capture and inspect ESC/POS command output without physical hardware, enabling deterministic verification of control codes (e.g., bold toggling `ESC E`).

```go
// Example: Table that would overflow gets auto-reduced
// Original: [20, 5, 12] = 39 chars (exceeds 32 max for 58mm)
// After: [13, 5, 12] = 32 chars (fits perfectly)
// Log: "Table auto-reduced: 39 → 32 chars (7 reductions applied)"
```

## Module Dependencies

Expand Down
178 changes: 178 additions & 0 deletions pkg/tables/table_engine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package tables

import (
"bytes"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/adcondev/poster/pkg/constants"
)
Comment on lines +1 to +12
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new test file uses package tables and testify, while the other tests in pkg/tables consistently use the external test package (package tables_test) and the standard testing assertions. For consistency (and to keep tests focused on the public API), consider switching this file to package tables_test and aligning assertion style with the rest of the pkg/tables tests (or standardize the whole package on testify).

Copilot uses AI. Check for mistakes.

// TestRender tests the TabEngine.Render method
func TestRender(t *testing.T) {
// Common test data
def := Definition{
Columns: []Column{
{Name: "Item", Width: 10, Align: constants.Left},
{Name: "Qty", Width: 5, Align: constants.Center},
{Name: "Price", Width: 8, Align: constants.Right},
},
}

tests := []struct {
name string
data *Data
opts *Options
expected []string // Expected substrings
unexpected []string // Unexpected substrings
expectError bool
errorMsg string
}{
{
name: "Render Basic Table with Headers",
data: &Data{
Definition: def,
ShowHeaders: true,
Rows: []Row{
{"Apple", "10", "1.50"},
},
},
opts: DefaultOptions(),
expected: []string{
"\x1bE\x01", // Enable Bold
"Item Qty Price", // Header row
"\x1bE\x00", // Disable Bold
"Apple 10 1.50", // Data row
},
expectError: false,
},
{
name: "Render Table without Headers",
data: &Data{
Definition: def,
ShowHeaders: false,
Rows: []Row{
{"Banana", "5", "0.99"},
},
},
opts: &Options{
PaperWidth: 80,
ShowHeaders: false,
HeaderStyle: Style{Bold: true},
WordWrap: true,
ColumnSpacing: 1,
},
expected: []string{
"Banana 5 0.99", // Data row
},
unexpected: []string{
"Item", "Qty", "Price", // Headers should not be present
"\x1bE\x01", // No bold command
},
expectError: false,
},
{
name: "Render Table with Alignment",
data: &Data{
Definition: def,
Rows: []Row{
{"Left", "Cnt", "Right"},
},
},
opts: &Options{
PaperWidth: 80,
ShowHeaders: false,
HeaderStyle: Style{Bold: true},
WordWrap: true,
ColumnSpacing: 1,
},
expected: []string{
"Left Cnt Right",
},
expectError: false,
},
{
name: "Render Table with Word Wrap",
data: &Data{
Definition: Definition{
Columns: []Column{
{Name: "Desc", Width: 5, Align: constants.Left},
},
},
Rows: []Row{
{"Long Text"}, // Should wrap to "Long " and "Text "
},
},
opts: DefaultOptions(),
expected: []string{
"Long ",
"Text ",
},
expectError: false,
},
{
name: "Error: Nil Data",
data: nil,
opts: DefaultOptions(),
expectError: true,
errorMsg: "table data cannot be nil",
},
{
name: "Error: Invalid Data (Row Length Mismatch)",
data: &Data{
Definition: def,
Rows: []Row{
{"Apple", "10"}, // Missing one column
},
},
opts: DefaultOptions(),
expectError: true,
errorMsg: "invalid table data",
},
{
name: "Error: Invalid Data (Empty definition)",
data: &Data{
Definition: Definition{},
Rows: []Row{
{"Apple"},
},
},
opts: DefaultOptions(),
expectError: true,
errorMsg: "table must have at least one column",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
engine := NewEngine(&def, tt.opts)
var buf bytes.Buffer

err := engine.Render(&buf, tt.data)

if tt.expectError {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.errorMsg)
} else {
require.NoError(t, err)
output := buf.String()

for _, exp := range tt.expected {
assert.Contains(t, output, exp, "Output should contain expected string")
}

for _, unexp := range tt.unexpected {
assert.NotContains(t, output, unexp, "Output should NOT contain unexpected string")
}

// Verify Line Feeds are present
if len(tt.expected) > 0 {
assert.True(t, strings.HasSuffix(output, "\n"), "Output should end with a newline")
}
}
})
}
}
Loading