222 lines
6.6 KiB
Go
222 lines
6.6 KiB
Go
package feedback_test
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/egoist/mygo/ui"
|
||
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/feedback"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||
)
|
||
|
||
func TestAlertAndBannerReportClicks(t *testing.T) {
|
||
closed, action, dismissed := 0, "", 0
|
||
tt := view(500, 300, newClock(), false, func(c *ui.Context) {
|
||
if feedback.Alert(c, "Low HP", "Drink a potion.", feedback.AlertOptions{Kind: feedback.Danger, Closable: true}).Closed {
|
||
closed++
|
||
}
|
||
r := feedback.Banner(c, "New quest!", feedback.BannerOptions{Kind: feedback.Info, Actions: []string{"VIEW"}, Dismissable: true})
|
||
if r.Action != "" {
|
||
action = r.Action
|
||
}
|
||
if r.Dismissed {
|
||
dismissed++
|
||
}
|
||
})
|
||
if count(slots(t, tt.Image(), find(t, tt, "Low HP")), pixel.Bad) == 0 {
|
||
t.Fatal("a danger alert shows no Bad")
|
||
}
|
||
click(t, tt, "CLOSE")
|
||
click(t, tt, "VIEW")
|
||
if closed != 1 || action != "VIEW" {
|
||
t.Fatalf("closed %d, action %q", closed, action)
|
||
}
|
||
tt = view(500, 100, newClock(), false, func(c *ui.Context) {
|
||
if feedback.Banner(c, "New quest!", feedback.BannerOptions{Dismissable: true}).Dismissed {
|
||
dismissed++
|
||
}
|
||
})
|
||
click(t, tt, "CLOSE")
|
||
if dismissed != 1 {
|
||
t.Fatal("the banner's × did not dismiss it")
|
||
}
|
||
}
|
||
|
||
func TestEmptyStateAction(t *testing.T) {
|
||
acted := 0
|
||
tt := view(400, 300, newClock(), false, func(c *ui.Context) {
|
||
if feedback.EmptyState(c, "Your bag is empty.", feedback.EmptyStateOptions{Action: "SHOP"}).Acted {
|
||
acted++
|
||
}
|
||
})
|
||
find(t, tt, "NO ITEMS")
|
||
click(t, tt, "SHOP")
|
||
if acted != 1 {
|
||
t.Fatal("the action did not report")
|
||
}
|
||
}
|
||
|
||
func TestErrorStateCountsDownAndContinues(t *testing.T) {
|
||
clk := newClock()
|
||
cont := 0
|
||
tt := view(600, 500, clk, true, func(c *ui.Context) {
|
||
if feedback.ErrorState(c, "The slime won.", feedback.ErrorStateOptions{}).Continue {
|
||
cont++
|
||
}
|
||
})
|
||
find(t, tt, "CONTINUE? 9")
|
||
wait(tt, clk, 3*time.Second)
|
||
find(t, tt, "CONTINUE? 6")
|
||
wait(tt, clk, 10*time.Second)
|
||
find(t, tt, "CONTINUE? 0")
|
||
click(t, tt, "RETRY")
|
||
if cont != 1 {
|
||
t.Fatal("RETRY did not report Continue")
|
||
}
|
||
}
|
||
|
||
func TestSuccessStateStarsPopInTurn(t *testing.T) {
|
||
stars := func(reduce bool, d time.Duration) int {
|
||
clk := newClock()
|
||
tt := view(600, 400, clk, reduce, func(c *ui.Context) {
|
||
feedback.SuccessState(c, "All coins found.", feedback.SuccessStateOptions{Stars: 3})
|
||
})
|
||
wait(tt, clk, d)
|
||
return count(slots(t, tt.Image(), find(t, tt, "STAGE CLEAR!")), pixel.Warn)
|
||
}
|
||
early, late := stars(false, 300*time.Millisecond), stars(false, 2*time.Second)
|
||
if early == 0 || early >= late {
|
||
t.Fatalf("stars did not pop in turn: %d then %d", early, late)
|
||
}
|
||
if stars(true, 0) != late {
|
||
t.Fatal("Reduce Motion did not show every star at once")
|
||
}
|
||
}
|
||
|
||
func TestInlineMessageMarks(t *testing.T) {
|
||
tt := view(400, 100, newClock(), false, func(c *ui.Context) {
|
||
feedback.InlineMessage(c, "Name taken", feedback.InlineMessageOptions{Kind: feedback.Danger})
|
||
feedback.InlineMessage(c, "Looks good", feedback.InlineMessageOptions{Kind: feedback.Success})
|
||
})
|
||
bad, good := slots(t, tt.Image(), find(t, tt, "Name taken")), slots(t, tt.Image(), find(t, tt, "Looks good"))
|
||
if count(bad, pixel.Bad) == 0 || count(good, pixel.Good) == 0 {
|
||
t.Fatal("marks do not take their kind's slot")
|
||
}
|
||
// The mark is the first seven dots across.
|
||
mark := func(l string, ink pixel.Slot) (out []bool) {
|
||
r := find(t, tt, l)
|
||
for _, v := range slots(t, tt.Image(), ui.Rect{X: r.X, Y: r.Y, W: 14, H: r.H}) {
|
||
out = append(out, v == ink)
|
||
}
|
||
return out
|
||
}
|
||
if equal(mark("Name taken", pixel.Bad), mark("Looks good", pixel.Good)) {
|
||
t.Fatal("✓ and ! look alike")
|
||
}
|
||
}
|
||
|
||
func TestToastsQueueExpireAndAct(t *testing.T) {
|
||
clk := newClock()
|
||
q := []feedback.Toast{{Message: "one"}, {Message: "two"}, {Message: "three", For: 5 * time.Second}, {Message: "four", Action: "UNDO"}}
|
||
var acted *feedback.Toast
|
||
tt := view(500, 400, clk, false, func(c *ui.Context) {
|
||
if r := feedback.Toasts(c, &q, feedback.ToastsOptions{}); r.Acted != nil {
|
||
acted = r.Acted
|
||
}
|
||
})
|
||
if _, ok := tt.Find("four"); ok || len(q) != 4 {
|
||
t.Fatal("more than three toasts showed")
|
||
}
|
||
wait(tt, clk, 3*time.Second)
|
||
find(t, tt, "three")
|
||
find(t, tt, "four")
|
||
if _, ok := tt.Find("one"); ok || len(q) != 2 {
|
||
t.Fatalf("toasts did not leave on time: %d left", len(q))
|
||
}
|
||
click(t, tt, "UNDO")
|
||
if acted == nil || acted.Message != "four" || len(q) != 1 {
|
||
t.Fatal("the action did not report or clear its toast")
|
||
}
|
||
wait(tt, clk, 3*time.Second)
|
||
if len(q) != 0 {
|
||
t.Fatal("a five-second toast outlived its time")
|
||
}
|
||
}
|
||
|
||
func TestContinuePromptAnswersOrTimesOut(t *testing.T) {
|
||
clk := newClock()
|
||
var yes, timedOut int
|
||
tt := view(400, 400, clk, false, func(c *ui.Context) {
|
||
r := feedback.ContinuePrompt(c, feedback.ContinuePromptOptions{Seconds: 5})
|
||
if r.Yes {
|
||
yes++
|
||
}
|
||
if r.TimedOut {
|
||
timedOut++
|
||
}
|
||
})
|
||
find(t, tt, "5")
|
||
wait(tt, clk, 2*time.Second)
|
||
find(t, tt, "3")
|
||
wait(tt, clk, 4*time.Second)
|
||
wait(tt, clk, time.Second)
|
||
click(t, tt, "YES")
|
||
if timedOut != 1 || yes != 0 {
|
||
t.Fatalf("timed out %d times; YES after time counted %d", timedOut, yes)
|
||
}
|
||
clk2 := newClock()
|
||
tt = view(400, 400, clk2, false, func(c *ui.Context) {
|
||
if feedback.ContinuePrompt(c, feedback.ContinuePromptOptions{}).Yes {
|
||
yes++
|
||
}
|
||
})
|
||
click(t, tt, "YES")
|
||
wait(tt, clk2, 20*time.Second)
|
||
if yes != 1 {
|
||
t.Fatal("YES did not report")
|
||
}
|
||
}
|
||
|
||
func TestTwinToastsBothShow(t *testing.T) {
|
||
q := []feedback.Toast{{Message: "same"}, {Message: "same"}}
|
||
tt := view(500, 300, newClock(), false, func(c *ui.Context) {
|
||
feedback.Toasts(c, &q, feedback.ToastsOptions{})
|
||
})
|
||
n := 0
|
||
for _, s := range tt.Texts() {
|
||
if s == "same" {
|
||
n++
|
||
}
|
||
}
|
||
// Each toast and its text both carry the message.
|
||
if n != 4 {
|
||
t.Fatalf("%d labels for two twin toasts", n)
|
||
}
|
||
}
|
||
|
||
func TestDisabledFeedbackButtonsDoNothing(t *testing.T) {
|
||
events := 0
|
||
q := []feedback.Toast{{Message: "dropped", Action: "UNDO"}}
|
||
tt := view(800, 2000, newClock(), false, func(c *ui.Context) {
|
||
on := func(b bool) {
|
||
if b {
|
||
events++
|
||
}
|
||
}
|
||
on(feedback.Alert(c, "A", "a", feedback.AlertOptions{Closable: true, Disabled: true}).Closed)
|
||
r := feedback.Banner(c, "B", feedback.BannerOptions{Actions: []string{"GO"}, Disabled: true})
|
||
on(r.Action != "")
|
||
on(feedback.EmptyState(c, "E", feedback.EmptyStateOptions{Action: "SHOP", Disabled: true}).Acted)
|
||
on(feedback.ErrorState(c, "X", feedback.ErrorStateOptions{Disabled: true}).Continue)
|
||
on(feedback.ContinuePrompt(c, feedback.ContinuePromptOptions{Disabled: true}).Yes)
|
||
on(feedback.Toasts(c, &q, feedback.ToastsOptions{Disabled: true}).Acted != nil)
|
||
})
|
||
for _, l := range []string{"CLOSE", "GO", "SHOP", "RETRY", "YES", "UNDO"} {
|
||
click(t, tt, l)
|
||
}
|
||
if events != 0 {
|
||
t.Fatalf("disabled buttons reported %d events", events)
|
||
}
|
||
}
|