work template engine, implement item purchase and equip
This commit is contained in:
parent
90923cbfe7
commit
53f778c323
@ -197,7 +197,7 @@ button.btn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.btn-primary {
|
&.btn-primary {
|
||||||
color: rgba(0, 0, 0, 0.5);
|
color: rgba(0, 0, 0, 0.75);
|
||||||
background-color: #F2994A;
|
background-color: #F2994A;
|
||||||
background-image: url("/assets/images/overlay.png"), linear-gradient(to bottom, #F2C94C, #F2994A);
|
background-image: url("/assets/images/overlay.png"), linear-gradient(to bottom, #F2C94C, #F2994A);
|
||||||
border-color: #F2994A;
|
border-color: #F2994A;
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 94 B |
Binary file not shown.
Before Width: | Height: | Size: 94 B |
Binary file not shown.
Before Width: | Height: | Size: 94 B |
56
internal/actions/user_item.go
Normal file
56
internal/actions/user_item.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dk/internal/items"
|
||||||
|
"dk/internal/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserEquipItem equips a given item onto a user. This overwrites any
|
||||||
|
// previously equipped item in the slot. Does not save.
|
||||||
|
func UserEquipItem(user *users.User, item *items.Item) {
|
||||||
|
slotInUse := false
|
||||||
|
if item.Type == items.TypeWeapon && user.WeaponID != 0 {
|
||||||
|
slotInUse = true
|
||||||
|
}
|
||||||
|
if item.Type == items.TypeArmor && user.ArmorID != 0 {
|
||||||
|
slotInUse = true
|
||||||
|
}
|
||||||
|
if item.Type == items.TypeShield && user.ShieldID != 0 {
|
||||||
|
slotInUse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldItem *items.Item
|
||||||
|
if slotInUse && item.Type == items.TypeWeapon {
|
||||||
|
oldItem, _ = items.Find(user.WeaponID)
|
||||||
|
} else if slotInUse && item.Type == items.TypeArmor {
|
||||||
|
oldItem, _ = items.Find(user.ArmorID)
|
||||||
|
} else if slotInUse && item.Type == items.TypeShield {
|
||||||
|
oldItem, _ = items.Find(user.ShieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldItem != nil {
|
||||||
|
switch oldItem.Type {
|
||||||
|
case items.TypeWeapon:
|
||||||
|
user.Set("Attack", user.Attack-oldItem.Att)
|
||||||
|
case items.TypeArmor:
|
||||||
|
user.Set("Defense", user.Defense-oldItem.Att)
|
||||||
|
case items.TypeShield:
|
||||||
|
user.Set("Defense", user.Defense-oldItem.Att)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch item.Type {
|
||||||
|
case items.TypeWeapon:
|
||||||
|
user.Set("Attack", user.Attack+item.Att)
|
||||||
|
user.Set("WeaponID", item.ID)
|
||||||
|
user.Set("WeaponName", item.Name)
|
||||||
|
case items.TypeArmor:
|
||||||
|
user.Set("Defense", user.Defense+item.Att)
|
||||||
|
user.Set("ArmorID", item.ID)
|
||||||
|
user.Set("ArmorName", item.Name)
|
||||||
|
case items.TypeShield:
|
||||||
|
user.Set("Defense", user.Defense+item.Att)
|
||||||
|
user.Set("ShieldID", item.ID)
|
||||||
|
user.Set("ShieldName", item.Name)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"dk/internal/actions"
|
||||||
"dk/internal/auth"
|
"dk/internal/auth"
|
||||||
"dk/internal/helpers"
|
"dk/internal/helpers"
|
||||||
"dk/internal/items"
|
"dk/internal/items"
|
||||||
@ -9,6 +10,8 @@ import (
|
|||||||
"dk/internal/template/components"
|
"dk/internal/template/components"
|
||||||
"dk/internal/towns"
|
"dk/internal/towns"
|
||||||
"dk/internal/users"
|
"dk/internal/users"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterTownRoutes(r *router.Router) {
|
func RegisterTownRoutes(r *router.Router) {
|
||||||
@ -20,6 +23,7 @@ func RegisterTownRoutes(r *router.Router) {
|
|||||||
group.Get("/inn", showInn)
|
group.Get("/inn", showInn)
|
||||||
group.Get("/shop", showShop)
|
group.Get("/shop", showShop)
|
||||||
group.WithMiddleware(middleware.CSRF(auth.Manager)).Post("/inn", rest)
|
group.WithMiddleware(middleware.CSRF(auth.Manager)).Post("/inn", rest)
|
||||||
|
group.Get("/shop/buy/:id", buyItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showTown(ctx router.Ctx, _ []string) {
|
func showTown(ctx router.Ctx, _ []string) {
|
||||||
@ -93,3 +97,39 @@ func showShop(ctx router.Ctx, _ []string) {
|
|||||||
"error_message": errorHTML,
|
"error_message": errorHTML,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buyItem(ctx router.Ctx, params []string) {
|
||||||
|
id, err := strconv.Atoi(params[0])
|
||||||
|
if err != nil {
|
||||||
|
auth.SetFlashMessage(ctx, "error", "Error purchasing item; "+err.Error())
|
||||||
|
ctx.Redirect("/town/shop", 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
town := ctx.UserValue("town").(*towns.Town)
|
||||||
|
if !slices.Contains(town.GetShopItems(), id) {
|
||||||
|
auth.SetFlashMessage(ctx, "error", "The item doesn't exist in this shop.")
|
||||||
|
ctx.Redirect("/town/shop", 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := items.Find(id)
|
||||||
|
if err != nil {
|
||||||
|
auth.SetFlashMessage(ctx, "error", "Error purchasing item; "+err.Error())
|
||||||
|
ctx.Redirect("/town/shop", 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := ctx.UserValue("user").(*users.User)
|
||||||
|
if user.Gold < item.Value {
|
||||||
|
auth.SetFlashMessage(ctx, "error", "You don't have enough gold to buy "+item.Name)
|
||||||
|
ctx.Redirect("/town/shop", 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Set("Gold", user.Gold-item.Value)
|
||||||
|
actions.UserEquipItem(user, item)
|
||||||
|
user.Save()
|
||||||
|
|
||||||
|
ctx.Redirect("/town/shop", 302)
|
||||||
|
}
|
||||||
|
100
internal/template/cache.go
Normal file
100
internal/template/cache.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package template
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Cache *TemplateCache
|
||||||
|
|
||||||
|
type TemplateCache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
templates map[string]*Template
|
||||||
|
basePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCache(basePath string) *TemplateCache {
|
||||||
|
if basePath == "" {
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
basePath = "."
|
||||||
|
} else {
|
||||||
|
basePath = filepath.Dir(exe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TemplateCache{
|
||||||
|
templates: make(map[string]*Template),
|
||||||
|
basePath: basePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitializeCache(basePath string) {
|
||||||
|
Cache = NewCache(basePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TemplateCache) Load(name string) (*Template, error) {
|
||||||
|
c.mu.RLock()
|
||||||
|
tmpl, exists := c.templates[name]
|
||||||
|
c.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if err := c.checkAndReload(tmpl); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.loadFromFile(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TemplateCache) loadFromFile(name string) (*Template, error) {
|
||||||
|
filePath := filepath.Join(c.basePath, "templates", name)
|
||||||
|
|
||||||
|
info, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("template file not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := &Template{
|
||||||
|
name: name,
|
||||||
|
content: string(content),
|
||||||
|
modTime: info.ModTime(),
|
||||||
|
filePath: filePath,
|
||||||
|
cache: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
c.templates[name] = tmpl
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
return tmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TemplateCache) checkAndReload(tmpl *Template) error {
|
||||||
|
info, err := os.Stat(tmpl.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.ModTime().After(tmpl.modTime) {
|
||||||
|
content, err := os.ReadFile(tmpl.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
tmpl.content = string(content)
|
||||||
|
tmpl.modTime = info.ModTime()
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,314 +0,0 @@
|
|||||||
package template
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewCache(t *testing.T) {
|
|
||||||
cache := NewCache("")
|
|
||||||
if cache == nil {
|
|
||||||
t.Fatal("NewCache returned nil")
|
|
||||||
}
|
|
||||||
if cache.templates == nil {
|
|
||||||
t.Fatal("templates map not initialized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPositionalReplacement(t *testing.T) {
|
|
||||||
tmpl := &Template{
|
|
||||||
name: "test",
|
|
||||||
content: "Hello {0}, you are {1} years old!",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderPositional("Alice", 25)
|
|
||||||
expected := "Hello Alice, you are 25 years old!"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNamedReplacement(t *testing.T) {
|
|
||||||
tmpl := &Template{
|
|
||||||
name: "test",
|
|
||||||
content: "Hello {name}, you are {age} years old!",
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"name": "Bob",
|
|
||||||
"age": 30,
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(data)
|
|
||||||
expected := "Hello Bob, you are 30 years old!"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDotNotationReplacement(t *testing.T) {
|
|
||||||
tmpl := &Template{
|
|
||||||
name: "test",
|
|
||||||
content: "User: {user.name}, Email: {user.contact.email}",
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"user": map[string]any{
|
|
||||||
"name": "Charlie",
|
|
||||||
"contact": map[string]any{
|
|
||||||
"email": "charlie@example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(data)
|
|
||||||
expected := "User: Charlie, Email: charlie@example.com"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTemplateLoadingAndCaching(t *testing.T) {
|
|
||||||
tmpDir, err := os.MkdirTemp("", "template_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
templatesDir := filepath.Join(tmpDir, "templates")
|
|
||||||
err = os.MkdirAll(templatesDir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
templateFile := filepath.Join(templatesDir, "test.html")
|
|
||||||
content := "Hello {name}!"
|
|
||||||
err = os.WriteFile(templateFile, []byte(content), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := NewCache(tmpDir)
|
|
||||||
|
|
||||||
tmpl, err := cache.Load("test.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpl.content != content {
|
|
||||||
t.Errorf("Expected content %q, got %q", content, tmpl.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl2, err := cache.Load("test.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpl != tmpl2 {
|
|
||||||
t.Error("Template should be cached and return same instance")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTemplateReloading(t *testing.T) {
|
|
||||||
tmpDir, err := os.MkdirTemp("", "template_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
templatesDir := filepath.Join(tmpDir, "templates")
|
|
||||||
err = os.MkdirAll(templatesDir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
templateFile := filepath.Join(templatesDir, "test.html")
|
|
||||||
content1 := "Hello {name}!"
|
|
||||||
err = os.WriteFile(templateFile, []byte(content1), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := NewCache(tmpDir)
|
|
||||||
|
|
||||||
tmpl, err := cache.Load("test.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpl.content != content1 {
|
|
||||||
t.Errorf("Expected content %q, got %q", content1, tmpl.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
|
|
||||||
content2 := "Hi {name}, welcome!"
|
|
||||||
err = os.WriteFile(templateFile, []byte(content2), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl2, err := cache.Load("test.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpl2.content != content2 {
|
|
||||||
t.Errorf("Expected reloaded content %q, got %q", content2, tmpl2.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetNestedValue(t *testing.T) {
|
|
||||||
tmpl := &Template{}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"level1": map[string]any{
|
|
||||||
"level2": map[string]any{
|
|
||||||
"value": "found",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.getNestedValue(data, "level1.level2.value")
|
|
||||||
if result != "found" {
|
|
||||||
t.Errorf("Expected 'found', got %v", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
result = tmpl.getNestedValue(data, "level1.nonexistent")
|
|
||||||
if result != nil {
|
|
||||||
t.Errorf("Expected nil for nonexistent path, got %v", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMixedReplacementTypes(t *testing.T) {
|
|
||||||
tmpl := &Template{
|
|
||||||
name: "test",
|
|
||||||
content: "Hello {name}, you have {count} {items.type}s!",
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"name": "Dave",
|
|
||||||
"count": 5,
|
|
||||||
"items": map[string]any{
|
|
||||||
"type": "apple",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(data)
|
|
||||||
expected := "Hello Dave, you have 5 apples!"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIncludeSupport(t *testing.T) {
|
|
||||||
tmpDir, err := os.MkdirTemp("", "template_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
templatesDir := filepath.Join(tmpDir, "templates")
|
|
||||||
err = os.MkdirAll(templatesDir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create main template with include
|
|
||||||
mainTemplate := `<html><head>{include "header.html"}</head><body>Hello {name}!</body></html>`
|
|
||||||
err = os.WriteFile(filepath.Join(templatesDir, "main.html"), []byte(mainTemplate), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create included template
|
|
||||||
headerTemplate := `<title>{title}</title><meta charset="utf-8">`
|
|
||||||
err = os.WriteFile(filepath.Join(templatesDir, "header.html"), []byte(headerTemplate), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := NewCache(tmpDir)
|
|
||||||
tmpl, err := cache.Load("main.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"name": "Alice",
|
|
||||||
"title": "Welcome",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(data)
|
|
||||||
expected := "<html><head><title>Welcome</title><meta charset=\"utf-8\"></head><body>Hello Alice!</body></html>"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlocksAndYield(t *testing.T) {
|
|
||||||
tmpl := &Template{
|
|
||||||
name: "test",
|
|
||||||
content: `{block "content"}Default content{/block}<main>{yield content}</main>`,
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(map[string]any{})
|
|
||||||
expected := "<main>Default content</main>"
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTemplateInheritance(t *testing.T) {
|
|
||||||
tmpDir, err := os.MkdirTemp("", "template_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
templatesDir := filepath.Join(tmpDir, "templates")
|
|
||||||
err = os.MkdirAll(templatesDir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layout template
|
|
||||||
layoutTemplate := `<!DOCTYPE html><html><head><title>{title}</title></head><body>{yield content}</body></html>`
|
|
||||||
err = os.WriteFile(filepath.Join(templatesDir, "layout.html"), []byte(layoutTemplate), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page template that extends layout
|
|
||||||
pageTemplate := `{include "layout.html"}{block "content"}<h1>Welcome {name}!</h1><p>This is the content block.</p>{/block}`
|
|
||||||
err = os.WriteFile(filepath.Join(templatesDir, "page.html"), []byte(pageTemplate), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := NewCache(tmpDir)
|
|
||||||
tmpl, err := cache.Load("page.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]any{
|
|
||||||
"title": "Test Page",
|
|
||||||
"name": "Bob",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tmpl.RenderNamed(data)
|
|
||||||
expected := `<!DOCTYPE html><html><head><title>Test Page</title></head><body><h1>Welcome Bob!</h1><p>This is the content block.</p></body></html>`
|
|
||||||
|
|
||||||
if result != expected {
|
|
||||||
t.Errorf("Expected %q, got %q", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user