Files
bamort/backend/pdfrender/render_with_continuation_test.go
Frank 59fe69d35d refactor: Unify PDF pagination system and rename templates
BREAKING CHANGE: Template names changed from page1_stats.html to page_1.html

## Phase 1: Unified Pagination Function
- Implemented PaginateMultiList() to replace PaginateSkills(), PaginateSpells(), and PaginatePage2PlayLists()
- Single metadata-driven function handles all list types (skills, weapons, spells, equipment)
- Properly handles filters (learned/unlearned/language) via template metadata
- Shares list trackers by ListType+Filter combination to avoid duplication
- Added comprehensive tests for all edge cases

## Phase 2: Template Naming Convention
- Renamed templates to be data-agnostic:
  - page1_stats.html -> page_1.html
  - page1.2_stats.html -> page_1.2.html
  - page2_play.html -> page_2.html
  - page2.2_play.html -> page_2.2.html
  - page3_spell.html -> page_3.html
  - page3.2_spell.html -> page_3.2.html
  - page4_equip.html -> page_4.html
- Updated GenerateContinuationTemplateName() for new naming (page_1.html -> page_1.2.html)
- Updated ExtractBaseTemplateName() to handle new format
- Updated all test files and source files with new template names

## Phase 3: Simplified RenderPageWithContinuations
- Removed hardcoded switch statements based on template names
- Replaced with generic dataMap and unified pagination call
- Extracted populatePageDataFromDistribution() to handle data mapping
- Template type detection now driven by metadata, not hardcoded names

## Benefits
-  Extensibility: Add new templates without code changes
-  Maintainability: One pagination algorithm instead of three
-  Clarity: Template names reflect page numbers, not content types
-  Flexibility: Templates can mix any data types
-  All 40+ tests passing

## Technical Details
- Added SkillsColumn3 and SkillsColumn4 fields to PageData for continuation pages
- Template metadata loaded from HTML comments drives pagination behavior
- Backward compatibility maintained for old template references in comments
2025-12-21 22:07:46 +01:00

184 lines
4.4 KiB
Go

package pdfrender
import (
"bamort/models"
"testing"
)
// TestRenderWithContinuations_SkillsOverflow tests that continuation pages are rendered
// when skills exceed template capacity
func TestRenderWithContinuations_SkillsOverflow(t *testing.T) {
// Arrange - Create character with many skills to force continuation
char := &models.Char{
BamortBase: models.BamortBase{
Name: "Test Character With Many Skills",
},
Typ: "Krieger",
Grad: 5,
Alter: 30,
Eigenschaften: []models.Eigenschaft{
{Name: "St", Value: 90},
{Name: "Gs", Value: 80},
{Name: "Gw", Value: 70},
{Name: "Ko", Value: 85},
{Name: "In", Value: 60},
{Name: "Zt", Value: 55},
{Name: "Au", Value: 75},
{Name: "pA", Value: 65},
{Name: "Wk", Value: 50},
},
Lp: models.Lp{Value: 20, Max: 25},
Ap: models.Ap{Value: 15, Max: 20},
B: models.B{Value: 15},
}
// Add 50 skills to force multiple pages
char.Fertigkeiten = make([]models.SkFertigkeit, 50)
for i := 0; i < 50; i++ {
char.Fertigkeiten[i] = models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: "Skill " + string(rune('A'+i%26))},
},
Fertigkeitswert: 10 + i%15,
Pp: i % 8,
}
}
// Map to view model
viewModel, err := MapCharacterToViewModel(char)
if err != nil {
t.Fatalf("Failed to map character: %v", err)
}
// Load templates
loader := NewTemplateLoader("../templates/Default_A4_Quer")
if err = loader.LoadTemplates(); err != nil {
t.Fatalf("Failed to load templates: %v", err)
}
renderer := NewPDFRenderer()
// Act - Render page1 with continuations
pdfs, err := RenderPageWithContinuations(
viewModel,
"page_1.html",
1,
"20.12.2025",
loader,
renderer,
)
// Assert
if err != nil {
t.Fatalf("Failed to render with continuations: %v", err)
}
// Get template capacity to calculate expected pages
templateSet := DefaultA4QuerTemplateSet()
var skillsCapacity int
for _, tmpl := range templateSet.Templates {
if tmpl.Metadata.Name == "page_1.html" {
for _, block := range tmpl.Metadata.Blocks {
if block.ListType == "skills" {
skillsCapacity += block.MaxItems
}
}
break
}
}
expectedPages := (50 + skillsCapacity - 1) / skillsCapacity
if len(pdfs) != expectedPages {
t.Errorf("Expected %d PDFs (pages) for 50 skills with capacity %d, got %d",
expectedPages, skillsCapacity, len(pdfs))
}
// Verify each PDF is valid
for i, pdf := range pdfs {
if len(pdf) == 0 {
t.Errorf("PDF %d is empty", i+1)
}
if string(pdf[0:4]) != "%PDF" {
t.Errorf("PDF %d does not start with PDF marker", i+1)
}
}
t.Logf("✓ Successfully generated %d continuation pages for 50 skills (capacity: %d)",
len(pdfs), skillsCapacity)
}
// TestRenderWithContinuations_NoOverflow tests that single page is rendered
// when items fit within capacity
func TestRenderWithContinuations_NoOverflow(t *testing.T) {
// Arrange - Create character with few skills
char := &models.Char{
BamortBase: models.BamortBase{
Name: "Test Character",
},
Typ: "Krieger",
Grad: 5,
Alter: 30,
Eigenschaften: []models.Eigenschaft{
{Name: "St", Value: 90},
{Name: "Gs", Value: 80},
{Name: "Gw", Value: 70},
{Name: "Ko", Value: 85},
{Name: "In", Value: 60},
{Name: "Zt", Value: 55},
{Name: "Au", Value: 75},
{Name: "pA", Value: 65},
{Name: "Wk", Value: 50},
},
Lp: models.Lp{Value: 20, Max: 25},
Ap: models.Ap{Value: 15, Max: 20},
B: models.B{Value: 15},
}
// Add only 3 skills - should fit on one page
char.Fertigkeiten = make([]models.SkFertigkeit, 3)
for i := 0; i < 3; i++ {
char.Fertigkeiten[i] = models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: "Skill " + string(rune('A'+i))},
},
Fertigkeitswert: 10,
Pp: 5,
}
}
// Map to view model
viewModel, err := MapCharacterToViewModel(char)
if err != nil {
t.Fatalf("Failed to map character: %v", err)
}
// Load templates
loader := NewTemplateLoader("../templates/Default_A4_Quer")
if err = loader.LoadTemplates(); err != nil {
t.Fatalf("Failed to load templates: %v", err)
}
renderer := NewPDFRenderer()
// Act
pdfs, err := RenderPageWithContinuations(
viewModel,
"page_1.html",
1,
"20.12.2025",
loader,
renderer,
)
// Assert
if err != nil {
t.Fatalf("Failed to render: %v", err)
}
if len(pdfs) != 1 {
t.Errorf("Expected 1 PDF for 3 skills, got %d", len(pdfs))
}
t.Logf("✓ Correctly generated single page for 3 skills")
}