|
1 | | -import { app, BrowserWindow, Tray } from 'electron'; |
| 1 | +import { app, BrowserWindow, globalShortcut, Tray } from 'electron'; |
2 | 2 | import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest'; |
3 | 3 |
|
4 | 4 | import { Menubar } from './Menubar'; |
@@ -177,6 +177,138 @@ describe('Menubar hideOnClose option', () => { |
177 | 177 | }); |
178 | 178 | }); |
179 | 179 |
|
| 180 | +describe('Menubar global shortcut', () => { |
| 181 | + beforeEach(() => { |
| 182 | + vi.clearAllMocks(); |
| 183 | + }); |
| 184 | + |
| 185 | + it('registers the configured accelerator on ready', () => { |
| 186 | + const mb = new Menubar(app, { |
| 187 | + preloadWindow: true, |
| 188 | + globalShortcut: 'CmdOrCtrl+Shift+G', |
| 189 | + }); |
| 190 | + return new Promise<void>((resolve) => { |
| 191 | + mb.on('ready', () => { |
| 192 | + expect(globalShortcut.register).toHaveBeenCalledWith( |
| 193 | + 'CmdOrCtrl+Shift+G', |
| 194 | + expect.any(Function), |
| 195 | + ); |
| 196 | + resolve(); |
| 197 | + }); |
| 198 | + }); |
| 199 | + }); |
| 200 | + |
| 201 | + it('unregisters the previous accelerator when replacing it', () => { |
| 202 | + const mb = new Menubar(app, { |
| 203 | + preloadWindow: true, |
| 204 | + globalShortcut: 'CmdOrCtrl+Shift+G', |
| 205 | + }); |
| 206 | + return new Promise<void>((resolve) => { |
| 207 | + mb.on('ready', () => { |
| 208 | + mb.setGlobalShortcut('Alt+Space'); |
| 209 | + expect(globalShortcut.unregister).toHaveBeenCalledWith( |
| 210 | + 'CmdOrCtrl+Shift+G', |
| 211 | + ); |
| 212 | + expect(globalShortcut.register).toHaveBeenLastCalledWith( |
| 213 | + 'Alt+Space', |
| 214 | + expect.any(Function), |
| 215 | + ); |
| 216 | + resolve(); |
| 217 | + }); |
| 218 | + }); |
| 219 | + }); |
| 220 | + |
| 221 | + it('clears the accelerator when called with undefined', () => { |
| 222 | + const mb = new Menubar(app, { |
| 223 | + preloadWindow: true, |
| 224 | + globalShortcut: 'CmdOrCtrl+Shift+G', |
| 225 | + }); |
| 226 | + return new Promise<void>((resolve) => { |
| 227 | + mb.on('ready', () => { |
| 228 | + (globalShortcut.register as Mock).mockClear(); |
| 229 | + mb.setGlobalShortcut(undefined); |
| 230 | + expect(globalShortcut.unregister).toHaveBeenCalledWith( |
| 231 | + 'CmdOrCtrl+Shift+G', |
| 232 | + ); |
| 233 | + expect(globalShortcut.register).not.toHaveBeenCalled(); |
| 234 | + resolve(); |
| 235 | + }); |
| 236 | + }); |
| 237 | + }); |
| 238 | + |
| 239 | + it('does not retain a failed registration', () => { |
| 240 | + (globalShortcut.register as Mock).mockReturnValueOnce(false); |
| 241 | + const mb = new Menubar(app, { preloadWindow: true }); |
| 242 | + return new Promise<void>((resolve) => { |
| 243 | + mb.on('ready', () => { |
| 244 | + const ok = mb.setGlobalShortcut('CmdOrCtrl+Shift+G'); |
| 245 | + expect(ok).toBe(false); |
| 246 | + (globalShortcut.unregister as Mock).mockClear(); |
| 247 | + mb.destroy(); |
| 248 | + expect(globalShortcut.unregister).not.toHaveBeenCalled(); |
| 249 | + resolve(); |
| 250 | + }); |
| 251 | + }); |
| 252 | + }); |
| 253 | + |
| 254 | + it('unregisters on destroy()', () => { |
| 255 | + const mb = new Menubar(app, { |
| 256 | + preloadWindow: true, |
| 257 | + globalShortcut: 'CmdOrCtrl+Shift+G', |
| 258 | + }); |
| 259 | + return new Promise<void>((resolve) => { |
| 260 | + mb.on('ready', () => { |
| 261 | + mb.destroy(); |
| 262 | + expect(globalShortcut.unregister).toHaveBeenCalledWith( |
| 263 | + 'CmdOrCtrl+Shift+G', |
| 264 | + ); |
| 265 | + resolve(); |
| 266 | + }); |
| 267 | + }); |
| 268 | + }); |
| 269 | +}); |
| 270 | + |
| 271 | +describe('Menubar toggleWindow and recenterOnTray', () => { |
| 272 | + beforeEach(() => { |
| 273 | + vi.clearAllMocks(); |
| 274 | + }); |
| 275 | + |
| 276 | + it('toggleWindow shows when hidden and hides when visible', () => { |
| 277 | + const mb = new Menubar(app, { preloadWindow: true }); |
| 278 | + return new Promise<void>((resolve) => { |
| 279 | + mb.on('after-create-window', async () => { |
| 280 | + await mb.toggleWindow(); |
| 281 | + expect(mb.window!.show).toHaveBeenCalledTimes(1); |
| 282 | + await mb.toggleWindow(); |
| 283 | + expect(mb.window!.hide).toHaveBeenCalledTimes(1); |
| 284 | + resolve(); |
| 285 | + }); |
| 286 | + }); |
| 287 | + }); |
| 288 | + |
| 289 | + it('recenterOnTray sets a new position from tray bounds', () => { |
| 290 | + const mb = new Menubar(app, { preloadWindow: true }); |
| 291 | + return new Promise<void>((resolve) => { |
| 292 | + mb.on('after-create-window', () => { |
| 293 | + (mb.window!.setPosition as Mock).mockClear(); |
| 294 | + mb.recenterOnTray(); |
| 295 | + expect(mb.window!.setPosition).toHaveBeenCalled(); |
| 296 | + resolve(); |
| 297 | + }); |
| 298 | + }); |
| 299 | + }); |
| 300 | + |
| 301 | + it('recenterOnTray is a no-op without a window', () => { |
| 302 | + const mb = new Menubar(app, {}); |
| 303 | + return new Promise<void>((resolve) => { |
| 304 | + mb.on('ready', () => { |
| 305 | + expect(() => mb.recenterOnTray()).not.toThrow(); |
| 306 | + resolve(); |
| 307 | + }); |
| 308 | + }); |
| 309 | + }); |
| 310 | +}); |
| 311 | + |
180 | 312 | describe('Menubar contextMenu option', () => { |
181 | 313 | const originalPlatform = process.platform; |
182 | 314 | const fakeMenu = { __menu: true } as unknown as Electron.Menu; |
|
0 commit comments