Files
ZacharyZhang-NY 03680980da Add data components 151-175; clear MyGo row highlights
Lists, tables, trees, grids, inventories and records in data/.
Outline and GridView build on ui.List: ui.Outline strokes its arrows
and ui.GridView spaces and tints items. The pixel theme keeps MyGo's
row highlights clear so scrolled rows stay on whole dots; off lists
take a veil against the wheel. Adds Canvas.Silhouette, SwitchOptions
Label and exports Rarity.Slot.
2026-10-11 22:29:07 -04:00

144 lines
4.9 KiB
Go

package data_test
import (
"fmt"
"math"
"testing"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
func TestDescriptionListSetsTermsBesideText(t *testing.T) {
tt := view(400, 200, newClock(), false, func(c *ui.Context) {
data.DescriptionList(c, [][2]string{{"Class", "Knight"}, {"Home", "Castle Town"}}, data.DescriptionListOptions{})
})
term, desc := find(t, tt, "Home"), find(t, tt, "Castle Town")
if desc.X != term.X+176 || desc.Y != term.Y {
t.Fatalf("description at %v beside term at %v", desc, term)
}
keeps(t, tt.Image())
}
func TestLeaderboardMedalsAndPlayer(t *testing.T) {
scores := []data.Score{{Name: "ACE", Score: 9000}, {Name: "BOB", Score: 8000}, {Name: "CAT", Score: 7000}, {Name: "YOU", Score: 6000}, {Name: "DOG", Score: 5000}}
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
data.Leaderboard(c, scores, data.LeaderboardOptions{Player: 3})
})
for _, l := range []string{"GOLD", "SILVER", "BRONZE", "0006000", "4 YOU 6000"} {
if !shows(tt, l) {
t.Fatalf("no %q in %q", l, tt.Texts())
}
}
r, img := find(t, tt, "4 YOU 6000"), tt.Image()
if s := slot(t, img, int(r.X)+r2i(r.W)-4, int(r.Y)+4); s != pixel.Accent {
t.Fatalf("your row is %v", s)
}
keeps(t, img)
}
func r2i(f float32) int { return int(f) }
func TestTimelineMarksEachEvent(t *testing.T) {
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
data.Timeline(c, []data.Event{{Date: "1986", Text: "Zelda"}, {Date: "1991", Text: "A Link to the Past"}}, data.TimelineOptions{})
})
for _, l := range []string{"1986", "Zelda", "1991", "A Link to the Past"} {
if !shows(tt, l) {
t.Fatalf("no %q", l)
}
}
r, img := find(t, tt, "1986"), tt.Image()
// The marker's middle sits 13 dots left of the date.
if s := slot(t, img, int(r.X)-26+8, int(r.Y)+10); s != pixel.Accent {
t.Fatalf("marker is %v", s)
}
keeps(t, img)
}
func TestStatCompareInksDeltas(t *testing.T) {
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
data.StatCompare(c, []data.Change{{Label: "ATK", Old: 12, New: 15}, {Label: "DEF", Old: 9, New: 7}, {Label: "SPD", Old: 5, New: 5}}, data.StatCompareOptions{})
})
for _, l := range []string{"ATK 12 → 15 ▲3", "DEF 9 → 7 ▼2", "SPD 5 → 5 "} {
if !shows(tt, l) {
t.Fatalf("no %q in %q", l, tt.Texts())
}
}
img := tt.Image()
ink := func(label string) map[pixel.Slot]bool {
r, out := find(t, tt, label), map[pixel.Slot]bool{}
for y := int(r.Y); y < int(r.Y+r.H); y += 2 {
for x := int(r.X); x < int(r.X+r.W); x += 2 {
out[slot(t, img, x, y)] = true
}
}
return out
}
if up, dn := ink("ATK 12 → 15 ▲3"), ink("DEF 9 → 7 ▼2"); !up[pixel.Good] || up[pixel.Bad] || !dn[pixel.Bad] || dn[pixel.Good] {
t.Fatalf("delta inks: up %v, down %v", up, dn)
}
keeps(t, img)
}
func TestBestiaryHidesUnseen(t *testing.T) {
m := data.Monster{Sprite: icon.Skull, Name: "Skeleton", Stats: [][2]string{{"HP", "30"}}, Notes: "Rattles."}
for _, seen := range []bool{true, false} {
m.Seen = seen
tt := view(300, 240, newClock(), false, func(c *ui.Context) { data.Bestiary(c, m, data.BestiaryOptions{}) })
if shows(tt, "Skeleton") != seen || shows(tt, "HP 30") != seen || shows(tt, "Rattles.") != seen || shows(tt, "???") == seen {
t.Fatalf("seen %v: texts %q", seen, tt.Texts())
}
keeps(t, tt.Image())
}
}
func TestFeedListsEntries(t *testing.T) {
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
data.Feed(c, []data.Activity{{Icon: icon.Star, Text: "Aria found a star", Time: "2m"}, {Icon: icon.Coin, Text: "Brom sold a coin", Time: "5m"}}, data.FeedOptions{})
})
a, b := find(t, tt, "Aria found a star"), find(t, tt, "Brom sold a coin")
if !shows(tt, "2m") || !shows(tt, "5m") || b.Y <= a.Y || b.X != a.X {
t.Fatalf("entries at %v and %v", a, b)
}
keeps(t, tt.Image())
}
func TestCardGridFitsColumns(t *testing.T) {
// Widths are pixels, two per dot; cards need 96 dots.
for _, tc := range []struct{ w, cols int }{{800, 4}, {500, 2}, {180, 1}} {
tt := view(tc.w+32, 300, newClock(), false, func(c *ui.Context) {
ui.Column(c).Padding(16).Children(func() {
data.CardGrid(c, 6, data.CardGridOptions{}, func(i int) {
pixel.Text(c, fmt.Sprintf("card %d", i), pixel.TextOptions{})
})
})
})
tt.Frame()
first, row := find(t, tt, "card 0"), 0
for i := range 6 {
if find(t, tt, fmt.Sprintf("card %d", i)).Y == first.Y {
row++
}
}
if row != tc.cols {
t.Fatalf("%d px wide: %d cards in the first row, want %d", tc.w, row, tc.cols)
}
keeps(t, tt.Image())
}
}
func TestItemSlotRefusesNaNCooldown(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatal("a NaN cooldown did not panic")
}
}()
view(100, 100, newClock(), false, func(c *ui.Context) {
data.ItemSlot(c, data.Item{Name: "Ether"}, data.ItemSlotOptions{Cooldown: math.NaN()})
})
}