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.
222 lines
6.4 KiB
Go
222 lines
6.4 KiB
Go
package data_test
|
||
|
||
import (
|
||
"slices"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/egoist/mygo/ui"
|
||
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||
)
|
||
|
||
func TestCollapsibleShowsContentWhileOpen(t *testing.T) {
|
||
open, changes := false, 0
|
||
tt := view(300, 200, newClock(), false, func(c *ui.Context) {
|
||
if data.Collapsible(c, "Secrets", &open, data.CollapsibleOptions{}, func() {
|
||
pixel.Text(c, "Konami", pixel.TextOptions{})
|
||
}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
if shows(tt, "Konami") {
|
||
t.Fatal("closed content shows")
|
||
}
|
||
click(t, tt, "Secrets")
|
||
if !open || !shows(tt, "Konami") {
|
||
t.Fatal("a click did not open it")
|
||
}
|
||
tt.Key(0, ui.KeyEnter)
|
||
if open || shows(tt, "Konami") || changes != 2 {
|
||
t.Fatalf("Enter left open %v, %d changes", open, changes)
|
||
}
|
||
keeps(t, tt.Image())
|
||
}
|
||
|
||
func TestAccordionKeepsOneOpenUnlessMany(t *testing.T) {
|
||
for _, many := range []bool{false, true} {
|
||
open := []bool{true, false, false}
|
||
tt := view(300, 300, newClock(), false, func(c *ui.Context) {
|
||
data.Accordion(c, "Help", []string{"Move", "Jump", "Save"}, open, data.AccordionOptions{Many: many}, func(i int) {
|
||
pixel.Text(c, []string{"Arrows", "Space", "Start"}[i], pixel.TextOptions{})
|
||
})
|
||
})
|
||
click(t, tt, "Save")
|
||
want := []bool{many, false, true}
|
||
if !slices.Equal(open, want) || shows(tt, "Arrows") != many || !shows(tt, "Start") {
|
||
t.Fatalf("many %v: open %v", many, open)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestCollapsibleOffStaysPut(t *testing.T) {
|
||
for _, inert := range []bool{false, true} {
|
||
open := false
|
||
tt := view(300, 200, newClock(), inert, func(c *ui.Context) {
|
||
data.Collapsible(c, "Secrets", &open, data.CollapsibleOptions{Disabled: !inert}, func() {})
|
||
})
|
||
click(t, tt, "Secrets")
|
||
if open {
|
||
t.Fatalf("inert %v: a click opened it", inert)
|
||
}
|
||
}
|
||
}
|
||
|
||
func deck(c *ui.Context, page *int, o data.CarouselOptions, changes *int) {
|
||
if data.Carousel(c, "Tips", 3, page, o, func(i int) {
|
||
pixel.Text(c, []string{"Tip A", "Tip B", "Tip C"}[i], pixel.TextOptions{})
|
||
}).Changed {
|
||
*changes++
|
||
}
|
||
}
|
||
|
||
func TestCarouselTurnsAndWraps(t *testing.T) {
|
||
page, changes := 0, 0
|
||
tt := view(300, 200, newClock(), false, func(c *ui.Context) { deck(c, &page, data.CarouselOptions{}, &changes) })
|
||
click(t, tt, "PREVIOUS")
|
||
if page != 2 || !shows(tt, "Tip C") {
|
||
t.Fatalf("◀ from the first page went to %d", page)
|
||
}
|
||
click(t, tt, "NEXT")
|
||
click(t, tt, "NEXT")
|
||
if page != 1 || !shows(tt, "Tip B") || changes != 3 {
|
||
t.Fatalf("▶ ▶ went to %d, %d changes", page, changes)
|
||
}
|
||
keeps(t, tt.Image())
|
||
}
|
||
|
||
func TestCarouselTurnsByItselfUnlessReduced(t *testing.T) {
|
||
for _, reduce := range []bool{false, true} {
|
||
clk, page, changes := newClock(), 0, 0
|
||
tt := view(300, 200, clk, false, func(c *ui.Context) { deck(c, &page, data.CarouselOptions{Every: 3}, &changes) })
|
||
if reduce {
|
||
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
|
||
}
|
||
tt.Move(599, 399)
|
||
clk.t = clk.t.Add(2 * time.Second)
|
||
tt.Frame()
|
||
if page != 0 {
|
||
t.Fatalf("reduce %v: turned after 2 s", reduce)
|
||
}
|
||
clk.t = clk.t.Add(time.Second)
|
||
tt.Frame()
|
||
if want := map[bool]int{false: 1, true: 0}[reduce]; page != want || !shows(tt, []string{"Tip A", "Tip B"}[want]) {
|
||
t.Fatalf("reduce %v: page %d after 3 s", reduce, page)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestCarouselWaitsUnderThePointer(t *testing.T) {
|
||
clk, page, changes := newClock(), 0, 0
|
||
tt := view(300, 200, clk, false, func(c *ui.Context) { deck(c, &page, data.CarouselOptions{Every: 1}, &changes) })
|
||
r := find(t, tt, "Tip A")
|
||
tt.Move(r.X+2, r.Y+2)
|
||
clk.t = clk.t.Add(5 * time.Second)
|
||
tt.Frame()
|
||
if page != 0 {
|
||
t.Fatalf("turned to %d under the pointer", page)
|
||
}
|
||
}
|
||
|
||
func TestPagerStepsAndResizes(t *testing.T) {
|
||
page, size, changes := 0, 20, 0
|
||
total := 312
|
||
tt := view(500, 100, newClock(), false, func(c *ui.Context) {
|
||
if data.Pager(c, "Pages", total, &page, &size, data.PagerOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
if !tt.HasText("1–20 OF 312") {
|
||
t.Fatalf("texts %q", tt.Texts())
|
||
}
|
||
click(t, tt, "NEXT")
|
||
if page != 1 || !tt.HasText("21–40 OF 312") {
|
||
t.Fatalf("▶ went to page %d", page)
|
||
}
|
||
click(t, tt, "50")
|
||
if size != 50 || page != 0 || !tt.HasText("1–50 OF 312") {
|
||
t.Fatalf("size 50 left page %d size %d", page, size)
|
||
}
|
||
page = 6
|
||
tt.Frame()
|
||
if !tt.HasText("301–312 OF 312") {
|
||
t.Fatalf("last page texts %q", tt.Texts())
|
||
}
|
||
total = 40
|
||
tt.Frame()
|
||
if page != 0 || changes != 3 {
|
||
t.Fatalf("a shorter total left page %d, %d changes", page, changes)
|
||
}
|
||
}
|
||
|
||
func TestFilterBarTogglesAndClears(t *testing.T) {
|
||
on := []bool{false, false, false}
|
||
changes := 0
|
||
tt := view(400, 100, newClock(), false, func(c *ui.Context) {
|
||
if data.FilterBar(c, "Filters", []string{"Fire", "Ice", "Bolt"}, on, data.FilterBarOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
click(t, tt, "CLEAR")
|
||
if changes != 0 {
|
||
t.Fatal("CLEAR worked with nothing on")
|
||
}
|
||
click(t, tt, "Fire")
|
||
click(t, tt, "Bolt")
|
||
if !slices.Equal(on, []bool{true, false, true}) {
|
||
t.Fatalf("chips left %v", on)
|
||
}
|
||
click(t, tt, "CLEAR")
|
||
if slices.Contains(on, true) || changes != 3 {
|
||
t.Fatalf("CLEAR left %v, %d changes", on, changes)
|
||
}
|
||
}
|
||
|
||
func TestPropertyGridEditsEachKind(t *testing.T) {
|
||
name, speed, fly := "Aria", 3.0, false
|
||
changes := 0
|
||
props := []data.Property{{Name: "Name", Text: &name}, {Name: "Speed", Number: &speed}, {Name: "Fly", Switch: &fly}}
|
||
tt := view(400, 200, newClock(), false, func(c *ui.Context) {
|
||
if data.PropertyGrid(c, "Hero", props, data.PropertyGridOptions{}).Changed {
|
||
changes++
|
||
}
|
||
})
|
||
// The switch sits past the 80-dot name column.
|
||
r := find(t, tt, "Fly")
|
||
tt.ClickAt(r.X+180, r.Y+r.H/2)
|
||
if !fly {
|
||
t.Fatalf("the switch row did not switch: %v", r)
|
||
}
|
||
n := find(t, tt, "Name")
|
||
tt.ClickAt(n.X+380, n.Y+n.H/2)
|
||
tt.Type("!")
|
||
if name != "Aria!" || changes != 2 {
|
||
t.Fatalf("typing gave %q, %d changes", name, changes)
|
||
}
|
||
defer func() {
|
||
if recover() == nil {
|
||
t.Fatal("a row with two values did not panic")
|
||
}
|
||
}()
|
||
view(400, 200, newClock(), false, func(c *ui.Context) {
|
||
data.PropertyGrid(c, "Bad", []data.Property{{Name: "Both", Text: &name, Switch: &fly}}, data.PropertyGridOptions{})
|
||
})
|
||
}
|
||
|
||
func TestPagerRefusesBadSizes(t *testing.T) {
|
||
for _, sizes := range [][]int{{20, 0}, {20, -5}} {
|
||
func() {
|
||
defer func() {
|
||
if recover() == nil {
|
||
t.Fatalf("sizes %v did not panic", sizes)
|
||
}
|
||
}()
|
||
page, size := 0, 20
|
||
view(400, 100, newClock(), false, func(c *ui.Context) {
|
||
data.Pager(c, "Pages", 50, &page, &size, data.PagerOptions{Sizes: sizes})
|
||
})
|
||
}()
|
||
}
|
||
}
|