wails/v3/pkg/application/menu_internal_test.go
Lea Anthony 2604ecc0f8
test(v3): add comprehensive unit tests for pkg/application (#4827)
* test(v3): add comprehensive unit tests for pkg/application

Add 11 new test files to improve test coverage of the pkg/application
package from 13.6% to 17.7%.

New test files:
- context_test.go: Context struct operations
- services_test.go: Service management and lifecycle
- parameter_test.go: Parameter and CallError types
- dialogs_test.go: Dialog utilities and button methods
- webview_window_options_test.go: Window options and constants
- application_options_test.go: ChainMiddleware and app config
- keys_test.go: Keyboard accelerator parsing
- single_instance_test.go: Single instance management and encryption
- menuitem_internal_test.go: Menu item internal functions
- menu_internal_test.go: Menu internal functions
- screenmanager_internal_test.go: Screen geometry and transformations

The 40% target was not fully achievable because ~50% of the codebase
is platform-specific code that can only be tested on respective
platforms. Tests focus on pure Go logic, utility functions, and
data structures that can be tested cross-platform.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review comments

- dialogs_test.go: improve ID recycling test to verify either recycled
  ID (id3 == id1) or new unique ID (id3 > id2)
- keys_test.go: make accelerator String() tests platform-agnostic by
  checking suffix patterns rather than exact platform-specific strings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: normalize temp dir path for macOS compatibility in test

os.TempDir() returns a trailing slash on macOS, causing path comparison
to fail. Use filepath.Clean() to normalize both paths.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove REVIEW.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: simplify changelog entry

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 09:53:24 +11:00

403 lines
8.1 KiB
Go

package application
import (
"testing"
)
func TestNewMenu(t *testing.T) {
menu := NewMenu()
if menu == nil {
t.Fatal("NewMenu returned nil")
}
if menu.items != nil {
t.Error("items should be nil initially")
}
}
func TestMenu_Add(t *testing.T) {
menu := NewMenu()
item := menu.Add("Test Item")
if item == nil {
t.Fatal("Add returned nil")
}
if item.label != "Test Item" {
t.Errorf("label = %q, want %q", item.label, "Test Item")
}
if len(menu.items) != 1 {
t.Errorf("items count = %d, want 1", len(menu.items))
}
// Clean up
menu.Destroy()
}
func TestMenu_AddSeparator(t *testing.T) {
menu := NewMenu()
menu.AddSeparator()
if len(menu.items) != 1 {
t.Errorf("items count = %d, want 1", len(menu.items))
}
if menu.items[0].itemType != separator {
t.Error("item should be a separator")
}
// Clean up
menu.Destroy()
}
func TestMenu_AddCheckbox(t *testing.T) {
menu := NewMenu()
item := menu.AddCheckbox("Check Me", true)
if item == nil {
t.Fatal("AddCheckbox returned nil")
}
if item.label != "Check Me" {
t.Errorf("label = %q, want %q", item.label, "Check Me")
}
if item.itemType != checkbox {
t.Error("item should be a checkbox")
}
if !item.checked {
t.Error("checkbox should be checked")
}
// Clean up
menu.Destroy()
}
func TestMenu_AddRadio(t *testing.T) {
menu := NewMenu()
item := menu.AddRadio("Radio Option", false)
if item == nil {
t.Fatal("AddRadio returned nil")
}
if item.label != "Radio Option" {
t.Errorf("label = %q, want %q", item.label, "Radio Option")
}
if item.itemType != radio {
t.Error("item should be a radio")
}
if item.checked {
t.Error("radio should not be checked")
}
// Clean up
menu.Destroy()
}
func TestMenu_AddSubmenu(t *testing.T) {
menu := NewMenu()
subMenu := menu.AddSubmenu("Submenu")
if subMenu == nil {
t.Fatal("AddSubmenu returned nil")
}
if len(menu.items) != 1 {
t.Errorf("items count = %d, want 1", len(menu.items))
}
if menu.items[0].itemType != submenu {
t.Error("item should be a submenu")
}
// Clean up
menu.Destroy()
}
func TestMenu_SetLabel(t *testing.T) {
menu := NewMenu()
menu.SetLabel("My Menu")
if menu.label != "My Menu" {
t.Errorf("label = %q, want %q", menu.label, "My Menu")
}
}
func TestMenu_ItemAt(t *testing.T) {
menu := NewMenu()
menu.Add("Item 0")
menu.Add("Item 1")
menu.Add("Item 2")
item := menu.ItemAt(1)
if item == nil {
t.Fatal("ItemAt returned nil")
}
if item.label != "Item 1" {
t.Errorf("label = %q, want %q", item.label, "Item 1")
}
// Clean up
menu.Destroy()
}
func TestMenu_ItemAt_OutOfBounds(t *testing.T) {
menu := NewMenu()
menu.Add("Item")
if menu.ItemAt(-1) != nil {
t.Error("ItemAt(-1) should return nil")
}
if menu.ItemAt(1) != nil {
t.Error("ItemAt(1) should return nil for single item menu")
}
if menu.ItemAt(100) != nil {
t.Error("ItemAt(100) should return nil")
}
// Clean up
menu.Destroy()
}
func TestMenu_FindByLabel(t *testing.T) {
menu := NewMenu()
menu.Add("First")
menu.Add("Second")
menu.Add("Third")
found := menu.FindByLabel("Second")
if found == nil {
t.Fatal("FindByLabel returned nil")
}
if found.label != "Second" {
t.Errorf("label = %q, want %q", found.label, "Second")
}
// Clean up
menu.Destroy()
}
func TestMenu_FindByLabel_NotFound(t *testing.T) {
menu := NewMenu()
menu.Add("First")
found := menu.FindByLabel("NonExistent")
if found != nil {
t.Error("FindByLabel should return nil for non-existent label")
}
// Clean up
menu.Destroy()
}
func TestMenu_FindByLabel_InSubmenu(t *testing.T) {
menu := NewMenu()
submenu := menu.AddSubmenu("Submenu")
submenu.Add("Nested Item")
found := menu.FindByLabel("Nested Item")
if found == nil {
t.Fatal("FindByLabel should find item in submenu")
}
if found.label != "Nested Item" {
t.Errorf("label = %q, want %q", found.label, "Nested Item")
}
// Clean up
menu.Destroy()
}
func TestMenu_RemoveMenuItem(t *testing.T) {
menu := NewMenu()
item1 := menu.Add("First")
item2 := menu.Add("Second")
menu.Add("Third")
menu.RemoveMenuItem(item2)
if len(menu.items) != 2 {
t.Errorf("items count = %d, want 2", len(menu.items))
}
if menu.FindByLabel("Second") != nil {
t.Error("Second item should be removed")
}
// Clean up
removeMenuItemByID(item1.id)
}
func TestMenu_Clear(t *testing.T) {
menu := NewMenu()
menu.Add("First")
menu.Add("Second")
menu.Add("Third")
menu.Clear()
if menu.items != nil {
t.Error("items should be nil after Clear")
}
}
func TestMenu_Append(t *testing.T) {
menu1 := NewMenu()
menu1.Add("Item 1")
menu2 := NewMenu()
menu2.Add("Item 2")
menu2.Add("Item 3")
menu1.Append(menu2)
if len(menu1.items) != 3 {
t.Errorf("items count = %d, want 3", len(menu1.items))
}
// Clean up
menu1.Destroy()
}
func TestMenu_Append_Nil(t *testing.T) {
menu := NewMenu()
menu.Add("Item")
menu.Append(nil)
if len(menu.items) != 1 {
t.Error("Append(nil) should not change menu")
}
// Clean up
menu.Destroy()
}
func TestMenu_Prepend(t *testing.T) {
menu1 := NewMenu()
menu1.Add("Item 3")
menu2 := NewMenu()
menu2.Add("Item 1")
menu2.Add("Item 2")
menu1.Prepend(menu2)
if len(menu1.items) != 3 {
t.Errorf("items count = %d, want 3", len(menu1.items))
}
if menu1.items[0].label != "Item 1" {
t.Errorf("First item should be 'Item 1', got %q", menu1.items[0].label)
}
// Clean up
menu1.Destroy()
}
func TestMenu_Clone(t *testing.T) {
menu := NewMenu()
menu.SetLabel("Original Menu")
menu.Add("Item 1")
menu.Add("Item 2")
clone := menu.Clone()
if clone == menu {
t.Error("Clone should return different pointer")
}
if clone.label != menu.label {
t.Error("Clone should have same label")
}
if len(clone.items) != len(menu.items) {
t.Error("Clone should have same number of items")
}
// Clean up
menu.Destroy()
clone.Destroy()
}
func TestNewMenuFromItems(t *testing.T) {
item1 := NewMenuItem("Item 1")
item2 := NewMenuItem("Item 2")
item3 := NewMenuItem("Item 3")
menu := NewMenuFromItems(item1, item2, item3)
if menu == nil {
t.Fatal("NewMenuFromItems returned nil")
}
if len(menu.items) != 3 {
t.Errorf("items count = %d, want 3", len(menu.items))
}
// Clean up
menu.Destroy()
}
func TestNewSubmenu(t *testing.T) {
items := NewMenu()
items.Add("Sub Item 1")
items.Add("Sub Item 2")
item := NewSubmenu("My Submenu", items)
if item == nil {
t.Fatal("NewSubmenu returned nil")
}
if item.label != "My Submenu" {
t.Errorf("label = %q, want %q", item.label, "My Submenu")
}
if item.submenu != items {
t.Error("submenu should be the provided menu")
}
// Clean up
removeMenuItemByID(item.id)
}
func TestMenu_ProcessRadioGroups(t *testing.T) {
menu := NewMenu()
// Add some non-radio items
menu.Add("Regular Item")
// Add a group of radio items
radio1 := menu.AddRadio("Radio 1", true)
radio2 := menu.AddRadio("Radio 2", false)
radio3 := menu.AddRadio("Radio 3", false)
// Add separator to end the group
menu.AddSeparator()
// Add another group
radio4 := menu.AddRadio("Radio A", true)
radio5 := menu.AddRadio("Radio B", false)
// Process radio groups
menu.processRadioGroups()
// First group should be linked
if len(radio1.radioGroupMembers) != 3 {
t.Errorf("First group should have 3 members, got %d", len(radio1.radioGroupMembers))
}
if len(radio2.radioGroupMembers) != 3 {
t.Errorf("radio2 should have 3 members, got %d", len(radio2.radioGroupMembers))
}
if len(radio3.radioGroupMembers) != 3 {
t.Errorf("radio3 should have 3 members, got %d", len(radio3.radioGroupMembers))
}
// Second group should be linked
if len(radio4.radioGroupMembers) != 2 {
t.Errorf("Second group should have 2 members, got %d", len(radio4.radioGroupMembers))
}
if len(radio5.radioGroupMembers) != 2 {
t.Errorf("radio5 should have 2 members, got %d", len(radio5.radioGroupMembers))
}
// Clean up
menu.Destroy()
}
func TestMenu_SetContextData(t *testing.T) {
menu := NewMenu()
menu.Add("Item 1")
menu.Add("Item 2")
data := &ContextMenuData{Data: "test-data"}
menu.setContextData(data)
// Verify data was set on items
for _, item := range menu.items {
if item.contextMenuData != data {
t.Error("Context data should be set on all items")
}
}
// Clean up
menu.Destroy()
}