feat: layar Pengaturan (PRD §5.1.5) + tombol Setting beranda + toggle suara
CI / test (push) Successful in 39s
CI / test (push) Successful in 39s
- Layar Pengaturan (',' atau klik chip Pengaturan beranda): bahasa, mode tema,
efek suara, volume, animasi, timestamp — semua berlaku langsung & tersimpan
ke config (config.Save), plus path config + catatan.
- Audio live: Engine.SetEnabled/SetVolume/Enabled/Volume; init idempoten
(readyOnce/started) sehingga engine yang lahir mati bisa dinyalakan tanpa
restart — komentar 'shortcut' F-024 lama dihapus.
- Beranda: kolom tengah kini CTA + hint '[m] suara: …' (klik = toggle) +
chip Pengaturan — draf dashboard lengkap.
- Tombol lama 'T'/'a'/'S' kini lewat applySetting yang sama (DRY + persist).
- TIA_LANG dihapus (tak perlu lagi): e2e memakai XDG_CONFIG_HOME temp
(config bersih per tes) → tes tidak tergantung config pengguna.
- Toast: klik di luar kotak toast tidak lagi menutupnya (hanya klik pada toast)
— tombol lain tetap bisa diklik saat notifikasi tampil.
- Tes: unit settings (apply/persist/jepit volume, baris plain==styled tanpa
ANSI) + e2e TestSettingsScreen (klik chip, toggle, bahasa→Inggris,
verifikasi config.yaml).
This commit is contained in:
1 parent
e903912029
commit
2d07935f83
10 files changed
+663
-62
No files matched your search
+99
-4
@@ -64,9 +64,26 @@ type tui struct {
|
||||
waited bool
|
||||
}
|
||||
|
||||
// startTUI memakai TIA_LANG=id eksplisit — deterministik walau mesin
|
||||
// pengembang/testing punya config.yaml sendiri.
|
||||
func startTUI(t *testing.T) *tui { return startTUIEnv(t, "TIA_LANG=id") }
|
||||
// startTUI memakai config bersih di XDG temp (bahasa id) — hasil tes tidak
|
||||
// bergantung config pengguna yang sebenarnya.
|
||||
func startTUI(t *testing.T) *tui {
|
||||
return startTUIEnv(t, "XDG_CONFIG_HOME="+writeTestConfig(t, "id"))
|
||||
}
|
||||
|
||||
// writeTestConfig — config.yaml minimal di XDG temp; kembalikan direktori XDG.
|
||||
func writeTestConfig(t *testing.T, lang string) string {
|
||||
t.Helper()
|
||||
xdg := t.TempDir()
|
||||
dir := filepath.Join(xdg, "tia")
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg := "version: 1\nui:\n language: " + lang + "\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(cfg), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return xdg
|
||||
}
|
||||
|
||||
// envWith — gabung env proses + override tanpa duplikat key
|
||||
// (Linux getenv memakai kemunculan pertama, jadi duplikat berbahaya).
|
||||
@@ -166,6 +183,37 @@ func (u *tui) waitContains(substr string, d time.Duration) {
|
||||
u.t.Fatalf("layar tidak memuat %q dalam %v.\n--- snapshot layar ---\n%s", substr, d, u.screen())
|
||||
}
|
||||
|
||||
// clickUntil — klik target berulang sampai substr tujuan muncul. Layar bisa
|
||||
// tertinggal satu frame tepat saat toast menutup; klik ulang aman (klik yang
|
||||
// tidak kena apa-apa tidak berefek).
|
||||
func (u *tui) clickUntil(clickSubstr, want string, d time.Duration) {
|
||||
u.t.Helper()
|
||||
deadline := time.Now().Add(d)
|
||||
for time.Now().Before(deadline) {
|
||||
if strings.Contains(u.screen(), want) {
|
||||
return
|
||||
}
|
||||
if !u.clickText(clickSubstr) {
|
||||
u.t.Fatalf("target klik %q tidak ditemukan", clickSubstr)
|
||||
}
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
}
|
||||
u.waitContains(want, time.Second) // gagalkan dengan snapshot
|
||||
}
|
||||
|
||||
// waitGone — tunggu sampai substr hilang dari layar (mis. toast selesai).
|
||||
func (u *tui) waitGone(substr string, d time.Duration) {
|
||||
u.t.Helper()
|
||||
deadline := time.Now().Add(d)
|
||||
for time.Now().Before(deadline) {
|
||||
if !strings.Contains(u.screen(), substr) {
|
||||
return
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
u.t.Fatalf("layar masih memuat %q setelah %v", substr, d)
|
||||
}
|
||||
|
||||
// waitHighlighted — menunggu baris yang memuat substr ber-latar (SGR bg),
|
||||
// yaitu penanda baris terpilih di daftar.
|
||||
func (u *tui) waitHighlighted(substr string, d time.Duration) {
|
||||
@@ -743,6 +791,53 @@ func TestWorkspaceBar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSettingsScreen — layar Pengaturan: tombol di beranda membuka, hint suara
|
||||
// & toggle berlaku, ganti bahasa langsung + tersimpan ke config.
|
||||
func TestSettingsScreen(t *testing.T) {
|
||||
xdg := writeTestConfig(t, "id")
|
||||
u := startTUIEnv(t, "XDG_CONFIG_HOME="+xdg)
|
||||
u.waitContains("beranda", 5*time.Second)
|
||||
|
||||
if !u.clickText("Pengaturan") { // chip di kolom tengah beranda
|
||||
t.Fatal("tombol Pengaturan tidak ditemukan di beranda")
|
||||
}
|
||||
u.waitContains("Efek suara", 3*time.Second)
|
||||
|
||||
// efek suara: nyala → mati (toast + baris)
|
||||
if !u.clickText("nyala") {
|
||||
t.Fatal("nilai efek suara tidak ditemukan")
|
||||
}
|
||||
u.waitContains("Efek suara: mati", 3*time.Second)
|
||||
|
||||
// `,` tutup → kembali ke beranda; hint "[m] suara: mati" ikut berubah
|
||||
u.send(",")
|
||||
u.waitContains("suara: mati", 3*time.Second)
|
||||
u.waitGone("Efek suara: mati", 6*time.Second) // tunggu toast tutup (layout stabil)
|
||||
|
||||
// klik hint suara = toggle (mouse-native): mati → nyala
|
||||
u.clickUntil("suara: mati", "suara: nyala", 6*time.Second)
|
||||
|
||||
// `,` buka lagi; ganti bahasa → UI langsung Inggris
|
||||
u.send(",")
|
||||
u.waitContains("Konfigurasi", 3*time.Second) // penanda layar Pengaturan (bukan chip beranda)
|
||||
u.clickUntil("Indonesia", "[ English ]", 6*time.Second) // nilai baris = penanda tetap
|
||||
u.send("q")
|
||||
if code := u.waitExit(5 * time.Second); code != 0 {
|
||||
t.Fatalf("exit code %d, mau 0", code)
|
||||
}
|
||||
|
||||
// perubahan tersimpan ke config
|
||||
b, err := os.ReadFile(filepath.Join(xdg, "tia", "config.yaml"))
|
||||
if err != nil {
|
||||
t.Fatalf("config tidak terbaca: %v", err)
|
||||
}
|
||||
for _, want := range []string{"language: en", "sound_enabled: true"} {
|
||||
if !strings.Contains(string(b), want) {
|
||||
t.Fatalf("config tidak memuat %q:\n%s", want, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -872,7 +967,7 @@ func TestThemeCycle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnglishLanguage(t *testing.T) {
|
||||
u := startTUIEnv(t, "TIA_LANG=en")
|
||||
u := startTUIEnv(t, "XDG_CONFIG_HOME="+writeTestConfig(t, "en"))
|
||||
u.waitContains("· home", 5*time.Second)
|
||||
u.waitContains("Clock", 5*time.Second)
|
||||
u.send("1")
|
||||
|
||||
Reference in new issue
Block a user