From fe14090d66d607c38e7670af81d4df4daa8e18c9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 10 Jun 2022 11:03:12 -0400 Subject: [PATCH] Fix TopBar unit test to not require global shared state or mockFS Signed-off-by: Sebastian Malton --- .../layout/top-bar/close-window.injectable.ts | 15 ++ .../layout/top-bar/go-back.injectable.ts | 15 ++ .../layout/top-bar/go-forward.injectable.ts | 15 ++ .../top-bar/maximize-window.injectable.ts | 15 ++ .../layout/top-bar/next-enabled.injectable.ts | 18 ++ .../open-app-context-menu.injectable.ts | 14 ++ .../layout/top-bar/prev-enabled.injectable.ts | 18 ++ .../top-bar/start-state-sync.injectable.ts | 33 +++ .../layout/top-bar/state.injectable.ts | 16 ++ .../toggle-maximize-window.injectable.ts | 15 ++ .../layout/top-bar/top-bar.test.tsx | 202 +++++++----------- .../components/layout/top-bar/top-bar.tsx | 111 +++++----- 12 files changed, 304 insertions(+), 183 deletions(-) create mode 100644 src/renderer/components/layout/top-bar/close-window.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/go-back.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/go-forward.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/maximize-window.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/next-enabled.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/open-app-context-menu.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/prev-enabled.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/start-state-sync.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/state.injectable.ts create mode 100644 src/renderer/components/layout/top-bar/toggle-maximize-window.injectable.ts diff --git a/src/renderer/components/layout/top-bar/close-window.injectable.ts b/src/renderer/components/layout/top-bar/close-window.injectable.ts new file mode 100644 index 0000000000..906cf88427 --- /dev/null +++ b/src/renderer/components/layout/top-bar/close-window.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WindowAction } from "../../../../common/ipc/window"; +import { requestWindowAction } from "../../../ipc"; + +const closeWindowInjectable = getInjectable({ + id: "close-window", + instantiate: () => () => requestWindowAction(WindowAction.CLOSE), + causesSideEffects: true, +}); + +export default closeWindowInjectable; diff --git a/src/renderer/components/layout/top-bar/go-back.injectable.ts b/src/renderer/components/layout/top-bar/go-back.injectable.ts new file mode 100644 index 0000000000..88a804bd9f --- /dev/null +++ b/src/renderer/components/layout/top-bar/go-back.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WindowAction } from "../../../../common/ipc/window"; +import { requestWindowAction } from "../../../ipc"; + +const goBackInjectable = getInjectable({ + id: "go-back", + instantiate: () => () => requestWindowAction(WindowAction.GO_BACK), + causesSideEffects: true, +}); + +export default goBackInjectable; diff --git a/src/renderer/components/layout/top-bar/go-forward.injectable.ts b/src/renderer/components/layout/top-bar/go-forward.injectable.ts new file mode 100644 index 0000000000..a4a69b66f5 --- /dev/null +++ b/src/renderer/components/layout/top-bar/go-forward.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WindowAction } from "../../../../common/ipc/window"; +import { requestWindowAction } from "../../../ipc"; + +const goForwardInjectable = getInjectable({ + id: "go-forward", + instantiate: () => () => requestWindowAction(WindowAction.GO_FORWARD), + causesSideEffects: true, +}); + +export default goForwardInjectable; diff --git a/src/renderer/components/layout/top-bar/maximize-window.injectable.ts b/src/renderer/components/layout/top-bar/maximize-window.injectable.ts new file mode 100644 index 0000000000..bfcb2916c4 --- /dev/null +++ b/src/renderer/components/layout/top-bar/maximize-window.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WindowAction } from "../../../../common/ipc/window"; +import { requestWindowAction } from "../../../ipc"; + +const maximizeWindowInjectable = getInjectable({ + id: "maximize-window", + instantiate: () => () => requestWindowAction(WindowAction.MINIMIZE), + causesSideEffects: true, +}); + +export default maximizeWindowInjectable; diff --git a/src/renderer/components/layout/top-bar/next-enabled.injectable.ts b/src/renderer/components/layout/top-bar/next-enabled.injectable.ts new file mode 100644 index 0000000000..1347327ba6 --- /dev/null +++ b/src/renderer/components/layout/top-bar/next-enabled.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import topBarStateInjectable from "./state.injectable"; + +const topBarNextEnabledInjectable = getInjectable({ + id: "top-bar-next-enabled", + instantiate: (di) => { + const state = di.inject(topBarStateInjectable); + + return computed(() => state.nextEnabled); + }, +}); + +export default topBarNextEnabledInjectable; diff --git a/src/renderer/components/layout/top-bar/open-app-context-menu.injectable.ts b/src/renderer/components/layout/top-bar/open-app-context-menu.injectable.ts new file mode 100644 index 0000000000..4d2bf911d3 --- /dev/null +++ b/src/renderer/components/layout/top-bar/open-app-context-menu.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { emitOpenAppMenuAsContextMenu } from "../../../ipc"; + +const openAppContextMenuInjectable = getInjectable({ + id: "open-app-context-menu", + instantiate: () => emitOpenAppMenuAsContextMenu, + causesSideEffects: true, +}); + +export default openAppContextMenuInjectable; diff --git a/src/renderer/components/layout/top-bar/prev-enabled.injectable.ts b/src/renderer/components/layout/top-bar/prev-enabled.injectable.ts new file mode 100644 index 0000000000..fcdf318e34 --- /dev/null +++ b/src/renderer/components/layout/top-bar/prev-enabled.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import topBarStateInjectable from "./state.injectable"; + +const topBarPrevEnabledInjectable = getInjectable({ + id: "top-bar-prev-enabled", + instantiate: (di) => { + const state = di.inject(topBarStateInjectable); + + return computed(() => state.prevEnabled); + }, +}); + +export default topBarPrevEnabledInjectable; diff --git a/src/renderer/components/layout/top-bar/start-state-sync.injectable.ts b/src/renderer/components/layout/top-bar/start-state-sync.injectable.ts new file mode 100644 index 0000000000..7ca881d527 --- /dev/null +++ b/src/renderer/components/layout/top-bar/start-state-sync.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import { beforeFrameStartsInjectionToken } from "../../../before-frame-starts/before-frame-starts-injection-token"; +import ipcRendererInjectable from "../../../utils/channel/ipc-renderer.injectable"; +import topBarStateInjectable from "./state.injectable"; + +// TODO: replace with a SyncBox +const startTopbarStateSyncInjectable = getInjectable({ + id: "start-topbar-state-sync", + instantiate: (di) => { + const state = di.inject(topBarStateInjectable); + const ipcRenderer = di.inject(ipcRendererInjectable); + + return { + run: () => { + ipcRenderer.on("history:can-go-back", action((event, canGoBack: boolean) => { + state.prevEnabled = canGoBack; + })); + + ipcRenderer.on("history:can-go-forward", action((event, canGoForward: boolean) => { + state.nextEnabled = canGoForward; + })); + }, + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default startTopbarStateSyncInjectable; diff --git a/src/renderer/components/layout/top-bar/state.injectable.ts b/src/renderer/components/layout/top-bar/state.injectable.ts new file mode 100644 index 0000000000..afc826a607 --- /dev/null +++ b/src/renderer/components/layout/top-bar/state.injectable.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; + +const topBarStateInjectable = getInjectable({ + id: "top-bar-state", + instantiate: () => observable.object({ + prevEnabled: false, + nextEnabled: false, + }), +}); + +export default topBarStateInjectable; diff --git a/src/renderer/components/layout/top-bar/toggle-maximize-window.injectable.ts b/src/renderer/components/layout/top-bar/toggle-maximize-window.injectable.ts new file mode 100644 index 0000000000..3ebecc3b39 --- /dev/null +++ b/src/renderer/components/layout/top-bar/toggle-maximize-window.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WindowAction } from "../../../../common/ipc/window"; +import { requestWindowAction } from "../../../ipc"; + +const toggleMaximizeWindowInjectable = getInjectable({ + id: "toggle-maximize-window", + instantiate: () => () => requestWindowAction(WindowAction.TOGGLE_MAXIMIZE), + causesSideEffects: true, +}); + +export default toggleMaximizeWindowInjectable; diff --git a/src/renderer/components/layout/top-bar/top-bar.test.tsx b/src/renderer/components/layout/top-bar/top-bar.test.tsx index 9355da74ae..2a8ce964a1 100644 --- a/src/renderer/components/layout/top-bar/top-bar.test.tsx +++ b/src/renderer/components/layout/top-bar/top-bar.test.tsx @@ -12,163 +12,125 @@ import type { DiContainer } from "@ogre-tools/injectable"; import type { DiRender } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor"; import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable"; -import { computed } from "mobx"; -import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import mockFs from "mock-fs"; +import { computed, observable } from "mobx"; import isLinuxInjectable from "../../../../common/vars/is-linux.injectable"; import isWindowsInjectable from "../../../../common/vars/is-windows.injectable"; - -jest.mock("../../../../common/vars", () => { - const { SemVer } = require("semver"); - - return { - ...jest.requireActual<{}>("../../../../common/vars"), - appSemVer: new SemVer("1.0.0"), - }; -}); - -const goBack = jest.fn(); -const goForward = jest.fn(); - -jest.mock( - "electron", - () => ({ - ipcRenderer: { - on: jest.fn( - (channel: string, listener: (event: any, ...args: any[]) => void) => { - if (channel === "history:can-go-back") { - listener({}, true); - } - - if (channel === "history:can-go-forward") { - listener({}, true); - } - }, - ), - invoke: jest.fn( - (channel: string, action: string) => { - console.log("channel", channel, action); - - if (channel !== "window:window-action") return; - - switch(action) { - case "back": { - goBack(); - break; - } - - case "forward": { - goForward(); - break; - } - } - }, - ), - }, - }), -); - -jest.mock("../../+catalog", () => ({ - previousActiveTab: jest.fn(), -})); +import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; +import closeWindowInjectable from "./close-window.injectable"; +import goBackInjectable from "./go-back.injectable"; +import goForwardInjectable from "./go-forward.injectable"; +import maximizeWindowInjectable from "./maximize-window.injectable"; +import openAppContextMenuInjectable from "./open-app-context-menu.injectable"; +import toggleMaximizeWindowInjectable from "./toggle-maximize-window.injectable"; +import topBarStateInjectable from "./state.injectable"; describe("", () => { let di: DiContainer; let render: DiRender; + let goBack: jest.MockedFunction<() => void>; + let goForward: jest.MockedFunction<() => void>; beforeEach(() => { di = getDiForUnitTesting({ doGeneralOverrides: true }); - mockFs(); - - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); + di.override(rendererExtensionsInjectable, () => computed(() => [])); + di.override(openAppContextMenuInjectable, () => jest.fn()); + di.override(goBackInjectable, () => goBack = jest.fn()); + di.override(goForwardInjectable, () => goForward = jest.fn()); + di.override(closeWindowInjectable, () => jest.fn()); + di.override(maximizeWindowInjectable, () => jest.fn()); + di.override(toggleMaximizeWindowInjectable, () => jest.fn()); render = renderFor(di); }); - afterEach(() => { - mockFs.restore(); - }); + describe("with both previous and next history enabled", () => { + beforeEach(() => { + di.override(topBarStateInjectable, () => observable.object({ + prevEnabled: true, + nextEnabled: true, + })); + }); + it("renders w/o errors", () => { + const { container } = render(); - it("renders w/o errors", () => { - const { container } = render(); + expect(container).toBeInstanceOf(HTMLElement); + }); - expect(container).toBeInstanceOf(HTMLElement); - }); + it("renders home button", async () => { + const { findByTestId } = render(); - it("renders home button", async () => { - const { findByTestId } = render(); + expect(await findByTestId("home-button")).toBeInTheDocument(); + }); - expect(await findByTestId("home-button")).toBeInTheDocument(); - }); + it("renders history arrows", async () => { + const { findByTestId } = render(); - it("renders history arrows", async () => { - const { findByTestId } = render(); + expect(await findByTestId("history-back")).toBeInTheDocument(); + expect(await findByTestId("history-forward")).toBeInTheDocument(); + }); - expect(await findByTestId("history-back")).toBeInTheDocument(); - expect(await findByTestId("history-forward")).toBeInTheDocument(); - }); + it("enables arrow by ipc event", async () => { + const { findByTestId } = render(); - it("enables arrow by ipc event", async () => { - const { findByTestId } = render(); + expect(await findByTestId("history-back")).not.toHaveClass("disabled"); + expect(await findByTestId("history-forward")).not.toHaveClass("disabled"); + }); - expect(await findByTestId("history-back")).not.toHaveClass("disabled"); - expect(await findByTestId("history-forward")).not.toHaveClass("disabled"); - }); + it("triggers browser history back and forward", async () => { + const { findByTestId } = render(); - it("triggers browser history back and forward", async () => { - const { findByTestId } = render(); + const prevButton = await findByTestId("history-back"); + const nextButton = await findByTestId("history-forward"); - const prevButton = await findByTestId("history-back"); - const nextButton = await findByTestId("history-forward"); + fireEvent.click(prevButton); - fireEvent.click(prevButton); + expect(goBack).toBeCalled(); - expect(goBack).toBeCalled(); + fireEvent.click(nextButton); - fireEvent.click(nextButton); + expect(goForward).toBeCalled(); + }); - expect(goForward).toBeCalled(); - }); + it("renders items", async () => { + const testId = "testId"; + const text = "an item"; - it("renders items", async () => { - const testId = "testId"; - const text = "an item"; - - di.override(topBarItemsInjectable, () => computed(() => [ - { - components: { - Item: () => {text}, + di.override(topBarItemsInjectable, () => computed(() => [ + { + components: { + Item: () => {text}, + }, }, - }, - ])); + ])); - const { findByTestId } = render(); + const { findByTestId } = render(); - expect(await findByTestId(testId)).toHaveTextContent(text); - }); + expect(await findByTestId(testId)).toHaveTextContent(text); + }); - it("doesn't show windows title buttons on macos", () => { - di.override(isLinuxInjectable, () => false); - di.override(isWindowsInjectable, () => false); + it("doesn't show windows title buttons on macos", () => { + di.override(isLinuxInjectable, () => false); + di.override(isWindowsInjectable, () => false); - const { queryByTestId } = render(); + const { queryByTestId } = render(); - expect(queryByTestId("window-menu")).not.toBeInTheDocument(); - expect(queryByTestId("window-minimize")).not.toBeInTheDocument(); - expect(queryByTestId("window-maximize")).not.toBeInTheDocument(); - expect(queryByTestId("window-close")).not.toBeInTheDocument(); - }); + expect(queryByTestId("window-menu")).not.toBeInTheDocument(); + expect(queryByTestId("window-minimize")).not.toBeInTheDocument(); + expect(queryByTestId("window-maximize")).not.toBeInTheDocument(); + expect(queryByTestId("window-close")).not.toBeInTheDocument(); + }); - it("does show windows title buttons on linux", () => { - di.override(isLinuxInjectable, () => true); - di.override(isWindowsInjectable, () => false); + it("does show windows title buttons on linux", () => { + di.override(isLinuxInjectable, () => true); + di.override(isWindowsInjectable, () => false); - const { queryByTestId } = render(); + const { queryByTestId } = render(); - expect(queryByTestId("window-menu")).toBeInTheDocument(); - expect(queryByTestId("window-minimize")).toBeInTheDocument(); - expect(queryByTestId("window-maximize")).toBeInTheDocument(); - expect(queryByTestId("window-close")).toBeInTheDocument(); + expect(queryByTestId("window-menu")).toBeInTheDocument(); + expect(queryByTestId("window-minimize")).toBeInTheDocument(); + expect(queryByTestId("window-maximize")).toBeInTheDocument(); + expect(queryByTestId("window-close")).toBeInTheDocument(); + }); }); }); diff --git a/src/renderer/components/layout/top-bar/top-bar.tsx b/src/renderer/components/layout/top-bar/top-bar.tsx index adc5457747..50b3c84f1c 100644 --- a/src/renderer/components/layout/top-bar/top-bar.tsx +++ b/src/renderer/components/layout/top-bar/top-bar.tsx @@ -8,15 +8,11 @@ import React, { useEffect, useRef } from "react"; import { observer } from "mobx-react"; import type { IComputedValue } from "mobx"; import { Icon } from "../../icon"; -import { observable } from "mobx"; -import { ipcRendererOn } from "../../../../common/ipc"; import { watchHistoryState } from "../../../remote-helpers/history-updater"; import { cssNames, noop } from "../../../utils"; import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { TopBarRegistration } from "./top-bar-registration"; -import { emitOpenAppMenuAsContextMenu, requestWindowAction } from "../../../ipc"; -import { WindowAction } from "../../../../common/ipc/window"; import isLinuxInjectable from "../../../../common/vars/is-linux.injectable"; import isWindowsInjectable from "../../../../common/vars/is-windows.injectable"; import type { NavigateToCatalog } from "../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; @@ -24,6 +20,14 @@ import navigateToCatalogInjectable from "../../../../common/front-end-routing/ro import catalogRouteInjectable from "../../../../common/front-end-routing/routes/catalog/catalog-route.injectable"; import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; import { UpdateButton } from "../../update-button"; +import topBarPrevEnabledInjectable from "./prev-enabled.injectable"; +import topBarNextEnabledInjectable from "./next-enabled.injectable"; +import openAppContextMenuInjectable from "./open-app-context-menu.injectable"; +import goBackInjectable from "./go-back.injectable"; +import goForwardInjectable from "./go-forward.injectable"; +import closeWindowInjectable from "./close-window.injectable"; +import maximizeWindowInjectable from "./maximize-window.injectable"; +import toggleMaximizeWindowInjectable from "./toggle-maximize-window.injectable"; interface Dependencies { navigateToCatalog: NavigateToCatalog; @@ -31,62 +35,43 @@ interface Dependencies { items: IComputedValue; isWindows: boolean; isLinux: boolean; + prevEnabled: IComputedValue; + nextEnabled: IComputedValue; + openAppContextMenu: () => void; + goBack: () => void; + goForward: () => void; + minimizeWindow: () => void; + toggleMaximizeWindow: () => void; + closeWindow: () => void; } -const prevEnabled = observable.box(false); -const nextEnabled = observable.box(false); - -ipcRendererOn("history:can-go-back", (event, state: boolean) => { - prevEnabled.set(state); -}); - -ipcRendererOn("history:can-go-forward", (event, state: boolean) => { - nextEnabled.set(state); -}); - const NonInjectedTopBar = observer(({ items, navigateToCatalog, catalogRouteIsActive, isWindows, isLinux, + prevEnabled, + nextEnabled, + openAppContextMenu, + goBack, + goForward, + closeWindow, + minimizeWindow, + toggleMaximizeWindow, }: Dependencies) => { const elem = useRef(null); - const openAppContextMenu = () => { - emitOpenAppMenuAsContextMenu(); - }; - const goHome = () => { navigateToCatalog(); }; - const goBack = () => { - requestWindowAction(WindowAction.GO_BACK); - }; - - const goForward = () => { - requestWindowAction(WindowAction.GO_FORWARD); - }; - const windowSizeToggle = (evt: React.MouseEvent) => { if (elem.current === evt.target) { - toggleMaximize(); + toggleMaximizeWindow(); } }; - const minimizeWindow = () => { - requestWindowAction(WindowAction.MINIMIZE); - }; - - const toggleMaximize = () => { - requestWindowAction(WindowAction.TOGGLE_MAXIMIZE); - }; - - const closeWindow = () => { - requestWindowAction(WindowAction.CLOSE); - }; - useEffect(() => watchHistoryState(), []); return ( @@ -147,14 +132,14 @@ const NonInjectedTopBar = observer(({ width="10" height="1" x="1" - y="9" + y="9" />
@@ -193,23 +178,23 @@ const renderRegisteredItems = (items: TopBarRegistration[]) => ( }) ); -export const TopBar = withInjectables( - NonInjectedTopBar, - { - getProps: (di) => { - const catalogRoute = di.inject(catalogRouteInjectable); - - return { - navigateToCatalog: di.inject(navigateToCatalogInjectable), - items: di.inject(topBarItemsInjectable), - isLinux: di.inject(isLinuxInjectable), - isWindows: di.inject(isWindowsInjectable), - - catalogRouteIsActive: di.inject( - routeIsActiveInjectable, - catalogRoute, - ), - }; - }, - }, -); +export const TopBar = withInjectables(NonInjectedTopBar, { + getProps: (di) => ({ + navigateToCatalog: di.inject(navigateToCatalogInjectable), + items: di.inject(topBarItemsInjectable), + isLinux: di.inject(isLinuxInjectable), + isWindows: di.inject(isWindowsInjectable), + prevEnabled: di.inject(topBarPrevEnabledInjectable), + nextEnabled: di.inject(topBarNextEnabledInjectable), + catalogRouteIsActive: di.inject( + routeIsActiveInjectable, + di.inject(catalogRouteInjectable), + ), + openAppContextMenu: di.inject(openAppContextMenuInjectable), + goBack: di.inject(goBackInjectable), + goForward: di.inject(goForwardInjectable), + closeWindow: di.inject(closeWindowInjectable), + minimizeWindow: di.inject(maximizeWindowInjectable), + toggleMaximizeWindow: di.inject(toggleMaximizeWindowInjectable), + }), +});