test: test menu rendering accelerators (#44634)

* test: test menu rendering accelerators

* Update spec/api-menu-spec.ts

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

---------

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
Shelley Vohr 2025-05-06 20:09:35 +02:00 committed by GitHub
commit e876cecbc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 59 additions and 50 deletions

View file

@ -2,51 +2,15 @@ import { globalShortcut } from 'electron/main';
import { expect } from 'chai'; import { expect } from 'chai';
import { singleModifierCombinations, doubleModifierCombinations } from './lib/accelerator-helpers';
import { ifdescribe } from './lib/spec-helpers'; import { ifdescribe } from './lib/spec-helpers';
const modifiers = [
'CmdOrCtrl',
'Alt',
process.platform === 'darwin' ? 'Option' : null,
'AltGr',
'Shift',
'Super',
'Meta'
].filter(Boolean);
const keyCodes = [
...Array.from({ length: 10 }, (_, i) => `${i}`), // 0 to 9
...Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i)), // A to Z
...Array.from({ length: 24 }, (_, i) => `F${i + 1}`), // F1 to F24
')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ':', ';', ':', '+', '=',
'<', ',', '_', '-', '>', '.', '?', '/', '~', '`', '{', ']', '[', '|', '\\',
'}', '"', 'Plus', 'Space', 'Tab', 'Capslock', 'Numlock', 'Scrolllock',
'Backspace', 'Delete', 'Insert', 'Return', 'Enter', 'Up', 'Down', 'Left',
'Right', 'Home', 'End', 'PageUp', 'PageDown', 'Escape', 'Esc', 'PrintScreen',
'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9',
'numdec', 'numadd', 'numsub', 'nummult', 'numdiv'
];
ifdescribe(process.platform !== 'win32')('globalShortcut module', () => { ifdescribe(process.platform !== 'win32')('globalShortcut module', () => {
beforeEach(() => { beforeEach(() => {
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
}); });
it('can register and unregister single accelerators', () => { it('can register and unregister single accelerators', () => {
const singleModifierCombinations = modifiers.flatMap(
mod => keyCodes.map(key => {
return key === '+' ? `${mod}+Plus` : `${mod}+${key}`;
})
);
const doubleModifierCombinations = modifiers.flatMap(
(mod1, i) => modifiers.slice(i + 1).flatMap(
mod2 => keyCodes.map(key => {
return key === '+' ? `${mod1}+${mod2}+Plus` : `${mod1}+${mod2}+${key}`;
})
)
);
const combinations = [...singleModifierCombinations, ...doubleModifierCombinations]; const combinations = [...singleModifierCombinations, ...doubleModifierCombinations];
combinations.forEach((accelerator) => { combinations.forEach((accelerator) => {

View file

@ -7,6 +7,7 @@ import { once } from 'node:events';
import * as path from 'node:path'; import * as path from 'node:path';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { singleModifierCombinations } from './lib/accelerator-helpers';
import { ifit } from './lib/spec-helpers'; import { ifit } from './lib/spec-helpers';
import { closeWindow } from './lib/window-helpers'; import { closeWindow } from './lib/window-helpers';
import { sortMenuItems } from '../lib/browser/api/menu-utils'; import { sortMenuItems } from '../lib/browser/api/menu-utils';
@ -927,19 +928,22 @@ describe('Menu module', function () {
w.show(); w.show();
}); });
it('does not crash when rendering menu item with Super or meta accelerator', async () => { const chunkSize = 10;
const menu = Menu.buildFromTemplate([{ let chunkCount = 0;
label: 'Test Super', const totalChunks = Math.ceil(singleModifierCombinations.length / chunkSize);
accelerator: 'Super+Ctrl+T' for (let i = 0; i < singleModifierCombinations.length; i += chunkSize) {
}, { const chunk = singleModifierCombinations.slice(i, i + chunkSize);
label: 'Test Meta', it(`does not crash when rendering menu item with single accelerator combinations ${++chunkCount}/${totalChunks}`, async () => {
accelerator: 'Meta+Ctrl+T' const menu = Menu.buildFromTemplate([
}]); ...chunk.map(combination => ({
const menuWillClose = once(menu, 'menu-will-close'); label: `Test ${combination}`,
menu.popup({ window: w }); accelerator: combination
menu.closePopup(); }))
await menuWillClose; ]);
}); menu.popup({ window: w });
menu.closePopup();
});
}
}); });
describe('Menu.setApplicationMenu', () => { describe('Menu.setApplicationMenu', () => {

View file

@ -0,0 +1,41 @@
/**
* @fileoverview A set of helper functions to make it easier to work
* with accelerators across tests.
*/
const modifiers = [
'CmdOrCtrl',
'Alt',
process.platform === 'darwin' ? 'Option' : null,
'AltGr',
'Shift',
'Super',
'Meta'
].filter(Boolean);
const keyCodes = [
...Array.from({ length: 10 }, (_, i) => `${i}`), // 0 to 9
...Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i)), // A to Z
...Array.from({ length: 24 }, (_, i) => `F${i + 1}`), // F1 to F24
')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ':', ';', ':', '+', '=',
'<', ',', '_', '-', '>', '.', '?', '/', '~', '`', '{', ']', '[', '|', '\\',
'}', '"', 'Plus', 'Space', 'Tab', 'Capslock', 'Numlock', 'Scrolllock',
'Backspace', 'Delete', 'Insert', 'Return', 'Enter', 'Up', 'Down', 'Left',
'Right', 'Home', 'End', 'PageUp', 'PageDown', 'Escape', 'Esc', 'PrintScreen',
'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9',
'numdec', 'numadd', 'numsub', 'nummult', 'numdiv'
];
export const singleModifierCombinations = modifiers.flatMap(
mod => keyCodes.map(key => {
return key === '+' ? `${mod}+Plus` : `${mod}+${key}`;
})
);
export const doubleModifierCombinations = modifiers.flatMap(
(mod1, i) => modifiers.slice(i + 1).flatMap(
mod2 => keyCodes.map(key => {
return key === '+' ? `${mod1}+${mod2}+Plus` : `${mod1}+${mod2}+${key}`;
})
)
);