From a1035a9cba656b7223f02ad096ba1bd84adeac69 Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Wed, 29 Dec 2021 15:19:53 +0200 Subject: [PATCH] Add tests to tray menu items. Fix autorun infinite loop. Signed-off-by: Juho Heikka --- src/main/index.ts | 6 +- ...table.ts => tray-menu-items.injectable.ts} | 0 src/main/tray/tray-menu-items.test.ts | 132 ++++++++++++++++++ src/main/tray/tray.ts | 11 +- 4 files changed, 140 insertions(+), 9 deletions(-) rename src/main/tray/{tray-items.injectable.ts => tray-menu-items.injectable.ts} (100%) create mode 100644 src/main/tray/tray-menu-items.test.ts diff --git a/src/main/index.ts b/src/main/index.ts index 26e65a8614..f40b92aa21 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -68,7 +68,7 @@ import { getDi } from "./getDi"; import electronMenuItemsInjectable from "./menu/electron-menu-items.injectable"; import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable"; import lensProtocolRouterMainInjectable from "./protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable"; -import trayItemsInjectable from "./tray/tray-items.injectable"; +import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable"; const di = getDi(); @@ -105,7 +105,7 @@ mangleProxyEnv(); logger.debug("[APP-MAIN] initializing ipc main handlers"); const menuItems = di.inject(electronMenuItemsInjectable); -const trayItems = di.inject(trayItemsInjectable); +const trayMenuItems = di.inject(trayMenuItemsInjectable); initializers.initIpcMainHandlers(menuItems); @@ -246,7 +246,7 @@ app.on("ready", async () => { onQuitCleanup.push( initMenu(windowManager, menuItems), - initTray(windowManager, trayItems), + initTray(windowManager, trayMenuItems), () => ShellSession.cleanup(), ); diff --git a/src/main/tray/tray-items.injectable.ts b/src/main/tray/tray-menu-items.injectable.ts similarity index 100% rename from src/main/tray/tray-items.injectable.ts rename to src/main/tray/tray-menu-items.injectable.ts diff --git a/src/main/tray/tray-menu-items.test.ts b/src/main/tray/tray-menu-items.test.ts new file mode 100644 index 0000000000..69a5941058 --- /dev/null +++ b/src/main/tray/tray-menu-items.test.ts @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; +import { LensMainExtension } from "../../extensions/lens-main-extension"; +import trayItemsInjectable from "./tray-menu-items.injectable"; +import type { IComputedValue } from "mobx"; +import { computed, ObservableMap, runInAction } from "mobx"; +import type { MenuItemConstructorOptions } from "electron"; +import { getDiForUnitTesting } from "../getDiForUnitTesting"; +import mainExtensionsInjectable from "../../extensions/main-extensions.injectable"; + +describe("tray-menu-items", () => { + let di: ConfigurableDependencyInjectionContainer; + let trayMenuItems: IComputedValue; + let extensionsStub: ObservableMap; + + beforeEach(() => { + di = getDiForUnitTesting(); + + extensionsStub = new ObservableMap(); + + di.override( + mainExtensionsInjectable, + () => computed(() => [...extensionsStub.values()]), + ); + + trayMenuItems = di.inject(trayItemsInjectable); + }); + + it("does not have any items yet", () => { + expect(trayMenuItems.get()).toHaveLength(0); + }); + + describe("when extension is enabled", () => { + beforeEach(() => { + const someExtension = new SomeTestExtension({ + id: "some-extension-id", + trayMenus: [{ label: "tray-menu-from-some-extension" }], + }); + + runInAction(() => { + extensionsStub.set("some-extension-id", someExtension); + }); + }); + + it("has tray menu items", () => { + expect(trayMenuItems.get()).toEqual([ + { + label: "tray-menu-from-some-extension", + }, + ]); + }); + + it("when disabling extension, does not have tray menu items", () => { + extensionsStub.delete("some-extension-id"); + + expect(trayMenuItems.get()).toHaveLength(0); + }); + + describe("when other extension is enabled", () => { + beforeEach(() => { + const someOtherExtension = new SomeTestExtension({ + id: "some-extension-id", + trayMenus: [{ label: "some-label-from-second-extension" }], + }); + + extensionsStub.set("some-other-extension-id", someOtherExtension); + }); + + it("has tray menu items for both extensions", () => { + expect(trayMenuItems.get()).toEqual([ + { + label: "tray-menu-from-some-extension", + }, + + { + label: "some-label-from-second-extension", + }, + ]); + }); + + it("when extension is disabled, still returns tray menu items for extensions that are enabled", () => { + runInAction(() => { + extensionsStub.delete("some-other-extension-id"); + }); + + expect(trayMenuItems.get()).toEqual([ + { + label: "tray-menu-from-some-extension", + }, + ]); + }); + }); + }); +}); + +class SomeTestExtension extends LensMainExtension { + constructor({ id, trayMenus }: { + id: string; + trayMenus: MenuItemConstructorOptions[]; + }) { + super({ + id, + absolutePath: "irrelevant", + isBundled: false, + isCompatible: false, + isEnabled: false, + manifest: { name: id, version: "some-version" }, + manifestPath: "irrelevant", + }); + + this.trayMenus = trayMenus; + } +} diff --git a/src/main/tray/tray.ts b/src/main/tray/tray.ts index 50de3a9997..58a4ccfaa8 100644 --- a/src/main/tray/tray.ts +++ b/src/main/tray/tray.ts @@ -30,6 +30,7 @@ import logger from "../logger"; import { isDevelopment, isWindows, productName } from "../../common/vars"; import { exitApp } from "../exit-app"; import { preferencesURL } from "../../common/routes"; +import { toJS } from "../../common/utils"; import type { MenuItemConstructorOptions } from "electron/main"; const TRAY_LOG_PREFIX = "[TRAY]"; @@ -47,7 +48,7 @@ export function getTrayIcon(): string { export function initTray( windowManager: WindowManager, - trayItems: IComputedValue, + trayMenuItems: IComputedValue, ) { const icon = getTrayIcon(); @@ -66,7 +67,7 @@ export function initTray( const disposers = [ autorun(() => { try { - const menu = createTrayMenu(windowManager, trayItems.get()); + const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get())); tray.setContextMenu(menu); } catch (error) { @@ -86,7 +87,7 @@ function createTrayMenu( windowManager: WindowManager, extensionTrayItems: MenuItemConstructorOptions[], ): Menu { - const template: Electron.MenuItemConstructorOptions[] = [ + let template: Electron.MenuItemConstructorOptions[] = [ { label: `Open ${productName}`, click() { @@ -115,9 +116,7 @@ function createTrayMenu( }); } - for (const item of extensionTrayItems) { - template.push(item); - } + template = template.concat(extensionTrayItems); return Menu.buildFromTemplate(template.concat([ {