Ikon otomatis dari internet + cursor mengikuti tema host
- Cari ikon online (Iconify) dari dialog install, plus tombol sync ikon per shortcut - Auto ikon: file terdekat -> favicon situs (termasuk ikon manifest PWA) -> ikon brand yang namanya cocok -> ikon default; sumber ikon disimpan di metadata - Query dibersihkan (web.whatsapp.com -> whatsapp) dan kata <4 huruf tidak dipakai supaya aplikasi tanpa ikon resmi tidak dapat ikon acak - Unduhan thumbnail dibatasi 4 bersamaan + retry sekali (rate limit Iconify) - Cursor: XCURSOR_THEME/XCURSOR_SIZE diambil dari config KDE/GTK/~/.icons default, dan AppImage tidak lagi membundel libX*/libwayland*/libxkbcommon (dipakai dari host) - README: bagian ikon + integrasi desktop
This commit is contained in:
1 parent
70534bf6e2
commit
7f7cb97a65
19 files changed
+1117
-40
No files matched your search
@@ -0,0 +1,135 @@
|
||||
package icon
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func fakeIconify(t *testing.T) *httptest.Server {
|
||||
t.Helper()
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/search":
|
||||
got := r.URL.Query().Get("query")
|
||||
if got == "" {
|
||||
http.Error(w, "no query", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// ikon whatsapp hanya untuk query "whatsapp" polos, supaya fallback ikut teruji
|
||||
if !strings.Contains(strings.ToLower(got), "whatsapp") || strings.Contains(got, ".") {
|
||||
_, _ = w.Write([]byte(`{"icons":[],"total":0}`))
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"icons":["ic:baseline-whatsapp","mdi:whatsapp","logos:whatsapp-icon"],"total":3}`))
|
||||
case "/logos/whatsapp-icon.svg":
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
_, _ = w.Write([]byte(`<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"></svg>`))
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
return srv
|
||||
}
|
||||
|
||||
func TestSearchBuildsDownloadURLs(t *testing.T) {
|
||||
srv := fakeIconify(t)
|
||||
SetSearchEndpoint(srv.URL)
|
||||
t.Cleanup(func() { SetSearchEndpoint("https://api.iconify.design") })
|
||||
|
||||
results, err := Search("whatsapp", 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(results) != 3 {
|
||||
t.Fatalf("hasil pencarian = %d, mau 3", len(results))
|
||||
}
|
||||
if results[0].Name != "ic:baseline-whatsapp" || results[0].ShortName() != "baseline-whatsapp" {
|
||||
t.Errorf("hasil pertama salah: %+v", results[0])
|
||||
}
|
||||
if results[2].URL != srv.URL+"/logos/whatsapp-icon.svg?height=256" {
|
||||
t.Errorf("URL unduhan salah: %s", results[2].URL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryHintAndSearchBest(t *testing.T) {
|
||||
srv := fakeIconify(t)
|
||||
SetSearchEndpoint(srv.URL)
|
||||
t.Cleanup(func() { SetSearchEndpoint("https://api.iconify.design") })
|
||||
|
||||
// nama seperti host: TLD dibuang, kata terpanjang dipakai
|
||||
if got := QueryHint("web.whatsapp.com"); got != "whatsapp" {
|
||||
t.Errorf("QueryHint(host) = %q", got)
|
||||
}
|
||||
if got := QueryHint("WhatsApp Web"); got != "WhatsApp" {
|
||||
t.Errorf("QueryHint(nama) = %q", got)
|
||||
}
|
||||
if got := QueryHint(""); got != "" {
|
||||
t.Errorf("QueryHint(kosong) = %q", got)
|
||||
}
|
||||
|
||||
// query mentah gagal, tapi varian "whatsapp" berhasil
|
||||
results, err := SearchBest("web.whatsapp.com", 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(results) != 3 {
|
||||
t.Errorf("SearchBest() = %d hasil", len(results))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindIgnoresGenericWords(t *testing.T) {
|
||||
srv := fakeIconify(t)
|
||||
SetSearchEndpoint(srv.URL)
|
||||
t.Cleanup(func() { SetSearchEndpoint("https://api.iconify.design") })
|
||||
|
||||
// kata <= 3 huruf ("my", "go", "app") tidak boleh dipakai untuk mencari
|
||||
if _, ok := Find("my-go-app"); ok {
|
||||
t.Error("nama tanpa ikon resmi harus gagal, bukan dapat ikon acak")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchEmptyQuery(t *testing.T) {
|
||||
if _, err := Search(" ", 5); err == nil {
|
||||
t.Error("query kosong harus error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindPicksMatchingName(t *testing.T) {
|
||||
srv := fakeIconify(t)
|
||||
SetSearchEndpoint(srv.URL)
|
||||
t.Cleanup(func() { SetSearchEndpoint("https://api.iconify.design") })
|
||||
|
||||
// "WhatsApp Web" harus cocok dengan ikon bernama whatsapp, bukan hasil acak.
|
||||
// (hasil pertama "ic:baseline-whatsapp" tidak cocok karena namanya lebih panjang,
|
||||
// jadi yang dipakai "mdi:whatsapp")
|
||||
got, ok := Find("WhatsApp Web")
|
||||
if !ok {
|
||||
t.Fatal("Find() tidak menemukan kandidat")
|
||||
}
|
||||
if got.Name != "mdi:whatsapp" {
|
||||
t.Errorf("ikon terpilih = %s", got.Name)
|
||||
}
|
||||
|
||||
// Nama yang tidak berhubungan tidak boleh dipaksa memakai ikon apa pun.
|
||||
if _, ok := Find("zzz-tidak-ada-xyz"); ok {
|
||||
t.Error("nama asing tidak boleh dapat ikon acak")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromURLSniffsFormat(t *testing.T) {
|
||||
srv := fakeIconify(t)
|
||||
data, ext, err := FromURL(srv.URL + "/logos/whatsapp-icon.svg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ext != ".svg" || len(data) == 0 {
|
||||
t.Errorf("FromURL = ext %q, %d byte", ext, len(data))
|
||||
}
|
||||
|
||||
if _, _, err := FromURL(srv.URL + "/tidak-ada"); err == nil {
|
||||
t.Error("URL 404 harus error")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user