// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package atspi_test import ( "context" "os" "strings" "testing" "time" "git.stormux.org/storm/cthulhu/internal/atspi" "git.stormux.org/storm/cthulhu/internal/presentation" ) // GTK 4 popover menu items leave Name empty and point LABELLED_BY at an // internal label whose role is invalid; only its text holds the item's label. func TestNativeGTK4MenuItemLabels(t *testing.T) { if os.Getenv("CTHULHU_NATIVE_GTK4_MENU_LABELS") != "1" { t.Skip("requires isolated GTK 4 menu label fixture") } ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) defer cancel() adapter := atspi.New() if err := adapter.Start(ctx); err != nil { t.Fatal(err) } defer func() { stopCtx, stopCancel := context.WithTimeout(context.Background(), 3*time.Second) defer stopCancel() if err := adapter.Stop(stopCtx); err != nil { t.Error(err) } }() tree, err := adapter.CaptureDesktop(ctx) if err != nil { t.Fatal(err) } root, err := tree.Root() if err != nil { t.Fatal(err) } var items []atspi.Snapshot var walk func(atspi.ObjectReference) walk = func(ref atspi.ObjectReference) { snapshot, err := tree.Resolve(ref) if err != nil { t.Fatal(err) } if snapshot.Role == "menu item" || snapshot.Role == "check menu item" || snapshot.Role == "radio menu item" { items = append(items, snapshot) } child, ok, err := tree.FirstChild(ref) if err != nil { t.Fatal(err) } for ok { walk(child) child, ok, err = tree.NextSibling(child) if err != nil { t.Fatal(err) } } } walk(root) for _, want := range []string{"New Message", "Add Account…", "Group into Conversations", "All Messages"} { t.Run(want, func(t *testing.T) { for _, item := range items { if item.Name == want { if spoken := presentation.FocusText(item); !strings.Contains(spoken, want) { t.Errorf("speech %q omits label %q", spoken, want) } return } } names := make([]string, 0, len(items)) for _, item := range items { names = append(names, item.Role+": "+item.Name) } t.Errorf("no menu item named %q; captured %q", want, names) }) } }