feat(logs): log viewer streaming real-time (F-004) — tab Log, follow+scroll, toggle timestamp, focus panel
CI / test (push) Successful in 38s

This commit is contained in:
oxpoe committed 2026-10-15 14:32:14 +07:00
1 parent 8f7b6c8ab6
commit de8bbbd354
7 files changed
+522 -30

No files matched your search

+43 -1
View File
@@ -401,7 +401,49 @@ func TestClickSelectsContainer(t *testing.T) {
// klik baris item ke-2: xterm y=4 (1-based) → app row 3 → indeks 1
u.send("\x1b[<0;10;4M\x1b[<0;10;4m")
u.waitHighlighted(short(items[1].Name), 3*time.Second)
u.waitContains(items[1].ID, 3*time.Second) // panel detail ikut berpindah
u.send("q")
if code := u.waitExit(5 * time.Second); code != 0 {
t.Fatalf("exit code %d, mau 0", code)
}
}
// TestLogTab — tab Log: stream nyata (timestamp/level), placeholder tab lain,
// fokus & scroll tidak crash. Butuh docker + minimal 1 kontainer.
func TestLogTab(t *testing.T) {
if _, err := os.Stat("/var/run/docker.sock"); err != nil {
t.Skip("docker socket tidak ada")
}
cl, err := docker.New()
if err != nil {
t.Skipf("docker: %v", err)
}
defer cl.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
items, err := cl.ListContainers(ctx)
if err != nil || len(items) == 0 {
t.Skipf("tidak ada kontainer (err=%v)", err)
}
name := items[0].Name
if len(name) > 12 {
name = name[:12]
}
u := startTUI(t)
u.waitContains("beranda", 5*time.Second)
u.send("1")
u.waitContains(name, 5*time.Second)
u.waitContains("Statistik", 3*time.Second) // baris tab
u.send("\t") // fokus ke panel konten
u.send("k")
u.send("j")
u.send("G")
u.send("l") // tab berikutnya → placeholder
u.waitContains("menyusul", 3*time.Second)
u.send("h") // kembali ke tab Log
u.send("\x1b[<0;60;10M\x1b[<0;60;10m") // klik area konten = fokus
u.send("q")
if code := u.waitExit(5 * time.Second); code != 0 {