Class wiki page refactors

This commit is contained in:
2026-05-27 09:50:22 +02:00
parent fd83d96c3a
commit bd9d2c6da0
2 changed files with 289 additions and 0 deletions
+114
View File
@@ -778,6 +778,120 @@ providers:
}
}
func TestWikiTemplateClassFactsCanUseDerivedClassHelpers(t *testing.T) {
root := t.TempDir()
dataPath := filepath.Join(root, "data.yaml")
writeFile(t, dataPath, `
providers:
ClassFacts:
source: facts
facts:
rows:
- label: Hit Die
value: "{{\"d\" + HitDie + \" (\" + HitDie + \" per level)\"}}"
- label: Proficiencies
value: "{{join(class_proficiencies(), \", \")}}"
- label: Skill Points at 1st Level
value: "{{\"(\" + SkillPointBase + \" + Int modifier) x4\"}}"
- label: Skill Points at Each Additional Level
value: "{{SkillPointBase + \" + Int modifier\"}}"
- label: Saves
value: "{{class_saves()}}"
- label: Base Attack Bonus
value: "{{class_bab()}}"
- label: Spellcasting
value: "{{\"To cast a spell, a \" + text(Lower) + \" must have a \" + ability_name(SpellcastingAbil) + \" score of 10 + the spell's level. For example, to cast a 2nd-level spell, a \" + text(Lower) + \" must have a \" + ability_name(SpellcastingAbil) + \" of 12.\"}}"
when: "{{SpellCaster == 1}}"
ClassBonusFeats:
source: facts
facts:
rows:
- label: Bonus Feats
value: "{{join(class_selectable_feats(), \", \")}}"
`+"\n")
providers, err := loadWikiDataProviders(dataPath)
if err != nil {
t.Fatalf("load data providers: %v", err)
}
ctx := testWikiTableContext(t, "")
ctx.wikiDataPath = dataPath
ctx.wikiDataProviders = providers
ctx.featRows["feat:armor_proficiency_light"] = map[string]any{"FEAT": map[string]any{"tlk": map[string]any{"text": "Light Armor Proficiency"}}}
ctx.featRows["feat:shield_proficiency"] = map[string]any{"FEAT": map[string]any{"tlk": map[string]any{"text": "Shield Proficiency"}}}
ctx.featRows["feat:extra_music"] = map[string]any{"FEAT": map[string]any{"tlk": map[string]any{"text": "Extra Music"}}}
ctx.featRows["feat:lingering_song"] = map[string]any{"FEAT": map[string]any{"tlk": map[string]any{"text": "Lingering Song"}}}
ctx.classFeatTables["classes/feats:bard"] = wikiTable{OutputStem: "cls_feat_bard", Rows: []map[string]any{
{"FeatIndex": map[string]any{"id": "feat:literate"}, "GrantedOnLevel": "1", "List": "3"},
{"FeatIndex": map[string]any{"id": "feat:armor_proficiency_light"}, "GrantedOnLevel": "1", "List": "3"},
{"FeatIndex": map[string]any{"id": "feat:shield_proficiency"}, "GrantedOnLevel": "1", "List": "3"},
{"FeatIndex": map[string]any{"id": "feat:extra_music"}, "GrantedOnLevel": "-1", "List": "1"},
{"FeatIndex": map[string]any{"id": "feat:lingering_song"}, "GrantedOnLevel": "-1", "List": "2"},
}}
ctx.classSaveTables["classes/saves:reflex_will"] = wikiTable{OutputStem: "cls_savthr_rw", Rows: []map[string]any{
{"Level": "1", "FortSave": "0", "RefSave": "2", "WillSave": "2"},
}}
page := wikiTemplatePage{
PageID: "classes:bard",
Category: "classes",
Key: "classes:bard",
Title: "Bard",
Row: map[string]any{
"HitDie": "6",
"SkillPointBase": "8",
"Lower": map[string]any{"tlk": map[string]any{"text": "bard"}},
"SpellCaster": "1",
"SpellcastingAbil": "CHA",
"AttackBonusTable": "cls_atk_2",
"FeatsTable": map[string]any{"table": "classes/feats:bard"},
"SavingThrowTable": map[string]any{"table": "classes/saves:reflex_will"},
},
}
got, err := ctx.renderWikiTemplateString("classes.html", `<table>
{{#each ClassFacts}}<tr><th>{{Label}}</th><td>{{Value|wiki}}</td></tr>{{/each}}
</table>
{{#each ClassBonusFeats}}<p>{{Value|wiki}}</p>{{/each}}`, page)
if err != nil {
t.Fatalf("render template: %v", err)
}
for _, expected := range []string{
`<th>Hit Die</th><td>d6 (6 per level)</td>`,
`<th>Proficiencies</th><td>[[Feats/Light_Armor_Proficiency|Light Armor Proficiency]], [[Feats/Shield_Proficiency|Shield Proficiency]]</td>`,
`<th>Skill Points at 1st Level</th><td>(8 + Int modifier) x4</td>`,
`<th>Skill Points at Each Additional Level</th><td>8 + Int modifier</td>`,
`<th>Saves</th><td>High Reflex and Will</td>`,
`<th>Base Attack Bonus</th><td>+3/4</td>`,
`<th>Spellcasting</th><td>To cast a spell, a bard must have a Charisma score of 10 + the spell's level. For example, to cast a 2nd-level spell, a bard must have a Charisma of 12.</td>`,
`<p>[[Feats/Extra_Music|Extra Music]], [[Feats/Lingering_Song|Lingering Song]]</p>`,
} {
if !strings.Contains(got, expected) {
t.Fatalf("expected %q in rendered class facts:\n%s", expected, got)
}
}
}
func TestWikiTemplateExpressionCanRenderIndefiniteArticles(t *testing.T) {
ctx := &wikiContext{}
page := wikiTemplatePage{
PageID: "classes:adept",
Category: "classes",
Key: "classes:adept",
Title: "Adept",
Row: map[string]any{
"Lower": map[string]any{"tlk": map[string]any{"text": "adept"}},
"SpellcastingAbil": "INT",
},
}
got, err := ctx.renderWikiTemplateString("classes.html", `{{indefinite_article(text(Lower))}} {{text(Lower)}} / {{indefinite_article(ability_name(SpellcastingAbil))}} {{ability_name(SpellcastingAbil)}}`, page)
if err != nil {
t.Fatalf("render template: %v", err)
}
if got != "an adept / an Intelligence" {
t.Fatalf("expected vowel-sound articles, got %q", got)
}
}
func TestWikiTemplateClassProgressionProviderUsesDefaultNWNAttackSequences(t *testing.T) {
ctx := &wikiContext{
wikiDataPath: "test-data.yaml",