github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/internal/platform/menu/manager_test.go (about)

     1  //go:build windows
     2  
     3  package menu_test
     4  
     5  import (
     6  	platformMenu "github.com/AlpineAIO/wails/v2/internal/platform/menu"
     7  	"github.com/AlpineAIO/wails/v2/pkg/menu"
     8  	"github.com/stretchr/testify/require"
     9  	"testing"
    10  )
    11  
    12  func TestManager_ProcessClick_Checkbox(t *testing.T) {
    13  
    14  	checkbox := menu.Label("Checkbox").SetChecked(false)
    15  	menu1 := &menu.Menu{
    16  		Items: []*menu.MenuItem{
    17  			checkbox,
    18  		},
    19  	}
    20  	menu2 := &menu.Menu{
    21  		Items: []*menu.MenuItem{
    22  			checkbox,
    23  		},
    24  	}
    25  	menuWithNoCheckbox := &menu.Menu{
    26  		Items: []*menu.MenuItem{
    27  			menu.Label("No Checkbox"),
    28  		},
    29  	}
    30  	clicked := false
    31  
    32  	tests := []struct {
    33  		name                string
    34  		inputs              []*menu.Menu
    35  		startState          bool
    36  		expectedState       bool
    37  		expectedMenuUpdates map[*menu.Menu][]*menu.MenuItem
    38  		click               func(*menu.CallbackData)
    39  	}{
    40  		{
    41  			name:   "should callback menu checkbox state when clicked (false -> true)",
    42  			inputs: []*menu.Menu{menu1},
    43  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    44  				menu1: {checkbox},
    45  			},
    46  			startState:    false,
    47  			expectedState: true,
    48  		},
    49  		{
    50  			name:          "should callback multiple menus when checkbox state when clicked (false -> true)",
    51  			inputs:        []*menu.Menu{menu1, menu2},
    52  			startState:    false,
    53  			expectedState: true,
    54  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    55  				menu1: {checkbox},
    56  				menu2: {checkbox},
    57  			},
    58  		},
    59  		{
    60  			name:          "should callback only for the menus that the checkbox is in (false -> true)",
    61  			inputs:        []*menu.Menu{menu1, menuWithNoCheckbox},
    62  			startState:    false,
    63  			expectedState: true,
    64  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    65  				menu1: {checkbox},
    66  			},
    67  		},
    68  		{
    69  			name:   "should callback menu checkbox state when clicked (true->false)",
    70  			inputs: []*menu.Menu{menu1},
    71  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    72  				menu1: {checkbox},
    73  			},
    74  			startState:    true,
    75  			expectedState: false,
    76  		},
    77  		{
    78  			name:          "should callback multiple menus when checkbox state when clicked (true->false)",
    79  			inputs:        []*menu.Menu{menu1, menu2},
    80  			startState:    true,
    81  			expectedState: false,
    82  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    83  				menu1: {checkbox},
    84  				menu2: {checkbox},
    85  			},
    86  		},
    87  		{
    88  			name:          "should callback only for the menus that the checkbox is in (true->false)",
    89  			inputs:        []*menu.Menu{menu1, menuWithNoCheckbox},
    90  			startState:    true,
    91  			expectedState: false,
    92  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
    93  				menu1: {checkbox},
    94  			},
    95  		},
    96  		{
    97  			name:                "should callback no menus if checkbox not in them",
    98  			inputs:              []*menu.Menu{menuWithNoCheckbox},
    99  			startState:          false,
   100  			expectedState:       false,
   101  			expectedMenuUpdates: nil,
   102  		},
   103  		{
   104  			name:          "should call Click on the checkbox",
   105  			inputs:        []*menu.Menu{menu1, menu2},
   106  			startState:    false,
   107  			expectedState: true,
   108  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
   109  				menu1: {checkbox},
   110  				menu2: {checkbox},
   111  			},
   112  			click: func(data *menu.CallbackData) {
   113  				clicked = true
   114  			},
   115  		},
   116  	}
   117  	for _, tt := range tests {
   118  
   119  		menusUpdated := map[*menu.Menu][]*menu.MenuItem{}
   120  		clicked = false
   121  
   122  		var checkMenuItemStateInMenu func(menu *menu.Menu)
   123  
   124  		checkMenuItemStateInMenu = func(menu *menu.Menu) {
   125  			for _, item := range menusUpdated[menu] {
   126  				if item == checkbox {
   127  					require.Equal(t, tt.expectedState, item.Checked)
   128  				}
   129  				if item.SubMenu != nil {
   130  					checkMenuItemStateInMenu(item.SubMenu)
   131  				}
   132  			}
   133  		}
   134  
   135  		t.Run(tt.name, func(t *testing.T) {
   136  			m := platformMenu.NewManager()
   137  			checkbox.SetChecked(tt.startState)
   138  			checkbox.Click = tt.click
   139  			for _, thisMenu := range tt.inputs {
   140  				thisMenu := thisMenu
   141  				m.AddMenu(thisMenu, func(menuItem *menu.MenuItem) {
   142  					menusUpdated[thisMenu] = append(menusUpdated[thisMenu], menuItem)
   143  				})
   144  			}
   145  			m.ProcessClick(checkbox)
   146  
   147  			// Check the item has the correct state in all the menus
   148  			for thisMenu := range menusUpdated {
   149  				require.EqualValues(t, tt.expectedMenuUpdates[thisMenu], menusUpdated[thisMenu])
   150  			}
   151  
   152  			if tt.click != nil {
   153  				require.Equal(t, true, clicked)
   154  			}
   155  		})
   156  	}
   157  }
   158  
   159  func TestManager_ProcessClick_RadioGroups(t *testing.T) {
   160  
   161  	radio1 := menu.Radio("Radio1", false, nil, nil)
   162  	radio2 := menu.Radio("Radio2", false, nil, nil)
   163  	radio3 := menu.Radio("Radio3", false, nil, nil)
   164  	radio4 := menu.Radio("Radio4", false, nil, nil)
   165  	radio5 := menu.Radio("Radio5", false, nil, nil)
   166  	radio6 := menu.Radio("Radio6", false, nil, nil)
   167  
   168  	radioGroupOne := &menu.Menu{
   169  		Items: []*menu.MenuItem{
   170  			radio1,
   171  			radio2,
   172  			radio3,
   173  		},
   174  	}
   175  
   176  	radioGroupTwo := &menu.Menu{
   177  		Items: []*menu.MenuItem{
   178  			radio4,
   179  			radio5,
   180  			radio6,
   181  		},
   182  	}
   183  
   184  	radioGroupThree := &menu.Menu{
   185  		Items: []*menu.MenuItem{
   186  			radio1,
   187  			radio2,
   188  			radio3,
   189  		},
   190  	}
   191  
   192  	clicked := false
   193  
   194  	tests := []struct {
   195  		name                string
   196  		inputs              []*menu.Menu
   197  		startState          map[*menu.MenuItem]bool
   198  		selected            *menu.MenuItem
   199  		expectedMenuUpdates map[*menu.Menu][]*menu.MenuItem
   200  		click               func(*menu.CallbackData)
   201  		expectedState       map[*menu.MenuItem]bool
   202  	}{
   203  		{
   204  			name:   "should only set the clicked radio item",
   205  			inputs: []*menu.Menu{radioGroupOne},
   206  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
   207  				radioGroupOne: {radio1, radio2, radio3},
   208  			},
   209  			startState: map[*menu.MenuItem]bool{
   210  				radio1: true,
   211  				radio2: false,
   212  				radio3: false,
   213  			},
   214  			selected: radio2,
   215  			expectedState: map[*menu.MenuItem]bool{
   216  				radio1: false,
   217  				radio2: true,
   218  				radio3: false,
   219  			},
   220  		},
   221  		{
   222  			name:   "should not affect other radio groups or menus",
   223  			inputs: []*menu.Menu{radioGroupOne, radioGroupTwo},
   224  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
   225  				radioGroupOne: {radio1, radio2, radio3},
   226  			},
   227  			startState: map[*menu.MenuItem]bool{
   228  				radio1: true,
   229  				radio2: false,
   230  				radio3: false,
   231  				radio4: true,
   232  				radio5: false,
   233  				radio6: false,
   234  			},
   235  			selected: radio2,
   236  			expectedState: map[*menu.MenuItem]bool{
   237  				radio1: false,
   238  				radio2: true,
   239  				radio3: false,
   240  				radio4: true,
   241  				radio5: false,
   242  				radio6: false,
   243  			},
   244  		},
   245  		{
   246  			name:   "menus with the same radio group should be updated",
   247  			inputs: []*menu.Menu{radioGroupOne, radioGroupThree},
   248  			expectedMenuUpdates: map[*menu.Menu][]*menu.MenuItem{
   249  				radioGroupOne:   {radio1, radio2, radio3},
   250  				radioGroupThree: {radio1, radio2, radio3},
   251  			},
   252  			startState: map[*menu.MenuItem]bool{
   253  				radio1: true,
   254  				radio2: false,
   255  				radio3: false,
   256  			},
   257  			selected: radio2,
   258  			expectedState: map[*menu.MenuItem]bool{
   259  				radio1: false,
   260  				radio2: true,
   261  				radio3: false,
   262  			},
   263  		},
   264  	}
   265  	for _, tt := range tests {
   266  
   267  		menusUpdated := map[*menu.Menu][]*menu.MenuItem{}
   268  		clicked = false
   269  
   270  		t.Run(tt.name, func(t *testing.T) {
   271  			m := platformMenu.NewManager()
   272  
   273  			for item, value := range tt.startState {
   274  				item.SetChecked(value)
   275  			}
   276  
   277  			tt.selected.Click = tt.click
   278  			for _, thisMenu := range tt.inputs {
   279  				thisMenu := thisMenu
   280  				m.AddMenu(thisMenu, func(menuItem *menu.MenuItem) {
   281  					menusUpdated[thisMenu] = append(menusUpdated[thisMenu], menuItem)
   282  				})
   283  			}
   284  			m.ProcessClick(tt.selected)
   285  			require.Equal(t, tt.expectedMenuUpdates, menusUpdated)
   286  
   287  			// Check the items have the correct state in all the menus
   288  			for item, expectedValue := range tt.expectedState {
   289  				require.Equal(t, expectedValue, item.Checked)
   290  			}
   291  
   292  			if tt.click != nil {
   293  				require.Equal(t, true, clicked)
   294  			}
   295  		})
   296  	}
   297  }