Files
8bitMyGoUI/overlay/dialogs_test.go
T
ZacharyZhang-NY 5c3c883b7d Add overlay components 176-195; pixel Harbor and Kit.Cue
Dialogs on DialogBase with focusable panels that harbor parked focus,
popovers on PopoverBase, bubbles, drawers, sheets, a lightbox, an RPG
dialogue box, notifications, a pause menu and a dithered title card.
Kit.Cue moves from feedback to pixel.
2026-10-11 23:11:46 -04:00

336 lines
9.4 KiB
Go

package overlay_test
import (
"testing"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/overlay"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
func TestDialogActsClosesAndHandsFocusBack(t *testing.T) {
open, action, closed := false, -1, 0
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
if input.Button(c, "OPEN", input.ButtonOptions{}).Clicked {
open = true
}
r := overlay.Dialog(c, "Save game?", &open, overlay.DialogOptions{Actions: []string{"LATER", "SAVE"}}, func() {
pixel.Text(c, "Slot 1 is free.", pixel.TextOptions{})
})
if r.Action >= 0 {
action = r.Action
}
if r.Closed {
closed++
}
})
click(t, tt, "OPEN")
if !shows(tt, "Slot 1 is free.") {
t.Fatal("the dialog did not open")
}
click(t, tt, "SAVE")
if open || action != 1 || shows(tt, "Slot 1 is free.") {
t.Fatalf("SAVE left open %v, action %d", open, action)
}
if !tt.Focused("OPEN") {
t.Fatal("closing did not hand the focus back")
}
tt.Key(0, ui.KeyEnter)
tt.Key(0, ui.KeyEscape)
if open || closed != 1 {
t.Fatalf("Escape left open %v, %d closes", open, closed)
}
keeps(t, tt.Image())
}
func TestAlertDialogEnterPressesDefault(t *testing.T) {
open, pressed := true, -1
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
if r := overlay.AlertDialog(c, "Low HP", "Heal before the boss?", &open, overlay.AlertDialogOptions{Buttons: []string{"NO", "HEAL"}, Default: 1}); r.Pressed >= 0 {
pressed = r.Pressed
}
})
tt.Key(0, ui.KeyEnter)
if open || pressed != 1 {
t.Fatalf("Enter pressed %d, open %v", pressed, open)
}
open = true
tt.Frame()
click(t, tt, "NO")
if open || pressed != 0 {
t.Fatalf("NO pressed %d", pressed)
}
defer func() {
if recover() == nil {
t.Fatal("a Default past the buttons did not panic")
}
}()
view(100, 100, newClock(), false, func(c *ui.Context) {
overlay.AlertDialog(c, "x", "y", &open, overlay.AlertDialogOptions{Default: 1})
})
}
func TestConfirmDialogStartsOnNo(t *testing.T) {
open, yes, no := true, 0, 0
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
r := overlay.ConfirmDialog(c, "Drop the sword?", &open, overlay.ConfirmDialogOptions{})
if r.Confirmed {
yes++
}
if r.Canceled {
no++
}
})
if !shows(tt, "ARE YOU SURE?") {
t.Fatalf("texts %q", tt.Texts())
}
tt.Key(0, ui.KeyEnter)
if open || yes != 0 || no != 1 {
t.Fatalf("Enter on the start gave yes %d no %d", yes, no)
}
open = true
tt.Frame()
tt.Key(0, ui.KeyUp)
tt.Key(0, ui.KeyEnter)
if open || yes != 1 {
t.Fatalf("Up, Enter gave yes %d", yes)
}
open = true
tt.Frame()
click(t, tt, "YES")
tt.Frame()
open = true
tt.Frame()
tt.Key(0, ui.KeyEscape)
if yes != 2 || no != 2 {
t.Fatalf("click YES then Escape gave yes %d no %d", yes, no)
}
keeps(t, tt.Image())
}
func TestChoicePromptTakesDigitsArrowsAndClicks(t *testing.T) {
open, chosen, canceled := true, -1, 0
choices := []string{"Fight", "Talk", "Flee"}
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
r := overlay.ChoicePrompt(c, "A slime appears!", choices, &open, overlay.ChoicePromptOptions{})
if r.Chosen >= 0 {
chosen = r.Chosen
}
if r.Canceled {
canceled++
}
})
tt.Key(0, ui.Key3)
if open || chosen != 2 {
t.Fatalf("3 chose %d", chosen)
}
open = true
tt.Frame()
tt.Key(0, ui.KeyDown)
tt.Key(0, ui.KeySpace)
if chosen != 1 {
t.Fatalf("Down, Space chose %d", chosen)
}
open = true
tt.Frame()
click(t, tt, "Flee")
open = true
tt.Frame()
tt.Key(0, ui.KeyEscape)
if chosen != 2 || canceled != 1 || open {
t.Fatalf("click Flee, Escape: chosen %d, %d cancels", chosen, canceled)
}
}
func TestPauseMenuResumesAndChooses(t *testing.T) {
paused, chosen := true, -1
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
if r := overlay.PauseMenu(c, &paused, overlay.PauseMenuOptions{}); r.Chosen >= 0 {
chosen = r.Chosen
}
})
if !shows(tt, "PAUSED") {
t.Fatalf("texts %q", tt.Texts())
}
tt.Key(0, ui.KeyDown)
tt.Key(0, ui.KeyEnter)
if !paused || chosen != 1 {
t.Fatalf("OPTIONS left paused %v, chosen %d", paused, chosen)
}
tt.Key(0, ui.KeyEscape)
if paused || chosen != 0 {
t.Fatalf("Escape left paused %v, chosen %d", paused, chosen)
}
keeps(t, tt.Image())
}
func TestPromptDialogSubmitsAndCancels(t *testing.T) {
open, name, sent, canceled := true, "", 0, 0
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
r := overlay.PromptDialog(c, "ENTER YOUR NAME", &name, &open, overlay.PromptDialogOptions{MaxLength: 8})
if r.Submitted {
sent++
}
if r.Canceled {
canceled++
}
})
tt.Type("LINK")
tt.Key(0, ui.KeyEnter)
if open || name != "LINK" || sent != 1 {
t.Fatalf("typing, Enter left %q, open %v, %d sent", name, open, sent)
}
open = true
tt.Frame()
click(t, tt, "CANCEL")
if open || canceled != 1 {
t.Fatalf("CANCEL left open %v, %d cancels", open, canceled)
}
}
func TestProgressDialogCancels(t *testing.T) {
open, canceled := true, 0
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
if overlay.ProgressDialog(c, "Saving", 0.5, &open, overlay.ProgressDialogOptions{}).Canceled {
canceled++
}
})
if !shows(tt, "Saving") {
t.Fatalf("texts %q", tt.Texts())
}
click(t, tt, "CANCEL")
if open || canceled != 1 {
t.Fatalf("CANCEL left open %v", open)
}
keeps(t, tt.Image())
}
func TestOffDialogsTakeNoAnswers(t *testing.T) {
for _, inert := range []bool{false, true} {
open, action := true, -1
tt := view(640, 400, newClock(), inert, func(c *ui.Context) {
if r := overlay.Dialog(c, "Quest", &open, overlay.DialogOptions{Actions: []string{"ACCEPT"}, Disabled: !inert}, func() {}); r.Action >= 0 {
action = r.Action
}
})
click(t, tt, "ACCEPT")
if action >= 0 || !open {
t.Fatalf("inert %v: a dialog took action %d", inert, action)
}
alert, pressed := true, -1
tt = view(640, 400, newClock(), inert, func(c *ui.Context) {
if r := overlay.AlertDialog(c, "Warn", "Careful.", &alert, overlay.AlertDialogOptions{Disabled: !inert}); r.Pressed >= 0 {
pressed = r.Pressed
}
})
tt.Key(0, ui.KeyEnter)
click(t, tt, "OK")
if pressed >= 0 || !alert {
t.Fatalf("inert %v: an alert took button %d", inert, pressed)
}
}
}
func TestInertMenusTakeNoPicks(t *testing.T) {
open, paused, chosen, picked := true, true, -1, -1
tt := view(640, 400, newClock(), true, func(c *ui.Context) {
if r := overlay.ChoicePrompt(c, "Go?", []string{"Yes", "No"}, &open, overlay.ChoicePromptOptions{}); r.Chosen >= 0 {
chosen = r.Chosen
}
})
tt.Key(0, ui.Key1)
click(t, tt, "No")
tt2 := view(640, 400, newClock(), true, func(c *ui.Context) {
if r := overlay.PauseMenu(c, &paused, overlay.PauseMenuOptions{}); r.Chosen > 0 {
picked = r.Chosen
}
})
click(t, tt2, "QUIT")
if chosen >= 0 || picked >= 0 || !open {
t.Fatalf("inert menus picked %d and %d", chosen, picked)
}
}
func TestDisabledMenusAndPromptsTakeNoAnswers(t *testing.T) {
open, chosen, sure, paused, picked, prompt, name, sent := true, -1, true, true, -1, true, "", 0
tt := view(640, 400, newClock(), false, func(c *ui.Context) {
if r := overlay.ChoicePrompt(c, "Go?", []string{"Yes", "No"}, &open, overlay.ChoicePromptOptions{Disabled: true}); r.Chosen >= 0 {
chosen = r.Chosen
}
})
tt.Key(0, ui.Key1)
tt2 := view(640, 400, newClock(), false, func(c *ui.Context) {
if overlay.ConfirmDialog(c, "Sure?", &sure, overlay.ConfirmDialogOptions{Disabled: true}).Confirmed {
chosen = 99
}
})
click(t, tt2, "YES")
tt3 := view(640, 400, newClock(), false, func(c *ui.Context) {
if r := overlay.PauseMenu(c, &paused, overlay.PauseMenuOptions{Disabled: true}); r.Chosen > 0 {
picked = r.Chosen
}
})
click(t, tt3, "QUIT")
tt4 := view(640, 400, newClock(), false, func(c *ui.Context) {
if overlay.PromptDialog(c, "NAME", &name, &prompt, overlay.PromptDialogOptions{Disabled: true}).Submitted {
sent++
}
})
tt4.Type("AB")
click(t, tt4, "OK")
if chosen >= 0 || picked >= 0 || sent != 0 || name != "" || !open || !sure || !prompt {
t.Fatalf("disabled answers: chosen %d picked %d sent %d name %q", chosen, picked, sent, name)
}
}
func TestFocusComesBackAfterAnOffSpellInADialog(t *testing.T) {
inert, disabled := false, false
open, action := true, -1
tt := ui.NewTester(func(c *ui.Context) {
pixel.Use(c, pixel.Settings{Palette: pixel.SpringDay, Inert: inert})
if r := overlay.Dialog(c, "Quest", &open, overlay.DialogOptions{Actions: []string{"ACCEPT"}}, func() {}); r.Action >= 0 {
action = r.Action
}
}, 640, 400)
tt.Key(0, ui.KeyTab)
if !tt.Focused("ACCEPT") {
t.Fatal("Tab did not reach ACCEPT")
}
inert = true
tt.Frame()
inert = false
tt.Frame()
tt.Key(0, ui.KeyEnter)
if action != 0 {
t.Fatalf("Enter after the veil gave action %d", action)
}
chosen, choice := -1, true
tt = view(640, 400, newClock(), false, func(c *ui.Context) {
if r := overlay.ChoicePrompt(c, "Go?", []string{"Yes", "No"}, &choice, overlay.ChoicePromptOptions{Disabled: disabled}); r.Chosen >= 0 {
chosen = r.Chosen
}
})
disabled = true
tt.Frame()
disabled = false
tt.Frame()
tt.Key(0, ui.KeyEnter)
if chosen != 0 {
t.Fatalf("Enter after a disabled spell chose %d", chosen)
}
name, prompt := "", true
tt = view(640, 400, newClock(), false, func(c *ui.Context) {
overlay.PromptDialog(c, "NAME", &name, &prompt, overlay.PromptDialogOptions{Disabled: disabled})
})
disabled = true
tt.Frame()
disabled = false
tt.Frame()
tt.Type("AB")
if name != "AB" {
t.Fatalf("typing after a disabled spell gave %q", name)
}
}