Repository: electron/electron
Pull request: test: test menu rendering accelerators

Backport of #44634

See that PR for details.


Notes: none

Changed files:
filename: spec/api-global-shortcut-spec.ts
patch: @@ -2,51 +2,15 @@ import { globalShortcut } from 'electron/main';
 
 import { expect } from 'chai';
 
+import { singleModifierCombinations, doubleModifierCombinations } from './lib/accelerator-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', () => {
   beforeEach(() => {
     globalShortcut.unregisterAll();
   });
 
   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];
 
     combinations.forEach((accelerator) => {

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

filename: spec/lib/accelerator-helpers.ts
patch: @@ -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}`;
+    })
+  )
+);