package topdata import ( "encoding/json" "os" "path/filepath" "strings" "testing" "time" ) func TestBuildNativeGeneratesAndSkipsWikiPages(t *testing.T) { root := testProjectRoot(t) mkdirAll(t, filepath.Join(root, "topdata", "data", "skills")) writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n") writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{ "output": "skills.2da", "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"], "rows": [ { "id": 0, "key": "skills:athletics", "Label": "Athletics", "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}}, "Description": {"tlk": {"key": "skills:athletics.description", "text": "Ability: Strength.\n\nUses:\n- Climb\n- Swim"}}, "HideFromLevelUp": "0", "Untrained": "1", "KeyAbility": "STR", "ArmorCheckPenalty": "1", "Constant": "SKILL_ATHLETICS" } ] }`+"\n") writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n") proj := testProject(root) proj.Config.TopData.ReferenceBuilder = "" result, err := BuildNative(proj, nil) if err != nil { t.Fatalf("BuildNative failed: %v", err) } generatedHTMLCount := countGeneratedWikiHTMLFiles(t, filepath.Join(root, ".cache", "wiki", "pages")) if result.WikiPages != generatedHTMLCount { t.Fatalf("expected wiki page count to match generated HTML files, got result=%d files=%d", result.WikiPages, generatedHTMLCount) } pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "Athletics.html") got, err := os.ReadFile(pagePath) if err != nil { t.Fatalf("read generated page: %v", err) } text := string(got) if !strings.Contains(text, "
Hit Die: {{HitDie}}
Skill Points: {{SkillPointBase}}
{{format:StatusCallout}} {{format:Description}} {{UserBottomSection}}`, page) if err != nil { t.Fatalf("render template: %v", err) } if !strings.Contains(got, "Hit Die: 6
", "Skill Points: 4
", "This is a new class!", "Acolyte description.
", ``, } { if !strings.Contains(got, expected) { t.Fatalf("expected %q in rendered template:\n%s", expected, got) } } } func TestWikiTemplateMissingFieldFailsUnlessDefaultProvided(t *testing.T) { ctx := &wikiContext{} page := wikiTemplatePage{PageID: "classes:acolyte", Category: "classes", Title: "Acolyte", Row: map[string]any{}} if _, err := ctx.renderWikiTemplateString("classes.html", `{{HitDie}}
`, page); err == nil || !strings.Contains(err.Error(), `missing field "HitDie"`) { t.Fatalf("expected missing field error, got %v", err) } got, err := ctx.renderWikiTemplateString("classes.html", `{{field:HitDie|default=unknown}}
`, page) if err != nil { t.Fatalf("expected defaulted field to render, got %v", err) } if got != "unknown
" { t.Fatalf("expected default value, got %q", got) } } func TestWikiTemplateEachBlockRendersExplicitClassProgressionTable(t *testing.T) { ctx := &wikiContext{ wikiDataProviders: map[string]wikiDataProviderDefinition{ "ClassProgression": {Source: "class_progression", Levels: "1-2"}, }, } page := wikiTemplatePage{ PageID: "classes:fighter", Category: "classes", Key: "classes:fighter", Title: "Fighter", Row: map[string]any{ "HitDie": "10", "AttackBonusTable": "cls_atk_1", }, } template := `| Lvl | BAB | HP |
|---|---|---|
| {{Level|ordinal}} | {{BAB}} | {{HPMin}}-{{HPMax}} |
| 1st | +1 | 1-10 |
| 2nd | +2 | 2-20 |
Ability Adjustments | +2 Wisdom, +2 Charisma | `,
`Favored Class | [[Classes/Paladin|Paladin]] | `,
`{{DESCRIPTION|text|paragraph:first}} `,
want: `Type of Feat: General `,
reject: `Prerequisite`,
},
{
name: "specifics after marker as html",
template: `You gain a +2 dodge bonus. {{DESCRIPTION|text|paragraphs:"first,last"|html}} `,
want: `Type of Feat: General Use: Automatic. {{title}}{{table:ClassProgression}}`, page) if err != nil { t.Fatalf("render table template: %v", err) } for _, expected := range []string{ `
|