Repository: manusa/electronim (js)

Issue:
limited clipboard support with the mouse, the right click mouse button only shows DevTools
I expected to find there the operations cut, copy and paste

example with element-desktop, I could reproduce the same on telegram and whatsapp. with telegram sometimes does its own menu OK

![image](https://user-images.githubusercontent.com/39959198/196730707-49a4a218-3196-4bd6-b82b-409a660c72de.png)


Pull request:
✨ context-menu with clipboard functionality
Fixes #162 

Patch:
diff --git a/src/spell-check/index.js b/src/spell-check/index.js
index b525e0e..f830744 100644
--- a/src/spell-check/index.js
+++ b/src/spell-check/index.js
@@ -117,19 +117,21 @@ const menuItem = ({webContents, suggestion}) => new MenuItem({
   }
 });
 
-const contextMenuHandler = async (_event, {misspelledWord}, webContents) => {
+const contextMenuHandler = async (event, {misspelledWord}) => {
   const ret = [];
   if (misspelledWord && misspelledWord.length > 0) {
     const suggestions = await fakeRendererWorker.webContents.executeJavaScript(`getSuggestions('${misspelledWord}')`);
-    suggestions.forEach(suggestion => ret.push(menuItem({webContents, suggestion})));
+    suggestions.forEach(suggestion =>
+      ret.push(menuItem({webContents: event.sender, suggestion})));
   }
   return ret;
 };
 
-const contextMenuNativeHandler = (_event, {misspelledWord, dictionarySuggestions = []}, webContents) => {
+const contextMenuNativeHandler = (event, {misspelledWord, dictionarySuggestions = []}) => {
   const ret = [];
   if (misspelledWord && misspelledWord.length > 0) {
-    dictionarySuggestions.forEach(suggestion => ret.push(menuItem({webContents, suggestion})));
+    dictionarySuggestions.forEach(suggestion =>
+      ret.push(menuItem({webContents: event.sender, suggestion})));
   }
   return ret;
 };
diff --git a/src/tab-manager/context-menu.js b/src/tab-manager/context-menu.js
new file mode 100644
index 0000000..b5a95de
--- /dev/null
+++ b/src/tab-manager/context-menu.js
@@ -0,0 +1,87 @@
+/*
+   Copyright 2022 Marc Nuri San Felix
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ */
+const {Menu, MenuItem} = require('electron');
+const {contextMenuHandler, contextMenuNativeHandler} = require('../spell-check');
+
+const entries = (event, params) => {
+  const webContents = event.sender;
+  return [
+    [{
+      label: 'Back',
+      enabled: webContents.canGoBack(),
+      click: () => webContents.goBack()
+    }, {
+      label: 'Reload',
+      click: () => webContents.reload()
+    }], [{
+      label: 'Cut',
+      visible: params.editFlags.canCut,
+      click: () => webContents.cut()
+    }, {
+      label: 'Copy',
+      visible: params.editFlags.canCopy,
+      click: () => webContents.copy()
+    }, {
+      label: 'Copy image',
+      visible: params.mediaType === 'image',
+      click: () => webContents.copyImageAt(params.x, params.y)
+    }, {
+      label: 'Paste',
+      visible: params.editFlags.canPaste,
+      click: () => webContents.paste()
+    }], [{
+      label: 'DevTools',
+      click: () => webContents.openDevTools()
+    }]
+  ];
+};
+
+const spellCheckContextMenu = async (event, params) => {
+  const menu = new Menu();
+  let spellingSuggestions;
+  if (event.sender.session.spellcheck) {
+    spellingSuggestions = contextMenuNativeHandler(event, params);
+  } else {
+    spellingSuggestions = await contextMenuHandler(event, params);
+  }
+  if (spellingSuggestions && spellingSuggestions.length > 0) {
+    spellingSuggestions.forEach(mi => menu.append(mi));
+  }
+  return menu;
+};
+
+const regularContextMenu = (event, params) => {
+  const menu = new Menu();
+  entries(event, params).forEach(group => {
+    for (const entry of group) {
+      menu.append(new MenuItem({...entry}));
+    }
+  });
+  return menu;
+};
+
+const handleContextMenu = async (event, params) => {
+  let menu;
+  if (params.misspelledWord) {
+    menu = await spellCheckContextMenu(event, params);
+  } else {
+    menu = regularContextMenu(event, params);
+  }
+  const {x, y} = params;
+  menu.popup({x, y});
+};
+
+module.exports = {handleContextMenu};
diff --git a/src/tab-manager/index.js b/src/tab-manager/index.js
index cdc784c..287bfc7 100644
--- a/src/tab-manager/index.js
+++ b/src/tab-manager/index.js
@@ -13,12 +13,13 @@
    See the License for the specific language governing permissions and
    limitations under the License.
  */
-const {app, BrowserView, Menu, MenuItem, session} = require('electron');
+const {app, BrowserView, session} = require('electron');
 const path = require('path');
 const {APP_EVENTS} = require('../constants');
 const settings = require('../settings');
-const {contextMenuHandler, contextMenuNativeHandler, getEnabledDictionaries, getUseNativeSpellChecker} = require('../spell-check');
+const {getEnabledDictionaries, getUseNativeSpellChecker} = require('../spell-check');
 const {userAgentForView, addUserAgentInterceptor} = require('../user-agent');
+const {handleContextMenu} = require('./context-menu');
 const {handleRedirect} = require('./redirect');
 
 let activeTab = null;
@@ -55,26 +56,6 @@ const handlePageFaviconUpdated = (browserView, ipcSender, tabId) => async (_e, f
   }
 };
 
-const handleContextMenu = browserView => async (event, params) => {
-  const {webContents} = browserView;
-  const menu = new Menu();
-  let spellingSuggestions;
-  if (webContents.session.spellcheck) {
-    spellingSuggestions = contextMenuNativeHandler(event, params, webContents);
-  } else {
-    spellingSuggestions = await contextMenuHandler(event, params, webContents);
-  }
-  if (spellingSuggestions.length > 0) {
-    spellingSuggestions.forEach(mi => menu.append(mi));
-    menu.append(new MenuItem({type: 'separator'}));
-  }
-  menu.append(new MenuItem({label: 'Reload', click: () => webContents.reload()}));
-  menu.append(new MenuItem({type: 'separator'}));
-  menu.append(new MenuItem({label: 'DevTools', click: () => webContents.openDevTools()}));
-  const {x, y} = params;
-  menu.popup({x, y});
-};
-
 // Required for Service Workers -> https://github.com/electron/electron/issues/16196
 const setGlobalUserAgentFallback = userAgent => (app.userAgentFallback = userAgent);
 
@@ -123,7 +104,7 @@ const addTabs = ipcSender => tabsMetadata => {
     const handlePageFaviconUpdatedForCurrentTab = handlePageFaviconUpdated(tab, ipcSender, id);
     tab.webContents.on('page-favicon-updated', handlePageFaviconUpdatedForCurrentTab);
 
-    tab.webContents.on('context-menu', handleContextMenu(tab));
+    tab.webContents.on('context-menu', handleContextMenu);
 
     const registerIdInTab = () => tab.webContents.executeJavaScript(`window.tabId = '${id}';`);
     tab.webContents.on('dom-ready', registerIdInTab);


Test patch:
diff --git a/src/__tests__/electron.js b/src/__tests__/electron.js
index 694d5b5..08d035a 100644
--- a/src/__tests__/electron.js
+++ b/src/__tests__/electron.js
@@ -30,14 +30,41 @@ const mockBrowserWindowInstance = () => {
     setBounds: jest.fn(),
     setBrowserView: jest.fn(),
     webContents: {
+      copy: jest.fn(),
+      copyImageAt: jest.fn(),
+      cut: jest.fn(),
       destroy: jest.fn(),
+      executeJavaScript: jest.fn(async () => {}),
+      goBack: jest.fn(),
       loadURL: jest.fn(),
-      on: jest.fn(),
+      on: jest.fn((...args) => instance.on(...args)),
       openDevTools: jest.fn(),
-      send: jest.fn()
+      paste: jest.fn(),
+      reload: jest.fn(),
+      send: jest.fn(),
+      session: {},
+      userAgent: 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/1337.36 (KHTML, like Gecko) ElectronIM/13.337.0 Chrome/WillBeReplacedByLatestChromium Electron/0.0.99 Safari/537.36'
     }
   };
   return instance;
 };
 
-module.exports = {mockBrowserWindowInstance};
+const mockElectronInstance = ({...overriddenProps} = {}) => {
+  const browserViewInstance = mockBrowserWindowInstance();
+  return {
+    BrowserView: jest.fn(() => browserViewInstance),
+    browserViewInstance,
+    Menu: jest.fn(),
+    MenuItem: jest.fn(),
+    app: {},
+    session: {
+      fromPartition: jest.fn(() => ({
+        userAgentInterceptor: true
+      })),
+      defaultSession: {userAgentInterceptor: true}
+    },
+    ...overriddenProps
+  };
+};
+
+module.exports = {mockBrowserWindowInstance, mockElectronInstance};
diff --git a/src/__tests__/index.js b/src/__tests__/index.js
index 1dbe064..5252657 100644
--- a/src/__tests__/index.js
+++ b/src/__tests__/index.js
@@ -15,5 +15,6 @@
  */
 
 module.exports = {
-  mockBrowserWindowInstance: require('./electron.js').mockBrowserWindowInstance
+  mockBrowserWindowInstance: require('./electron.js').mockBrowserWindowInstance,
+  mockElectronInstance: require('./electron.js').mockElectronInstance
 };
diff --git a/src/tab-manager/__tests__/context-menu.test.js b/src/tab-manager/__tests__/context-menu.test.js
new file mode 100644
index 0000000..1649fe3
--- /dev/null
+++ b/src/tab-manager/__tests__/context-menu.test.js
@@ -0,0 +1,210 @@
+/*
+   Copyright 2022 Marc Nuri San Felix
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ */
+describe('Tab Manager context-menu test suite', () => {
+  let electron;
+  let event;
+  let listeners;
+  let mockMenu;
+  let mockMenuItem;
+  let params;
+  let tabManager;
+  beforeEach(() => {
+    jest.resetModules();
+    mockMenu = {
+      append: jest.fn(),
+      popup: jest.fn()
+    };
+    jest.mock('electron', () => require('../../__tests__').mockElectronInstance({
+      Menu: jest.fn(() => mockMenu),
+      MenuItem: jest.fn(() => mockMenuItem)
+    }));
+    jest.mock('../../settings', () => ({
+      loadSettings: jest.fn(() => ({
+        tabs: [{id: '1337', disableNotifications: false}],
+        disableNotificationsGlobally: false
+      })),
+      updateSettings: jest.fn()
+    }));
+    jest.mock('../../spell-check');
+    electron = require('electron');
+    electron.browserViewInstance.webContents.canGoBack = jest.fn(() => false);
+    listeners = electron.browserViewInstance.listeners;
+    event = new Event('');
+    event.sender = electron.browserViewInstance.webContents;
+    params = {x: 13, y: 37};
+    tabManager = require('../');
+    tabManager.addTabs({send: jest.fn()})([{id: '1337', url: 'https://localhost'}]);
+  });
+  describe('spellCheckContextMenu', () => {
+    let spellChecker;
+    beforeEach(() => {
+      spellChecker = require('../../spell-check');
+      params.misspelledWord = 'wrong-word';
+    });
+    test('should always popup the menu ??', async () => {
+      // When
+      await listeners['context-menu'](event, params);
+      // Then
+      expect(electron.Menu).toHaveBeenCalledTimes(1);
+      expect(mockMenu.popup).toHaveBeenCalledWith({x: 13, y: 37});
+    });
+    describe('with native spellcheck', () => {
+      beforeEach(() => {
+        electron.browserViewInstance.webContents.session.spellcheck = true;
+      });
+      test('Spelling suggestions, should open a Menu with all suggestions', async () => {
+        // Given
+        spellChecker.contextMenuNativeHandler.mockImplementationOnce(() => [
+          new electron.MenuItem({label: 'suggestion 1'}),
+          new electron.MenuItem({label: 'suggestion 2'})
+        ]);
+        // When
+        await listeners['context-menu'](event, params);
+        // Then
+        expect(spellChecker.contextMenuHandler).not.toHaveBeenCalled();
+        expect(electron.MenuItem).toHaveBeenCalledWith({label: 'suggestion 1'});
+        expect(electron.MenuItem).toHaveBeenCalledWith({label: 'suggestion 2'});
+      });
+    });
+    describe('with regular spellcheck', () => {
+      test('Spelling suggestions, should open a Menu with all suggestions', async () => {
+        // Given
+        spellChecker.contextMenuHandler.mockImplementationOnce(() => [
+          new electron.MenuItem({label: 'suggestion 1'})
+        ]);
+        // When
+        await listeners['context-menu'](event, params);
+        // Then
+        expect(spellChecker.contextMenuNativeHandler).not.toHaveBeenCalled();
+        expect(electron.MenuItem).toHaveBeenCalledWith({label: 'suggestion 1'});
+      });
+    });
+  });
+  describe('regularContextMenu', () => {
+    beforeEach(async () => {
+      params.editFlags = {};
+      await listeners['context-menu'](event, params);
+    });
+    test('should popup a menu at the specified location', async () => {
+      expect(electron.Menu).toHaveBeenCalledTimes(1);
+      expect(mockMenu.popup).toHaveBeenCalledWith({x: 13, y: 37});
+    });
+    test.each(['Back', 'Reload', 'Cut', 'Copy', 'Copy image', 'Paste', 'DevTools'])(
+      'adds MenuItem with label %s', async label => {
+        expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({label}));
+      });
+    describe('Back', () => {
+      test('disabled when canGoBack returns false', async () => {
+        expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+          enabled: false,
+          label: 'Back'
+        }));
+      });
+      test('enabled when canGoBack returns true', async () => {
+        electron.browserViewInstance.webContents.canGoBack = jest.fn(() => true);
+        await listeners['context-menu'](event, params);
+        expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+          enabled: false,
+          label: 'Back'
+        }));
+      });
+      test('click, should go back', async () => {
+        // When
+        electron.MenuItem.mock.calls.filter(c => c[0].label === 'Back')[0][0].click();
+        // Then
+        expect(electron.browserViewInstance.webContents.goBack).toHaveBeenCalledTimes(1);
+      });
+    });
+    test('Reload click, should trigger reload', async () => {
+      // When
+      electron.MenuItem.mock.calls.filter(c => c[0].label === 'Reload')[0][0].click();
+      // Then
+      expect(electron.browserViewInstance.webContents.reload).toHaveBeenCalledTimes(1);
+    });
+    test('DevTools click, should open devtools', async () => {
+      // When
+      electron.MenuItem.mock.calls.filter(c => c[0].label === 'DevTools')[0][0].click();
+      // Then
+      expect(electron.browserViewInstance.webContents.openDevTools).toHaveBeenCalledTimes(1);
+    });
+    describe('Clipboard related', () => {
+      describe('Cut', () => {
+        test('visible when canCut', async () => {
+          params.editFlags.canCut = true;
+          await listeners['context-menu'](event, params);
+          expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+            visible: true,
+            label: 'Cut'
+          }));
+        });
+        test('click, should cut', async () => {
+          // When
+          electron.MenuItem.mock.calls.filter(c => c[0].label === 'Cut')[0][0].click();
+          // Then
+          expect(electron.browserViewInstance.webContents.cut).toHaveBeenCalledTimes(1);
+        });
+      });
+      describe('Copy', () => {
+        test('visible when canCopy', async () => {
+          params.editFlags.canCopy = true;
+          await listeners['context-menu'](event, params);
+          expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+            visible: true,
+            label: 'Copy'
+          }));
+        });
+        test('click, should copy', async () => {
+          // When
+          electron.MenuItem.mock.calls.filter(c => c[0].label === 'Copy')[0][0].click();
+          // Then
+          expect(electron.browserViewInstance.webContents.copy).toHaveBeenCalledTimes(1);
+        });
+      });
+      describe('Copy image', () => {
+        test('visible when mediaType === image', async () => {
+          params.mediaType = 'image';
+          await listeners['context-menu'](event, params);
+          expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+            visible: true,
+            label: 'Copy image'
+          }));
+        });
+        test('click, should copy image', async () => {
+          // When
+          electron.MenuItem.mock.calls.filter(c => c[0].label === 'Copy image')[0][0].click();
+          // Then
+          expect(electron.browserViewInstance.webContents.copyImageAt).toHaveBeenCalledTimes(1);
+        });
+      });
+    });
+    describe('Paste', () => {
+      test('visible when canPaste', async () => {
+        params.editFlags.canPaste = true;
+        await listeners['context-menu'](event, params);
+        expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({
+          visible: true,
+          label: 'Paste'
+        }));
+      });
+      test('click, should paste', async () => {
+        // When
+        electron.MenuItem.mock.calls.filter(c => c[0].label === 'Paste')[0][0].click();
+        // Then
+        expect(electron.browserViewInstance.webContents.paste).toHaveBeenCalledTimes(1);
+      });
+    });
+  });
+});
diff --git a/src/tab-manager/__tests__/index.test.js b/src/tab-manager/__tests__/index.test.js
index a3275fd..302351c 100644
--- a/src/tab-manager/__tests__/index.test.js
+++ b/src/tab-manager/__tests__/index.test.js
@@ -15,37 +15,13 @@
  */
 describe('Tab Manager module test suite', () => {
   let mockBrowserView;
-  let mockMenu;
-  let mockMenuItem;
   let userAgent;
   let tabManager;
   let mockSettings;
   beforeEach(() => {
     jest.resetModules();
-    mockBrowserView = {
-      setAutoResize: jest.fn(),
-      webContents: {
-        session: {},
-        executeJavaScript: jest.fn(async () => {}),
-        on: jest.fn(),
-        loadURL: jest.fn(),
-        userAgent: 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/1337.36 (KHTML, like Gecko) ElectronIM/13.337.0 Chrome/WillBeReplacedByLatestChromium Electron/0.0.99 Safari/537.36',
-        destroy: jest.fn()
-      }
-    };
-    mockMenu = {};
-    jest.mock('electron', () => ({
-      app: {},
-      BrowserView: jest.fn(() => mockBrowserView),
-      Menu: jest.fn(() => mockMenu),
-      MenuItem: jest.fn(() => mockMenuItem),
-      session: {
-        fromPartition: jest.fn(() => ({
-          userAgentInterceptor: true
-        })),
-        defaultSession: {userAgentInterceptor: true}
-      }
-    }));
+    jest.mock('electron', () => require('../../__tests__').mockElectronInstance());
+    mockBrowserView = require('electron').browserViewInstance;
     mockSettings = {
       loadSettings: jest.fn(() => ({
         tabs: [{id: '1337', disableNotifications: false}],
@@ -54,7 +30,6 @@ describe('Tab Manager module test suite', () => {
       updateSettings: jest.fn()
     };
     jest.mock('../../settings', () => mockSettings);
-    jest.mock('../../spell-check');
     userAgent = require('../../user-agent');
     tabManager = require('../');
   });
@@ -196,24 +171,21 @@ describe('Tab Manager module test suite', () => {
       });
     });
     describe('Event listeners', () => {
-      let events;
       let mockIpcSender;
       beforeEach(() => {
-        events = {};
-        mockBrowserView.webContents.on = jest.fn((id, func) => (events[id] = func));
         mockIpcSender = {send: jest.fn()};
         tabManager.addTabs(mockIpcSender)([{id: '1337', url: 'https://localhost'}]);
       });
       test('handlePageTitleUpdated, should send setTabTitle event', () => {
         // When
-        events['page-title-updated'](new Event(''), 'Dr.');
+        mockBrowserView.listeners['page-title-updated'](new Event(''), 'Dr.');
         // Then
         expect(mockIpcSender.send).toHaveBeenCalledWith('setTabTitle', {id: '1337', title: 'Dr.'});
       });
       describe('handlePageFaviconUpdated', () => {
         test('Favicons provided, should send setTabFavicon with the last of the provided favicons', () => {
           // When
-          events['page-favicon-updated'](new Event(''), [
+          mockBrowserView.listeners['page-favicon-updated'](new Event(''), [
             'http://url-to-favicon/aitana.png',
             'http://url-to-favicon/alex.png'
           ]);
@@ -230,88 +202,12 @@ describe('Tab Manager module test suite', () => {
             return [];
           });
           // When
-          await events['page-favicon-updated'](new Event(''));
+          await mockBrowserView.listeners['page-favicon-updated'](new Event(''));
           // Then
           expect(mockIpcSender.send)
             .toHaveBeenCalledWith('setTabFavicon', {id: '1337', favicon: 'http://url-to-favicon/julia.png'});
         });
       });
-      describe('handleContextMenu', () => {
-        let electron;
-        let spellChecker;
-        beforeEach(() => {
-          electron = require('electron');
-          spellChecker = require('../../spell-check');
-          mockMenu.append = jest.fn();
-          mockMenu.popup = jest.fn();
-        });
-        test('should open a Menu with DevTools entry', async () => {
-          // Given
-          spellChecker.contextMenuHandler.mockImplementationOnce(() => []);
-          // When
-          await events['context-menu'](new Event(''), {x: 13, y: 37});
-          // Then
-          expect(electron.Menu).toHaveBeenCalledTimes(1);
-          expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({label: 'DevTools'}));
-        });
-        test('should open a Menu with Reload entry', async () => {
-          // Given
-          spellChecker.contextMenuHandler.mockImplementationOnce(() => []);
-          // When
-          await events['context-menu'](new Event(''), {x: 13, y: 37});
-          // Then
-          expect(electron.Menu).toHaveBeenCalledTimes(1);
-          expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({label: 'Reload'}));
-        });
-        test('should open a Menu at the specified location', async () => {
-          // Given
-          spellChecker.contextMenuHandler.mockImplementationOnce(() => []);
-          // When
-          await events['context-menu'](new Event(''), {x: 13, y: 37});
-          // Then
-          expect(mockMenu.popup).toHaveBeenCalledTimes(1);
-          expect(mockMenu.popup).toHaveBeenCalledWith({x: 13, y: 37});
-        });
-        describe('with native spellcheck disabled', () => {
-          beforeEach(() => {
-            mockBrowserView.webContents.session.spellcheck = false;
-          });
-          test('Spelling suggestions, should open a Menu with all suggestions, a separator and DevTools entry', async () => {
-            // Given
-            spellChecker.contextMenuHandler.mockImplementationOnce(() => [
-              new electron.MenuItem({label: 'suggestion 1'}),
-              new electron.MenuItem({label: 'suggestion 2'})
-            ]);
-            // When
-            await events['context-menu'](new Event(''), {x: 13, y: 37});
-            // Then
-            expect(electron.Menu).toHaveBeenCalledTimes(1);
-            expect(electron.MenuItem).toHaveBeenCalledWith({label: 'suggestion 1'});
-            expect(electron.MenuItem).toHaveBeenCalledWith({label: 'suggestion 2'});
-            expect(electron.MenuItem).toHaveBeenCalledWith({type: 'separator'});
-            expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({label: 'DevTools'}));
-          });
-        });
-        describe('with native spellcheck enabled', () => {
-          beforeEach(() => {
-            mockBrowserView.webContents.session.spellcheck = true;
-          });
-          test('Spelling suggestions, should open a Menu with all suggestions, a separator and DevTools entry', async () => {
-            // Given
-            spellChecker.contextMenuNativeHandler.mockImplementationOnce(() => [
-              new electron.MenuItem({label: 'native suggestion 1'})
-            ]);
-            // When
-            await events['context-menu'](new Event(''), {x: 13, y: 37});
-            // Then
-            expect(electron.Menu).toHaveBeenCalledTimes(1);
-            expect(electron.MenuItem).toHaveBeenCalledWith({label: 'native suggestion 1'});
-            expect(electron.MenuItem).toHaveBeenCalledWith({type: 'separator'});
-            expect(electron.MenuItem).toHaveBeenCalledWith({type: 'separator'});
-            expect(electron.MenuItem).toHaveBeenCalledWith(expect.objectContaining({label: 'DevTools'}));
-          });
-        });
-      });
     });
   });
   describe('activeTab', () => {