layout: ikuti mock SVG — kolom tengah dashboard, toolbar workspace, kolom Aksi Tab
CI / test (push) Failing after 38s
CI / test (push) Failing after 38s
- Beranda: kolom tengah CTA 'masuk workspace' (klikable), strip Disclaimer dan Info aplikasi (versi/repo/config); geometri satu sumber (homeGeom). - Workspace: baris toolbar — pill Cari (klik atau '/' → cari-lompat daftar) + tombol [+ stack]/[konversi] klikable (sementara keduanya jalur konverter). - Detail kontainer: kolom kanan 'Aksi Tab' (mock TAB ACTION) berisi aksi tab aktif yang bisa diklik; disembunyikan di layar sempit agar tab bar utuh. - Footer: hint aksi diprioritaskan; status diagnostik dibuang bila tak muat. - internal/version: versi build dipakai CLI & strip Info aplikasi. - audio: Play/Status aman untuk Engine nil (Model zero-value di tes). - Tes: 3 skenario E2E baru (CTA, cari toolbar, kolom aksi) + tes layout.
This commit is contained in:
1 parent
8880660ce4
commit
5450a89a80
9 files changed
+638
-112
No files matched your search
+113
-2
@@ -599,6 +599,116 @@ func TestMouseHomeContainer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestHomeCtaWorkspace — klik kotak CTA "masuk workspace" di kolom tengah
|
||||
// beranda → pindah ke workspace bagian Stack.
|
||||
func TestHomeCtaWorkspace(t *testing.T) {
|
||||
u := startTUI(t)
|
||||
u.waitContains("beranda", 5*time.Second)
|
||||
u.waitContains("masuk workspace", 5*time.Second)
|
||||
if !u.clickText("masuk workspace") {
|
||||
t.Fatal("kotak CTA tidak ditemukan di beranda")
|
||||
}
|
||||
u.waitContains("[u] jalankan", 3*time.Second) // footer Stack = bukti pindah view
|
||||
u.send("q")
|
||||
if code := u.waitExit(5 * time.Second); code != 0 {
|
||||
t.Fatalf("exit code %d, mau 0", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestToolbarSearch — pill "Cari" di toolbar workspace: klik, ketik → kursor
|
||||
// melompat ke kontainer yang cocok; Enter keluar mode cari (paritas "/").
|
||||
func TestToolbarSearch(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) < 2 {
|
||||
t.Skipf("butuh >= 2 kontainer (err=%v, n=%d)", err, len(items))
|
||||
}
|
||||
|
||||
// Target: prefix nama yang tidak termuat di nama kontainer sebelumnya —
|
||||
// pencarian melompat ke kecocokan pertama dari atas.
|
||||
target := ""
|
||||
for i, it := range items {
|
||||
q := it.Name
|
||||
if len(q) > 6 {
|
||||
q = q[:6]
|
||||
}
|
||||
unique := true
|
||||
for k := 0; k < i; k++ {
|
||||
if strings.Contains(strings.ToLower(items[k].Name), strings.ToLower(q)) {
|
||||
unique = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if unique {
|
||||
target = q
|
||||
break
|
||||
}
|
||||
}
|
||||
if target == "" {
|
||||
t.Skip("nama kontainer tidak unik untuk uji cari")
|
||||
}
|
||||
|
||||
u := startTUI(t)
|
||||
u.waitContains("beranda", 5*time.Second)
|
||||
u.send("2")
|
||||
u.waitContains("[1-5]", 3*time.Second)
|
||||
if !u.clickText("Cari") {
|
||||
t.Fatal("pill Cari tidak ditemukan")
|
||||
}
|
||||
u.send(target)
|
||||
u.waitContains("[ / "+target, 3*time.Second) // query masuk ke pill
|
||||
u.waitHighlighted(target, 3*time.Second) // kursor melompat ke item cocok
|
||||
u.send("\r") // Enter: keluar mode cari
|
||||
u.waitContains("[ / Cari ]", 3*time.Second)
|
||||
u.send("q") // bukti sudah keluar mode cari: q keluar, bukan mengetik
|
||||
if code := u.waitExit(5 * time.Second); code != 0 {
|
||||
t.Fatalf("exit code %d, mau 0", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTabActionColumn — kolom kanan "Aksi Tab" (mock TAB ACTION) di detail
|
||||
// kontainer: item tampil per tab dan bisa diklik (klik [S] → toggle timestamp).
|
||||
func TestTabActionColumn(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)
|
||||
}
|
||||
|
||||
u := startTUI(t)
|
||||
u.waitContains("beranda", 5*time.Second)
|
||||
u.send("2")
|
||||
u.waitContains("[1-5]", 3*time.Second)
|
||||
u.waitContains("Aksi Tab", 3*time.Second)
|
||||
u.waitContains("ikut ekor", 3*time.Second)
|
||||
if !u.clickText("[S]") {
|
||||
t.Fatal("aksi [S] tidak ditemukan di kolom aksi tab")
|
||||
}
|
||||
u.waitContains("Timestamp log", 3*time.Second) // toast = aksi benar-benar jalan
|
||||
u.send("q")
|
||||
if code := u.waitExit(5 * time.Second); code != 0 {
|
||||
t.Fatalf("exit code %d, mau 0", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConvertCommand — CLI `tia-love convert`: hasil di stdout, exit code jelas.
|
||||
func TestConvertCommand(t *testing.T) {
|
||||
out, err := exec.Command(binPath, "convert", "docker run -p 80:80 --name web nginx").CombinedOutput()
|
||||
@@ -804,8 +914,9 @@ func TestClickSelectsContainer(t *testing.T) {
|
||||
u.waitContains(short(items[0].Name), 5*time.Second)
|
||||
u.waitHighlighted(short(items[0].Name), 3*time.Second) // seleksi awal (kursor 0)
|
||||
|
||||
// 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")
|
||||
// klik baris item ke-2: xterm y=5 (1-based) → app row 4 → indeks 1
|
||||
// (header + toolbar + border panel di atasnya).
|
||||
u.send("\x1b[<0;10;5M\x1b[<0;10;5m")
|
||||
u.waitHighlighted(short(items[1].Name), 3*time.Second)
|
||||
|
||||
u.send("q")
|
||||
|
||||
Reference in new issue
Block a user