Files
tia-love/internal/app/layout_test.go
T
oxpoe 7af6d48a80
CI / test (push) Successful in 28s
layout: subjudul bar workspace jadi tautan seksi (klik = pindah seksi)
Nama seksi di subjudul (stack, kontainer, image, volume, jaringan) kini
bergaya aksen dan bisa diklik → pindah seksi, paritas tombol 1-5. Rentang
kolom tiap nama dihitung dari satu sumber (workspaceSubsections) sehingga
render & hit-test tidak bisa melenceng; layar sempit memotong nama yang
tidak muat. Tes: unit (span → seksi, batas sempit) + e2e (klik 'image').
2026-10-16 03:49:44 +07:00

134 lines
4.7 KiB
Go

package app
import (
"strings"
"testing"
"charm.land/lipgloss/v2"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/docker"
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/i18n"
)
// TestDetailWidths — kolom aksi tab tampil hanya bila layar cukup lebar
// (tab bar detail tetap utuh di layar sempit).
func TestDetailWidths(t *testing.T) {
m := Model{width: 120, section: sectionContainers}
wDetail, wAct := m.detailWidths()
if wAct == 0 {
t.Fatalf("layar 120: kolom aksi harus tampil (wDetail=%d)", wDetail)
}
if wDetail+wAct != 120-m.leftWidth() {
t.Fatalf("layar 120: lebar tidak menjumlah (wDetail=%d wAct=%d)", wDetail, wAct)
}
m.width = 80
if _, wAct = m.detailWidths(); wAct != 0 {
t.Fatalf("layar 80: kolom aksi harus disembunyikan, dapat %d", wAct)
}
}
// TestWorkspaceBar — bar workspace memuat subjudul seksi + tombol stack,
// menampilkan query saat mode cari, dan tidak melebihi lebar dalam panel.
func TestWorkspaceBar(t *testing.T) {
for _, width := range []int{40, 80, 120} {
m := Model{width: width, view: viewList}
subLine, searchLine := m.workspaceBarPlain()
if !strings.Contains(subLine, i18n.T("toolbar.addStack")) || !strings.Contains(subLine, i18n.T("toolbar.convert")) {
t.Fatalf("w=%d: tombol stack/konversi tidak ada: %q", width, subLine)
}
if !strings.Contains(searchLine, "[ / "+i18n.T("toolbar.search")+" ]") {
t.Fatalf("w=%d: pill cari tidak ada: %q", width, searchLine)
}
inner := width - 4
if w := lipgloss.Width(subLine); w > inner {
t.Fatalf("w=%d: subjudul+tombol lebar %d > %d", width, w, inner)
}
if w := lipgloss.Width(searchLine); w > inner {
t.Fatalf("w=%d: pill cari lebar %d > %d", width, w, inner)
}
m.searching, m.query = true, "abc"
if _, searchLine = m.workspaceBarPlain(); !strings.Contains(searchLine, "/ abc") {
t.Fatalf("w=%d: query tidak tampil di pill: %q", width, searchLine)
}
}
// Subjudul = daftar seksi (mock): "stack, kontainer, image, volume, jaringan".
m := Model{width: 120, view: viewList}
subLine, _ := m.workspaceBarPlain()
want := strings.ToLower(i18n.T("section.stacks")) + ", " +
strings.ToLower(i18n.T("section.containers")) + ", " +
strings.ToLower(i18n.T("section.images")) + ", " +
strings.ToLower(i18n.T("section.volumes")) + ", " +
strings.ToLower(i18n.T("section.networks"))
if !strings.HasPrefix(subLine, want) {
t.Fatalf("subjudul bar salah: %q (mau %q)", subLine, want)
}
// Tautan seksi: tiap span menunjuk nama seksi yang benar & bisa diklik.
keys := []string{"stacks", "containers", "images", "volumes", "networks"}
sub, spans := m.workspaceSubsections()
runes := []rune(sub)
if len(spans) != len(keys) {
t.Fatalf("jumlah span = %d, mau %d", len(spans), len(keys))
}
for i, sp := range spans {
got, wantName := string(runes[sp[0]:sp[1]]), strings.ToLower(i18n.T("section."+keys[i]))
if got != wantName {
t.Fatalf("span %d = %q, mau %q", i, got, wantName)
}
if s, ok := m.workspaceSectionAt(sp[0]); !ok || int(s) != i {
t.Fatalf("klik kolom %d harus → seksi %d (ok=%v s=%d)", sp[0], i, ok, s)
}
}
if _, ok := m.workspaceSectionAt(len(runes) + 5); ok {
t.Fatal("kolom di luar subjudul tidak boleh memetakan seksi")
}
// Layar sempit: span tetap di dalam subjudul yang tampak.
narrow := Model{width: 40, view: viewList}
subNarrow, spansNarrow, _, _, _ := narrow.workspaceBarParts()
limit := len([]rune(subNarrow))
for _, sp := range spansNarrow {
if sp[0] < 0 || sp[1] > limit {
t.Fatalf("span %v di luar subjudul tampak (%d kolom)", sp, limit)
}
}
}
// TestPanelTitleFits — judul/meta panjang tidak boleh melebihi lebar panel:
// nama kontainer/stack panjang pernah mendorong kolom kanan keluar layar.
func TestPanelTitleFits(t *testing.T) {
long := strings.Repeat("GITEA-ACTIONS-TASK-", 8)
for _, w := range []int{6, 10, 40, 100} {
for _, meta := range []string{"", "running", long} {
out := panel(long, meta, []string{"isi"}, w, 4, false)
for _, line := range strings.Split(out, "\n") {
if got := lipgloss.Width(line); got > w {
t.Fatalf("w=%d meta=%q: baris lebar %d > %d: %q", w, meta, got, w, line)
}
}
}
}
}
// TestSearchJump — pencarian melompat ke item pertama yang cocok (daftar tetap
// utuh), dan nama item mengikuti seksi aktif.
func TestSearchJump(t *testing.T) {
m := Model{
section: sectionContainers,
containers: []docker.Container{{Name: "alfa"}, {Name: "beta"}, {Name: "betawi"}},
}
m.query = "beta"
m.searchJumpCmd()
if got := m.cur(); got != 1 {
t.Fatalf("kursor = %d, mau 1 (kecocokan pertama)", got)
}
if m.itemName(2) != "betawi" {
t.Fatalf("itemName(2) = %q", m.itemName(2))
}
m.query = "tidak-ada"
m.searchJumpCmd()
if got := m.cur(); got != 1 {
t.Fatalf("tanpa kecocokan kursor harus tetap: %d", got)
}
}