Files
Piik/internal/client/clientapp/console_test.go
T
AAA da47189fc3 refactor: unify Go runtime and audited frontend
Squash integration of the reviewed Go core, frontend audit fixes, release tooling, and deployment hygiene. CI passed all Server/Client jobs.
2026-09-06 23:26:01 +08:00

111 lines
3.5 KiB
Go

package clientapp
import (
"bytes"
"context"
"strings"
"testing"
"time"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/charmbracelet/x/ansi"
)
func TestConsolePresentationAndShutdown(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
model := consoleModel{cancel: cancel, view: consoleView{
mode: "link", state: "ready", protected: true,
entry: "http://localhost:8787/#client-access=private-secret",
invite: "https://a-long-public-link-name.example.test/",
}}
for _, language := range []string{"zh", "en", "vis"} {
for _, width := range []int{30, 52, 90} {
model.language, model.width, model.height = language, width, 18
for _, styled := range []bool{true, false} {
content := model.content(styled)
if styled && strings.Count(content, "\n")+1 > model.height {
t.Fatalf("%s width %d height overflow: %d", language, width, strings.Count(content, "\n")+1)
}
if strings.Contains(content, "private-secret") || strings.Contains(content, "client-access") {
t.Fatal("console exposed the entry credential")
}
for _, line := range strings.Split(content, "\n") {
if ansi.StringWidth(line) > width {
t.Fatalf("%s width %d overflow: %q", language, width, line)
}
}
}
}
}
model.colors = true
for _, compact := range []bool{false, true} {
model.width, model.height = 80, 32
if compact {
model.width, model.height = 40, 18
}
for _, frame := range []int{0, 3, 5, 8} {
mascot := consoleBlockTV(frame, true)
lines := strings.Split(mascot, "\n")
if len(lines) != len(strings.Split(consoleBlockTV(0, false), "\n")) {
t.Fatal("mascot animation moved the layout")
}
for _, line := range lines {
if ansi.StringWidth(line) != ansi.StringWidth(lines[0]) {
t.Fatal("mascot frame changed width")
}
}
model.frame = frame
content := model.content(true)
if lipgloss.Width(content) > model.width || lipgloss.Height(content) > model.height {
t.Fatal("color console overflowed")
}
}
}
if !strings.Contains(consoleTV(5, true, false), "- | *") ||
!strings.Contains(consoleTV(0, false, false), "o -") ||
!strings.Contains(consoleBlockTV(0, false), "\u2584\u2584") ||
consoleTV(8, false, false) != consoleTV(0, false, false) {
t.Fatal("mascot wink or static state changed")
}
model.frame = 16
updated, cmd := model.Update(consoleIdleWink{})
waking := updated.(consoleModel)
if !waking.animating() || waking.frame != 8 || cmd == nil {
t.Fatal("idle wink did not resume without redrawing the shell")
}
for range 8 {
updated, _ = waking.Update(consoleTick{})
waking = updated.(consoleModel)
}
if waking.animating() || !waking.idlePending {
t.Fatal("idle wink did not return to rest")
}
var output bytes.Buffer
program := tea.NewProgram(model, tea.WithInput(nil), tea.WithOutput(&output), tea.WithoutSignalHandler(), tea.WithoutRenderer())
done := make(chan error, 1)
go func() { _, err := program.Run(); done <- err }()
program.Send(consoleLanguage("zh"))
program.Send(tea.KeyPressMsg{Code: 'q'})
select {
case <-ctx.Done():
case <-time.After(time.Second):
t.Fatal("quit did not request application shutdown")
}
program.Send(consoleFinished{})
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
case <-time.After(time.Second):
program.Kill()
t.Fatal("console did not finish after application cleanup")
}
updated, _ = model.Update(consoleFinished{})
if text := updated.(consoleModel).content(false); strings.Contains(text, "http") {
t.Fatal("stopped console retained a live entry")
}
}