Files
lazyrun/internal/ui/dialog_test.go
T
oxpoe deaa30689c Tooltip hint, loading bar, edit shortcut, export/import .lazy
- Keterangan tombol saat hover: baris hint di footer window dan di dalam dialog
  (bukan popup: overlay Fyne menelan klik pertama, tombol jadi perlu 2x klik)
- Dialog proses sekarang pakai progress bar berjalan (bukan spinner diam)
- Tombol edit per shortcut: ganti binary/URL, ikon (file/online), nama, kategori,
  dan lokasi; berkas .desktop + salinan Desktop ditulis ulang, berkas lama dibuang,
  jenis aplikasi dideteksi ulang, binary lama dihapus kalau nama berubah
- Export/Import daftar shortcut ke berkas SQLite .lazy (driver murni Go modernc),
  skema + meta format versi, impor tidak menimpa shortcut yang sudah ada
- Tes: detect kind, edit (rename/ganti binary/ikon/target), db roundtrip, merge impor,
  tooltip (hint muncul/hilang + klik tetap sampai ke tombol), semua paket hijau
- README: bagian backup + hint tombol
2026-10-13 09:12:39 +07:00

147 lines
4.4 KiB
Go

package ui
import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"fyne.io/fyne/v2/test"
"lazyrun/internal/icon"
"lazyrun/internal/store"
)
// TestNoEmptyDismissButton memastikan modal tidak memakai dialog.NewCustom,
// yang selalu menambah tombol dismiss walau labelnya kosong (tombol hantu
// di bagian bawah modal). Kita punya baris tombol sendiri, jadi harus memakai
// NewCustomWithoutButtons.
func TestNoEmptyDismissButton(t *testing.T) {
files, err := filepath.Glob("*.go")
if err != nil {
t.Fatal(err)
}
for _, file := range files {
if strings.HasSuffix(file, "_test.go") {
continue
}
data, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(data), "dialog.NewCustom(") {
t.Errorf("%s memakai dialog.NewCustom (menambah tombol kosong); gunakan NewCustomWithoutButtons", file)
}
}
}
// TestDialogsOpenWithoutPanic membuka semua modal di test driver.
func TestDialogsOpenWithoutPanic(t *testing.T) {
a := test.NewApp()
defer a.Quit()
// pencarian ikon diarahkan ke port mati: tes tidak boleh menyentuh internet
// dan tidak boleh mengubah UI dari goroutine latar.
icon.SetSearchEndpoint("http://127.0.0.1:1")
defer icon.SetSearchEndpoint("https://api.iconify.design")
ui, err := build(a, "test")
if err != nil {
t.Fatal(err)
}
ui.win.Show()
ui.showExecutableDialog()
if top := ui.win.Canvas().Overlays().Top(); top == nil {
t.Fatal("modal executable tidak muncul")
}
ui.showWebAppDialog()
if len(ui.browsers) > 0 {
if top := ui.win.Canvas().Overlays().Top(); top == nil {
t.Fatal("modal web app tidak muncul")
}
}
ui.showEditDialog(store.Shortcut{Slug: "winbox", Name: "Winbox", Type: store.TypeExecutable, Kind: "windows",
BinaryPath: "/tmp/winbox", IconPath: "", Categories: "Network;", Targets: store.Targets{Menu: true, Desktop: true}})
if top := ui.win.Canvas().Overlays().Top(); top == nil {
t.Fatal("modal edit executable tidak muncul")
}
ui.showEditDialog(store.Shortcut{Slug: "wa", Name: "WhatsApp Web", Type: store.TypeWebApp, Kind: "pwa",
URL: "https://web.whatsapp.com", Browser: "/usr/bin/vivaldi", Targets: store.Targets{Menu: true}})
if top := ui.win.Canvas().Overlays().Top(); top == nil {
t.Fatal("modal edit webapp tidak muncul")
}
ui.showIconSearch("whatsapp", func(icon.Result, []byte) {})
if top := ui.win.Canvas().Overlays().Top(); top == nil {
t.Fatal("modal pencarian ikon tidak muncul")
}
// tunggu goroutine pencarian selesai: test driver menjalankan fyne.Do inline,
// jadi sisa goroutine bisa bertabrakan dengan tes berikutnya kalau dibiarkan.
time.Sleep(300 * time.Millisecond)
}
// TestStringsCoverUsedKeys memastikan setiap kunci T() yang dipakai ada di tabel
// terjemahan (kalau tidak, UI menampilkan nama kunci mentah).
func TestStringsCoverUsedKeys(t *testing.T) {
files, err := filepath.Glob("*.go")
if err != nil {
t.Fatal(err)
}
used := map[string]bool{}
for _, file := range files {
if strings.HasSuffix(file, "_test.go") {
continue
}
data, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
for _, m := range regexp.MustCompile(`T\("([a-z_]+)"\)`).FindAllStringSubmatch(string(data), -1) {
used[m[1]] = true
}
}
if len(used) == 0 {
t.Fatal("tidak ada kunci T() yang ditemukan")
}
for key := range used {
if _, ok := tr[key]; !ok {
t.Errorf("kunci %q dipakai tapi tidak ada di tabel terjemahan", key)
}
}
// dan sebaliknya: kunci terjemahan yang tidak terpakai sebaiknya dihapus
for key := range tr {
if !used[key] {
t.Errorf("kunci %q ada di tabel terjemahan tapi tidak dipakai", key)
}
}
}
func TestMergeImported(t *testing.T) {
target := &store.Store{}
target.Add(store.Shortcut{Slug: "winbox", Name: "Winbox"})
added, skipped := mergeImported(target, []store.Shortcut{
{Slug: "winbox", Name: "Winbox lama"}, // sudah ada -> dilewati
{Slug: "figma", Name: "Figma"}, // baru
{Slug: "", Name: "Tanpa slug"}, // rusak -> dilewati
{Slug: "kosong", Name: " "}, // tanpa nama -> dilewati
{Slug: "deploy", Name: " deploy.sh "}, // nama dirapikan
})
if added != 2 || skipped != 3 {
t.Errorf("added=%d skipped=%d, mau 2 dan 3", added, skipped)
}
if len(target.Items) != 3 {
t.Errorf("jumlah item = %d", len(target.Items))
}
dep, ok := target.Get("deploy")
if !ok || target.Items[dep].Name != "deploy.sh" {
t.Errorf("nama item tidak dirapikan: %+v", target.Items)
}
}