This commit is contained in:
@@ -0,0 +1,553 @@
|
||||
package wpdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTableBuilderInitialState(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
tb := b.Table()
|
||||
|
||||
if tb == nil {
|
||||
t.Fatal("Table() returned nil")
|
||||
}
|
||||
if tb.builder != b {
|
||||
t.Error("builder back-reference not set")
|
||||
}
|
||||
if tb.padx != 2 {
|
||||
t.Errorf("default padx = %v, want 2", tb.padx)
|
||||
}
|
||||
if tb.pady != 2 {
|
||||
t.Errorf("default pady = %v, want 2", tb.pady)
|
||||
}
|
||||
if tb.defaultCellStyle == nil {
|
||||
t.Error("default cell style is nil")
|
||||
}
|
||||
if tb.RowCount() != 0 {
|
||||
t.Errorf("RowCount = %v, want 0", tb.RowCount())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderConfig(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := NewTableCellStyleOpt()
|
||||
tb := b.Table().
|
||||
PadX(5).
|
||||
PadY(7).
|
||||
Widths("10", "20", "auto").
|
||||
DefaultStyle(style).
|
||||
Debug(true)
|
||||
|
||||
if tb.padx != 5 {
|
||||
t.Errorf("padx = %v, want 5", tb.padx)
|
||||
}
|
||||
if tb.pady != 7 {
|
||||
t.Errorf("pady = %v, want 7", tb.pady)
|
||||
}
|
||||
if tb.columnWidths == nil || len(*tb.columnWidths) != 3 {
|
||||
t.Errorf("columnWidths not set correctly: %v", tb.columnWidths)
|
||||
}
|
||||
if tb.defaultCellStyle != style {
|
||||
t.Error("defaultCellStyle not set")
|
||||
}
|
||||
if tb.debug == nil || !*tb.debug {
|
||||
t.Error("debug not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderAddRowDefault(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
tb := b.Table().AddRowDefaultStyle("a", "b", "c")
|
||||
if tb.RowCount() != 1 {
|
||||
t.Errorf("RowCount = %v, want 1", tb.RowCount())
|
||||
}
|
||||
if len(tb.rows[0].cells) != 3 {
|
||||
t.Errorf("cells = %v, want 3", len(tb.rows[0].cells))
|
||||
}
|
||||
if tb.rows[0].cells[0].Content != "a" {
|
||||
t.Errorf("cell[0].Content = %q, want %q", tb.rows[0].cells[0].Content, "a")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderAddRowWithStyle(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := NewTableCellStyleOpt().Bold().FontSize(10)
|
||||
|
||||
tb := b.Table().AddRowWithStyle(style, "x", "y")
|
||||
if tb.RowCount() != 1 {
|
||||
t.Fatalf("RowCount = %v, want 1", tb.RowCount())
|
||||
}
|
||||
if len(tb.rows[0].cells) != 2 {
|
||||
t.Fatalf("cells = %v, want 2", len(tb.rows[0].cells))
|
||||
}
|
||||
for i, c := range tb.rows[0].cells {
|
||||
if c.Style.fontStyleOverride == nil || *c.Style.fontStyleOverride != Bold {
|
||||
t.Errorf("cell[%d] style not Bold", i)
|
||||
}
|
||||
if c.Style.fontSizeOverride == nil || *c.Style.fontSizeOverride != 10 {
|
||||
t.Errorf("cell[%d] fontSize not 10", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderAddRow(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
tc := TableCell{Content: "hello", Style: TableCellStyleOpt{}}
|
||||
tb := b.Table().AddRow(tc, tc)
|
||||
if tb.RowCount() != 1 || len(tb.rows[0].cells) != 2 {
|
||||
t.Errorf("AddRow did not add expected cells")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderBuildRowFlow(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
|
||||
tb := b.Table().
|
||||
BuildRow().Cell("a").Cell("b").Cell("c").BuildRow().
|
||||
BuildRow().Cells("d", "e", "f").BuildRow()
|
||||
|
||||
if tb.RowCount() != 2 {
|
||||
t.Errorf("RowCount = %v, want 2", tb.RowCount())
|
||||
}
|
||||
if len(tb.rows[0].cells) != 3 || len(tb.rows[1].cells) != 3 {
|
||||
t.Errorf("each row should have 3 cells; got %d and %d",
|
||||
len(tb.rows[0].cells), len(tb.rows[1].cells))
|
||||
}
|
||||
if tb.rows[0].cells[0].Content != "a" || tb.rows[1].cells[2].Content != "f" {
|
||||
t.Error("cell content mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableRowBuilderCellWithStyle(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := NewTableCellStyleOpt().Bold()
|
||||
tb := b.Table().BuildRow().CellWithStyle("x", style).BuildRow()
|
||||
|
||||
if tb.rows[0].cells[0].Style.fontStyleOverride == nil ||
|
||||
*tb.rows[0].cells[0].Style.fontStyleOverride != Bold {
|
||||
t.Error("cell style not applied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableRowBuilderCellObjects(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
c1 := TableCell{Content: "alpha"}
|
||||
c2 := TableCell{Content: "beta"}
|
||||
|
||||
tb := b.Table().BuildRow().CellObject(c1).CellObjects(c2).BuildRow()
|
||||
if len(tb.rows[0].cells) != 2 {
|
||||
t.Fatalf("cells = %v, want 2", len(tb.rows[0].cells))
|
||||
}
|
||||
if tb.rows[0].cells[0].Content != "alpha" || tb.rows[0].cells[1].Content != "beta" {
|
||||
t.Error("cell objects not added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableRowBuilderRowStyle(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
rowStyle := NewTableCellStyleOpt().Italic()
|
||||
|
||||
tb := b.Table().BuildRow().RowStyle(rowStyle).Cell("x").Cell("y").BuildRow()
|
||||
|
||||
for i, c := range tb.rows[0].cells {
|
||||
if c.Style.fontStyleOverride == nil || *c.Style.fontStyleOverride != Italic {
|
||||
t.Errorf("cell[%d] should use row style (Italic)", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableMaxFontSize(t *testing.T) {
|
||||
row := tableRow{
|
||||
cells: []TableCell{
|
||||
{Style: *NewTableCellStyleOpt().FontSize(10)},
|
||||
{Style: *NewTableCellStyleOpt().FontSize(20)},
|
||||
{Style: *NewTableCellStyleOpt().FontSize(15)},
|
||||
},
|
||||
}
|
||||
got := row.maxFontSize(8)
|
||||
if got != 20 {
|
||||
t.Errorf("maxFontSize = %v, want 20", got)
|
||||
}
|
||||
|
||||
rowEmpty := tableRow{cells: []TableCell{{Style: TableCellStyleOpt{}}}}
|
||||
got = rowEmpty.maxFontSize(12)
|
||||
if got != 12 {
|
||||
t.Errorf("maxFontSize default = %v, want 12", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderBuildEmpty(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
// Should not panic when no rows
|
||||
b.Table().Build()
|
||||
}
|
||||
|
||||
func TestTableBuilderBuildNumeric(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("30", "30", "30").
|
||||
AddRowDefaultStyle("a", "b", "c").
|
||||
AddRowDefaultStyle("d", "e", "f").
|
||||
Build()
|
||||
|
||||
bin, err := b.Build()
|
||||
if err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
if !bytes.HasPrefix(bin, []byte("%PDF-")) {
|
||||
t.Error("output not a PDF")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderBuildAuto(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("auto", "auto", "auto").
|
||||
AddRowDefaultStyle("a", "b", "c").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderBuildFr(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("1fr", "2fr", "*").
|
||||
AddRowDefaultStyle("a", "b", "c").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderMixedColumnSpecs(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("auto", "30", "1fr", "*").
|
||||
AddRowDefaultStyle("a", "b", "c", "d").
|
||||
AddRowDefaultStyle("longer text here", "x", "y", "z").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderNoColumnsDefined(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
// When Widths not specified, table uses "*" for each cell of first row
|
||||
b.Table().
|
||||
AddRowDefaultStyle("a", "b").
|
||||
AddRowDefaultStyle("c", "d").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderMismatchedColumnCount(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("10", "10").
|
||||
AddRowDefaultStyle("a", "b", "c"). // wrong column count
|
||||
Build()
|
||||
|
||||
// Should produce an error in the underlying gofpdf, surfaced by Build()
|
||||
_, err := b.Build()
|
||||
if err == nil {
|
||||
t.Error("expected error for mismatched column count, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderInvalidColumnWidth(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.Table().
|
||||
Widths("not-a-number").
|
||||
AddRowDefaultStyle("a").
|
||||
Build()
|
||||
|
||||
_, err := b.Build()
|
||||
if err == nil {
|
||||
t.Error("expected error for invalid column width, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderMultiCellRow(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := NewTableCellStyleOpt().MultiCell(true)
|
||||
b.Table().
|
||||
Widths("auto", "1fr").
|
||||
AddRowWithStyle(style, "a", "Multi line\ntext content").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderEllipsizeRow(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := NewTableCellStyleOpt().MultiCell(false).Ellipsize(true)
|
||||
b.Table().
|
||||
Widths("20").
|
||||
AddRowWithStyle(style, "this is a very long text that should be ellipsized").
|
||||
Build()
|
||||
|
||||
if _, err := b.Build(); err != nil {
|
||||
t.Fatalf("Build error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBuilderDefaultTableStyle(t *testing.T) {
|
||||
s := defaultTableStyle()
|
||||
if s == nil {
|
||||
t.Fatal("defaultTableStyle returned nil")
|
||||
}
|
||||
if s.minWidth == nil || *s.minWidth != 5 {
|
||||
t.Errorf("default minWidth = %v, want 5", s.minWidth)
|
||||
}
|
||||
if s.ellipsize == nil || !*s.ellipsize {
|
||||
t.Errorf("default ellipsize should be true")
|
||||
}
|
||||
if s.multiCell == nil || *s.multiCell {
|
||||
t.Errorf("default multiCell should be false")
|
||||
}
|
||||
if s.fontSizeOverride == nil || *s.fontSizeOverride != 8 {
|
||||
t.Errorf("default font size = %v, want 8", s.fontSizeOverride)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptBuilders(t *testing.T) {
|
||||
o := NewTableCellStyleOpt().
|
||||
MultiCell(true).
|
||||
Ellipsize(false).
|
||||
PaddingHorz(3).
|
||||
MinWidth(7).
|
||||
FillHeight(true)
|
||||
|
||||
if !*o.multiCell {
|
||||
t.Error("multiCell")
|
||||
}
|
||||
if *o.ellipsize {
|
||||
t.Error("ellipsize")
|
||||
}
|
||||
if *o.paddingHorz != 3 {
|
||||
t.Error("paddingHorz")
|
||||
}
|
||||
if *o.minWidth != 7 {
|
||||
t.Error("minWidth")
|
||||
}
|
||||
if !*o.fillHeight {
|
||||
t.Error("fillHeight")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptCellStyle(t *testing.T) {
|
||||
cell := *NewPDFCellOpt().Width(50).Bold()
|
||||
o := NewTableCellStyleOpt().CellStyle(cell)
|
||||
|
||||
if o.PDFCellOpt.width == nil || *o.PDFCellOpt.width != 50 {
|
||||
t.Error("CellStyle did not transfer width")
|
||||
}
|
||||
if o.PDFCellOpt.fontStyleOverride == nil || *o.PDFCellOpt.fontStyleOverride != Bold {
|
||||
t.Error("CellStyle did not transfer style")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptDelegates(t *testing.T) {
|
||||
o := NewTableCellStyleOpt().
|
||||
Width(10).
|
||||
Height(20).
|
||||
Border(BorderFull).
|
||||
LnPos(BreakToBelow).
|
||||
Align(AlignRight).
|
||||
FillBackground(true).
|
||||
Link(5).
|
||||
LinkStr("link").
|
||||
Font(FontTimes, Bold, 11).
|
||||
LnAfter(2).
|
||||
X(3).
|
||||
AutoWidth().
|
||||
AutoWidthPaddingX(1).
|
||||
TextColor(1, 2, 3).
|
||||
BorderColor(4, 5, 6).
|
||||
FillColor(7, 8, 9).
|
||||
Alpha(0.5, BlendNormal).
|
||||
Debug(true)
|
||||
|
||||
if *o.PDFCellOpt.width != 10 {
|
||||
t.Error("width")
|
||||
}
|
||||
if *o.PDFCellOpt.height != 20 {
|
||||
t.Error("height")
|
||||
}
|
||||
if *o.PDFCellOpt.border != BorderFull {
|
||||
t.Error("border")
|
||||
}
|
||||
if *o.PDFCellOpt.ln != BreakToBelow {
|
||||
t.Error("ln")
|
||||
}
|
||||
if *o.PDFCellOpt.align != AlignRight {
|
||||
t.Error("align")
|
||||
}
|
||||
if !*o.PDFCellOpt.fill {
|
||||
t.Error("fill")
|
||||
}
|
||||
if *o.PDFCellOpt.link != 5 {
|
||||
t.Error("link")
|
||||
}
|
||||
if *o.PDFCellOpt.linkStr != "link" {
|
||||
t.Error("linkStr")
|
||||
}
|
||||
if *o.PDFCellOpt.fontNameOverride != FontTimes {
|
||||
t.Error("fontName")
|
||||
}
|
||||
if *o.PDFCellOpt.fontStyleOverride != Bold {
|
||||
t.Error("fontStyle")
|
||||
}
|
||||
if *o.PDFCellOpt.fontSizeOverride != 11 {
|
||||
t.Error("fontSize")
|
||||
}
|
||||
if *o.PDFCellOpt.extraLn != 2 {
|
||||
t.Error("extraLn")
|
||||
}
|
||||
if *o.PDFCellOpt.x != 3 {
|
||||
t.Error("x")
|
||||
}
|
||||
if !*o.PDFCellOpt.autoWidth {
|
||||
t.Error("autoWidth")
|
||||
}
|
||||
if *o.PDFCellOpt.autoWidthPaddingX != 1 {
|
||||
t.Error("autoWidthPaddingX")
|
||||
}
|
||||
if *o.PDFCellOpt.textColor != (PDFColor{R: 1, G: 2, B: 3}) {
|
||||
t.Error("textColor")
|
||||
}
|
||||
if *o.PDFCellOpt.borderColor != (PDFColor{R: 4, G: 5, B: 6}) {
|
||||
t.Error("borderColor")
|
||||
}
|
||||
if *o.PDFCellOpt.fillColor != (PDFColor{R: 7, G: 8, B: 9}) {
|
||||
t.Error("fillColor")
|
||||
}
|
||||
if o.PDFCellOpt.alphaOverride.V1 != 0.5 || o.PDFCellOpt.alphaOverride.V2 != BlendNormal {
|
||||
t.Error("alpha")
|
||||
}
|
||||
if !*o.PDFCellOpt.debug {
|
||||
t.Error("debug")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptBoldItalic(t *testing.T) {
|
||||
if *NewTableCellStyleOpt().Bold().PDFCellOpt.fontStyleOverride != Bold {
|
||||
t.Error("Bold")
|
||||
}
|
||||
if *NewTableCellStyleOpt().Italic().PDFCellOpt.fontStyleOverride != Italic {
|
||||
t.Error("Italic")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptHexColors(t *testing.T) {
|
||||
o := NewTableCellStyleOpt().
|
||||
TextColorHex(0x010203).
|
||||
BorderColorHex(0x040506).
|
||||
FillColorHex(0x070809)
|
||||
|
||||
if *o.PDFCellOpt.textColor != (PDFColor{R: 1, G: 2, B: 3}) {
|
||||
t.Error("text hex")
|
||||
}
|
||||
if *o.PDFCellOpt.borderColor != (PDFColor{R: 4, G: 5, B: 6}) {
|
||||
t.Error("border hex")
|
||||
}
|
||||
if *o.PDFCellOpt.fillColor != (PDFColor{R: 7, G: 8, B: 9}) {
|
||||
t.Error("fill hex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellStyleOptIndividualFontSetters(t *testing.T) {
|
||||
o := NewTableCellStyleOpt().FontName(FontCourier).FontStyle(Italic).FontSize(9)
|
||||
if *o.PDFCellOpt.fontNameOverride != FontCourier {
|
||||
t.Error("FontName")
|
||||
}
|
||||
if *o.PDFCellOpt.fontStyleOverride != Italic {
|
||||
t.Error("FontStyle")
|
||||
}
|
||||
if *o.PDFCellOpt.fontSizeOverride != 9 {
|
||||
t.Error("FontSize")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCalculateColumnsNumeric(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
tb := b.Table().
|
||||
Widths("30", "20", "40").
|
||||
AddRowDefaultStyle("a", "b", "c")
|
||||
|
||||
w := tb.calculateColumns()
|
||||
if len(w) != 3 {
|
||||
t.Fatalf("widths = %v, want 3", len(w))
|
||||
}
|
||||
if w[0] != 30 || w[1] != 20 || w[2] != 40 {
|
||||
t.Errorf("widths = %v, want [30, 20, 40]", w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCalculateColumnsFrSplit(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
b.SetMargins(PDFMargins{Left: 0, Top: 0, Right: 0})
|
||||
|
||||
tb := b.Table().
|
||||
PadX(0).
|
||||
Widths("1fr", "1fr").
|
||||
AddRowDefaultStyle("a", "b")
|
||||
|
||||
pageW := b.GetPageWidth()
|
||||
w := tb.calculateColumns()
|
||||
if len(w) != 2 {
|
||||
t.Fatalf("widths = %v, want 2", len(w))
|
||||
}
|
||||
|
||||
// fr columns are bounded by autoWidths (max content); since "a" and "b"
|
||||
// are very narrow strings, both columns get the same auto-bounded width.
|
||||
if math.Abs(w[0]-w[1]) > 0.01 {
|
||||
t.Errorf("expected fr split widths roughly equal, got %v and %v", w[0], w[1])
|
||||
}
|
||||
|
||||
// Total should not exceed available page width.
|
||||
if w[0]+w[1] > pageW+0.01 {
|
||||
t.Errorf("total width %v exceeds pageW %v", w[0]+w[1], pageW)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCalculateColumnsAutoUsesMinWidth(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
style := *NewTableCellStyleOpt()
|
||||
mw := 50.0
|
||||
style.minWidth = &mw
|
||||
|
||||
tb := b.Table().
|
||||
Widths("auto").
|
||||
AddRowWithStyle(&style, "x")
|
||||
|
||||
w := tb.calculateColumns()
|
||||
if len(w) != 1 {
|
||||
t.Fatalf("widths = %v, want 1", len(w))
|
||||
}
|
||||
if w[0] < 50 {
|
||||
t.Errorf("width %v should respect minWidth=50", w[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCalculateColumnsNoRows(t *testing.T) {
|
||||
b := newBuilderWithPage(t)
|
||||
tb := b.Table()
|
||||
w := tb.calculateColumns()
|
||||
if len(w) != 0 {
|
||||
t.Errorf("widths for empty table = %v, want []", w)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user