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.
234 lines
7.4 KiB
Go
234 lines
7.4 KiB
Go
package data_test
|
||
|
||
import (
|
||
"slices"
|
||
"testing"
|
||
|
||
"github.com/egoist/mygo/ui"
|
||
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/display"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||
)
|
||
|
||
func TestInventoryChoosesAndSwaps(t *testing.T) {
|
||
items := []data.Item{{Sprite: icon.Potion, Name: "Potion", Count: 3}, {Sprite: icon.Sword, Name: "Sword", Rarity: display.Epic}, {}, {Sprite: icon.Gem, Name: "Gem"}}
|
||
sel, swaps := 0, 0
|
||
tt := view(400, 300, newClock(), false, func(c *ui.Context) {
|
||
if data.Inventory(c, "Bag", items, &sel, data.InventoryOptions{Columns: 2}).Swapped {
|
||
swaps++
|
||
}
|
||
})
|
||
r := find(t, tt, "Bag")
|
||
tt.ClickAt(r.X+4, r.Y+4)
|
||
tt.Key(0, ui.KeyDown)
|
||
tt.Key(0, ui.KeyRight)
|
||
if sel != 3 {
|
||
t.Fatalf("Down, Right chose %d", sel)
|
||
}
|
||
// One slot is 22 dots, 44 pixels.
|
||
tt.Press(r.X+10, r.Y+10)
|
||
tt.Move(r.X+30, r.Y+30)
|
||
tt.Move(r.X+54, r.Y+10)
|
||
tt.Release(r.X+54, r.Y+10)
|
||
if items[1].Name != "Potion" || items[0].Name != "Sword" || swaps != 1 {
|
||
t.Fatalf("drag swapped to %v, %d swaps", items, swaps)
|
||
}
|
||
tt.ClickAt(r.X+10, r.Y+10)
|
||
if sel != 0 || items[0].Name != "Sword" || swaps != 1 {
|
||
t.Fatalf("a click swapped or chose %d", sel)
|
||
}
|
||
keeps(t, tt.Image())
|
||
}
|
||
|
||
func TestInventoryOffNeitherChoosesNorSwaps(t *testing.T) {
|
||
for _, inert := range []bool{false, true} {
|
||
items := []data.Item{{Sprite: icon.Potion, Name: "Potion"}, {Sprite: icon.Gem, Name: "Gem"}}
|
||
sel := 0
|
||
tt := view(400, 300, newClock(), inert, func(c *ui.Context) {
|
||
data.Inventory(c, "Bag", items, &sel, data.InventoryOptions{Disabled: !inert})
|
||
})
|
||
r := find(t, tt, "Bag")
|
||
tt.Press(r.X+10, r.Y+10)
|
||
tt.Move(r.X+54, r.Y+10)
|
||
tt.Release(r.X+54, r.Y+10)
|
||
tt.Key(0, ui.KeyRight)
|
||
if sel != 0 || items[0].Name != "Potion" {
|
||
t.Fatalf("inert %v: sel %d, items %v", inert, sel, items)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestItemSlotShowsRarityCountAndCooldown(t *testing.T) {
|
||
tt := view(200, 100, newClock(), false, func(c *ui.Context) {
|
||
ui.Row(c).Children(func() {
|
||
data.ItemSlot(c, data.Item{Sprite: icon.Potion, Name: "Potion", Count: 12, Rarity: display.Legendary}, data.ItemSlotOptions{})
|
||
data.ItemSlot(c, data.Item{Sprite: icon.Potion, Name: "Ether"}, data.ItemSlotOptions{Cooldown: 1})
|
||
})
|
||
})
|
||
r, img := find(t, tt, "Potion ×12"), tt.Image()
|
||
if edge := slot(t, img, int(r.X)+2, int(r.Y)+20); edge != display.Legendary.Slot() {
|
||
t.Fatalf("legendary edge is %v", edge)
|
||
}
|
||
cool := find(t, tt, "Ether")
|
||
shade := 0
|
||
for x := int(cool.X) + 4; x < int(cool.X)+40; x += 2 {
|
||
if slot(t, img, x, int(cool.Y)+6) == pixel.Shade {
|
||
shade++
|
||
}
|
||
}
|
||
if shade == 0 {
|
||
t.Fatal("a full cooldown left no veil")
|
||
}
|
||
keeps(t, img)
|
||
}
|
||
|
||
func TestSortableMovesByAltArrowsAndDrag(t *testing.T) {
|
||
items := []string{"Aria", "Brom", "Cyra"}
|
||
changes := 0
|
||
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
|
||
if data.Sortable(c, "Order", &items, data.SortableOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
r := find(t, tt, "Order")
|
||
tt.ClickAt(r.X+20, r.Y+4)
|
||
tt.Key(ui.Alt, ui.KeyDown)
|
||
tt.Key(ui.Alt, ui.KeyDown)
|
||
if !slices.Equal(items, []string{"Brom", "Cyra", "Aria"}) {
|
||
t.Fatalf("Alt+Down twice gave %v", items)
|
||
}
|
||
tt.Key(0, ui.KeyUp)
|
||
tt.Key(ui.Alt, ui.KeyUp)
|
||
if !slices.Equal(items, []string{"Cyra", "Brom", "Aria"}) {
|
||
t.Fatalf("Up, Alt+Up gave %v", items)
|
||
}
|
||
// Rows are 16 dots, 32 pixels.
|
||
tt.Press(r.X+20, r.Y+72)
|
||
tt.Move(r.X+20, r.Y+40)
|
||
tt.Move(r.X+20, r.Y+8)
|
||
tt.Release(r.X+20, r.Y+8)
|
||
if !slices.Equal(items, []string{"Aria", "Cyra", "Brom"}) || changes != 5 {
|
||
t.Fatalf("drag to the top gave %v, %d changes", items, changes)
|
||
}
|
||
}
|
||
|
||
func TestTransferMovesChosenItems(t *testing.T) {
|
||
left, right := []string{"Aria", "Brom"}, []string{"Cyra"}
|
||
changes := 0
|
||
tt := view(400, 300, newClock(), false, func(c *ui.Context) {
|
||
if data.Transfer(c, "Party", &left, &right, data.TransferOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
click(t, tt, "Brom")
|
||
click(t, tt, "RIGHT")
|
||
if !slices.Equal(left, []string{"Aria"}) || !slices.Equal(right, []string{"Cyra", "Brom"}) {
|
||
t.Fatalf("→ moved to %v %v", left, right)
|
||
}
|
||
click(t, tt, "Cyra")
|
||
click(t, tt, "LEFT")
|
||
if !slices.Equal(left, []string{"Aria", "Cyra"}) || !slices.Equal(right, []string{"Brom"}) || changes != 2 {
|
||
t.Fatalf("← moved to %v %v, %d changes", left, right, changes)
|
||
}
|
||
}
|
||
|
||
func TestMatrixTogglesByClickAndKeys(t *testing.T) {
|
||
on := [][]bool{{false, false}, {false, true}}
|
||
changes := 0
|
||
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
|
||
if data.Matrix(c, "Rights", []string{"Read", "Write"}, []string{"Ann", "Bob"}, on, data.MatrixOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
r := find(t, tt, "Rights")
|
||
tt.ClickAt(r.X+4, r.Y+4)
|
||
tt.Key(0, ui.KeyDown)
|
||
tt.Key(0, ui.KeySpace)
|
||
tt.Key(0, ui.KeyRight)
|
||
tt.Key(0, ui.KeyEnter)
|
||
if !on[0][0] || !on[1][0] || on[1][1] || changes != 3 {
|
||
t.Fatalf("click, Down Space, Right Enter left %v, %d changes", on, changes)
|
||
}
|
||
keeps(t, tt.Image())
|
||
}
|
||
|
||
func TestInventoryDragEndsWithoutSwapWhenTurnedOff(t *testing.T) {
|
||
for _, inert := range []bool{false, true} {
|
||
items := []data.Item{{Sprite: icon.Potion, Name: "Potion"}, {Sprite: icon.Gem, Name: "Gem"}}
|
||
sel, off, swaps := 0, false, 0
|
||
tt := ui.NewTester(func(c *ui.Context) {
|
||
pixel.Use(c, pixel.Settings{Palette: pixel.SpringDay, Inert: off && inert})
|
||
if data.Inventory(c, "Bag", items, &sel, data.InventoryOptions{Disabled: off && !inert}).Swapped {
|
||
swaps++
|
||
}
|
||
}, 400, 300)
|
||
r := find(t, tt, "Bag")
|
||
tt.Press(r.X+10, r.Y+10)
|
||
tt.Move(r.X+54, r.Y+10)
|
||
off = true
|
||
tt.Frame()
|
||
off = false
|
||
tt.Release(r.X+54, r.Y+10)
|
||
if items[0].Name != "Potion" || swaps != 0 {
|
||
t.Fatalf("inert %v: turning off mid-drag swapped %v", inert, items)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestInventoryDragSurvivesShrinkingItems(t *testing.T) {
|
||
items := []data.Item{{Sprite: icon.Potion, Name: "Potion"}, {Sprite: icon.Gem, Name: "Gem"}, {Sprite: icon.Key, Name: "Key"}}
|
||
sel, swaps := 0, 0
|
||
tt := view(400, 300, newClock(), false, func(c *ui.Context) {
|
||
if data.Inventory(c, "Bag", items, &sel, data.InventoryOptions{}).Swapped {
|
||
swaps++
|
||
}
|
||
})
|
||
r := find(t, tt, "Bag")
|
||
tt.Press(r.X+98, r.Y+10)
|
||
tt.Move(r.X+10, r.Y+10)
|
||
items = items[:1]
|
||
tt.Frame()
|
||
tt.Release(r.X+10, r.Y+10)
|
||
if items[0].Name != "Potion" || swaps != 0 {
|
||
t.Fatalf("a drag from a lost slot swapped %v", items)
|
||
}
|
||
}
|
||
|
||
func TestInventoryReleaseWhileTurningOffDoesNotSwap(t *testing.T) {
|
||
for _, inert := range []bool{false, true} {
|
||
items := []data.Item{{Sprite: icon.Potion, Name: "Potion"}, {Sprite: icon.Gem, Name: "Gem"}}
|
||
sel, off := 0, false
|
||
tt := ui.NewTester(func(c *ui.Context) {
|
||
pixel.Use(c, pixel.Settings{Palette: pixel.SpringDay, Inert: off && inert})
|
||
data.Inventory(c, "Bag", items, &sel, data.InventoryOptions{Disabled: off && !inert})
|
||
}, 400, 300)
|
||
r := find(t, tt, "Bag")
|
||
tt.Press(r.X+10, r.Y+10)
|
||
tt.Move(r.X+54, r.Y+10)
|
||
off = true
|
||
tt.Release(r.X+54, r.Y+10)
|
||
if items[0].Name != "Potion" {
|
||
t.Fatalf("inert %v: a release as it turned off swapped %v", inert, items)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestMatrixCursorSurvivesShrinkingData(t *testing.T) {
|
||
rows, cols := []string{"Read", "Write"}, []string{"Ann", "Bob"}
|
||
on := [][]bool{{false, false}, {false, false}}
|
||
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
|
||
data.Matrix(c, "Rights", rows, cols, on, data.MatrixOptions{})
|
||
})
|
||
r := find(t, tt, "Rights")
|
||
tt.ClickAt(r.X+4, r.Y+4)
|
||
tt.Key(0, ui.KeyEnd)
|
||
rows, cols, on = rows[:1], cols[:1], [][]bool{{false}}
|
||
tt.Frame()
|
||
tt.Key(0, ui.KeySpace)
|
||
if !on[0][0] {
|
||
t.Fatalf("Space after shrinking left %v", on)
|
||
}
|
||
}
|