Replaces the single-slot status-line flash with a top-right toast stack over the focused pane. flashError, flashTransient, and notifyAttention all push onto the same stack (cap 5, FIFO drop). Ctrl-N dismisses the most recent toast; empty stack falls through to the focused PTY so readline / nano / emacs / opencode bindings keep working. A new "Clear notifications" palette item empties the stack.
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package app
|
|
|
|
import "testing"
|
|
|
|
func TestToastStackPushAndOrder(t *testing.T) {
|
|
var s toastStack
|
|
s.push(toastInfo, "one")
|
|
s.push(toastError, "two")
|
|
s.push(toastAttention, "three")
|
|
|
|
snap := s.snapshot()
|
|
if len(snap) != 3 {
|
|
t.Fatalf("snapshot len = %d, want 3", len(snap))
|
|
}
|
|
if snap[0].text != "one" || snap[1].text != "two" || snap[2].text != "three" {
|
|
t.Fatalf("snapshot order wrong: %#v", snap)
|
|
}
|
|
if snap[0].kind != toastInfo || snap[1].kind != toastError || snap[2].kind != toastAttention {
|
|
t.Fatalf("snapshot kinds wrong: %#v", snap)
|
|
}
|
|
// IDs strictly increase.
|
|
if !(snap[0].id < snap[1].id && snap[1].id < snap[2].id) {
|
|
t.Fatalf("ids not increasing: %#v", snap)
|
|
}
|
|
}
|
|
|
|
func TestToastStackCapDropsOldest(t *testing.T) {
|
|
var s toastStack
|
|
for i := 0; i < toastStackCap+3; i++ {
|
|
s.push(toastInfo, "msg")
|
|
}
|
|
snap := s.snapshot()
|
|
if len(snap) != toastStackCap {
|
|
t.Fatalf("len = %d, want %d", len(snap), toastStackCap)
|
|
}
|
|
// The earliest IDs should have been dropped, leaving the highest
|
|
// toastStackCap IDs.
|
|
for i := 1; i < len(snap); i++ {
|
|
if snap[i].id <= snap[i-1].id {
|
|
t.Fatalf("ordering broken after cap: %#v", snap)
|
|
}
|
|
}
|
|
// First retained id should be 4 (1,2,3 dropped; cap=5 leaves 4..8).
|
|
want := uint64(toastStackCap + 3 - toastStackCap + 1)
|
|
if snap[0].id != want {
|
|
t.Fatalf("first retained id = %d, want %d", snap[0].id, want)
|
|
}
|
|
}
|
|
|
|
func TestToastStackDismissTop(t *testing.T) {
|
|
var s toastStack
|
|
if s.dismissTop() {
|
|
t.Fatalf("dismissTop on empty stack returned true")
|
|
}
|
|
s.push(toastInfo, "a")
|
|
s.push(toastError, "b")
|
|
if !s.dismissTop() {
|
|
t.Fatalf("dismissTop returned false with items present")
|
|
}
|
|
snap := s.snapshot()
|
|
if len(snap) != 1 || snap[0].text != "a" {
|
|
t.Fatalf("after dismissTop: %#v", snap)
|
|
}
|
|
if !s.dismissTop() {
|
|
t.Fatalf("dismissTop on last item returned false")
|
|
}
|
|
if s.length() != 0 {
|
|
t.Fatalf("length after final dismiss = %d, want 0", s.length())
|
|
}
|
|
}
|
|
|
|
func TestToastStackClear(t *testing.T) {
|
|
var s toastStack
|
|
if s.clear() {
|
|
t.Fatalf("clear on empty returned true")
|
|
}
|
|
s.push(toastInfo, "a")
|
|
s.push(toastError, "b")
|
|
s.push(toastAttention, "c")
|
|
if !s.clear() {
|
|
t.Fatalf("clear returned false with items present")
|
|
}
|
|
if s.length() != 0 {
|
|
t.Fatalf("length after clear = %d, want 0", s.length())
|
|
}
|
|
if snap := s.snapshot(); snap != nil {
|
|
t.Fatalf("snapshot after clear = %#v, want nil", snap)
|
|
}
|
|
}
|
|
|
|
func TestToastStackSnapshotIsCopy(t *testing.T) {
|
|
var s toastStack
|
|
s.push(toastInfo, "a")
|
|
snap := s.snapshot()
|
|
snap[0].text = "mutated"
|
|
again := s.snapshot()
|
|
if again[0].text != "a" {
|
|
t.Fatalf("snapshot is not an independent copy: %#v", again)
|
|
}
|
|
}
|