Files
MujicaUI/code/log_viewer_test.go
T

132 lines
3.7 KiB
Go

package code
import (
"fmt"
"strings"
"testing"
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
)
func logViewerLines(n int) []LogLine {
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
lines := make([]LogLine, n)
for i := range lines {
lines[i] = LogLine{Time: at.Add(time.Duration(i) * time.Millisecond), Level: LogLevel(i % 4), Text: fmt.Sprintf("event %d", i)}
}
return lines
}
func logViewerTester(lines *[]LogLine, s *LogViewerState, disabled bool) *ui.Tester {
return ui.NewTester(func(c *ui.Context) {
core.Use(c, core.Settings{})
LogViewer(c, *lines, s, LogViewerOptions{Disabled: disabled}).Element.Height(400)
}, 760, 420)
}
func logViewerRows(tt *ui.Tester) int {
n := 0
for _, s := range tt.Texts() {
if strings.HasPrefix(s, "event ") {
n++
}
}
return n
}
func TestLogViewerHundredThousandLines(t *testing.T) {
lines := logViewerLines(100_000)
s := &LogViewerState{}
tt := logViewerTester(&lines, s, false)
if !tt.HasText("event 99999") {
t.Fatal("follow did not start at the tail")
}
if n := logViewerRows(tt); n == 0 || n > 40 {
t.Fatalf("built %d rows of 100000", n)
}
// Hiding a level refilters every line once.
if err := tt.Click("ERROR"); err != nil {
t.Fatal(err)
}
if len(s.shown) != 75_000 || tt.HasText("event 99999") {
t.Fatalf("shown %d after hiding ERROR", len(s.shown))
}
if n := logViewerRows(tt); n > 40 {
t.Fatalf("built %d rows after filtering", n)
}
}
func TestLogViewerFollowsAppends(t *testing.T) {
lines := logViewerLines(50)
s := &LogViewerState{}
tt := logViewerTester(&lines, s, false)
lines = append(lines, LogLine{Level: LogInfo, Text: "event fresh"})
tt.Frame()
if !tt.HasText("event fresh") || len(s.shown) != 51 {
t.Fatalf("appended line not followed; shown %d", len(s.shown))
}
if err := tt.Click("Follow"); err != nil {
t.Fatal(err)
}
if s.Follow {
t.Fatal("Follow toggle did not turn off")
}
tt.Scroll(300, 200, 0, -2000)
lines = append(lines, LogLine{Level: LogInfo, Text: "event later"})
tt.Frame()
if tt.HasText("event later") {
t.Fatal("paused viewer jumped to the new line")
}
}
func TestLogViewerHighlight(t *testing.T) {
lines := []LogLine{{Level: LogWarn, Text: "disk nearly full"}}
s := &LogViewerState{Query: "FULL"}
tt := logViewerTester(&lines, s, false)
if !tt.HasText("disk nearly full") || !tt.HasText("WARN") {
t.Fatalf("texts %q", tt.Texts())
}
var spans []ui.Span
ui.NewTester(func(c *ui.Context) {
core.Use(c, core.Settings{})
spans = CodeMatchSpans(c, "disk nearly full", "FULL")
}, 10, 10)
if len(spans) != 2 || spans[1].Text != "full" || !spans[1].Underline || spans[1].Background.A == 0 {
t.Fatalf("highlight spans %+v", spans)
}
}
func TestLogViewerDisabledAndEmpty(t *testing.T) {
lines := logViewerLines(8)
s := &LogViewerState{}
tt := logViewerTester(&lines, s, true)
_ = tt.Click("ERROR")
if s.Hidden[LogError] {
t.Fatal("disabled viewer changed its filter")
}
var none []LogLine
tt = logViewerTester(&none, &LogViewerState{}, false)
if !tt.HasText("No log lines") {
t.Fatalf("texts %q", tt.Texts())
}
}
func TestLogViewerSnapshot(t *testing.T) {
snapshot(t, "log_viewer", 640, 230, func(c *ui.Context) {
s := ui.Local(c.Root(), "snap", func() *LogViewerState { return &LogViewerState{Query: "seal"} })
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
lines := []LogLine{
{at, LogDebug, "loading ledger index"},
{at.Add(12 * time.Millisecond), LogInfo, "court opened by the chancellor"},
{at.Add(40 * time.Millisecond), LogWarn, "seal of decree 41 is faded"},
{at.Add(91 * time.Millisecond), LogError, "seal of decree 42 missing"},
}
ui.Column(c).Padding(12).Children(func() {
LogViewer(c, lines, *s, LogViewerOptions{}).Element.Height(206)
})
})
}