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 }