diff --git a/src/behaviours/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/src/behaviours/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap index 3a3d32317c..91606c6ec9 100644 --- a/src/behaviours/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ b/src/behaviours/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap @@ -52,9 +52,21 @@ exports[`extension special characters in page registrations renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
diff --git a/src/behaviours/__snapshots__/navigate-to-extension-page.test.tsx.snap b/src/behaviours/__snapshots__/navigate-to-extension-page.test.tsx.snap index c99c770ef1..9ed5890940 100644 --- a/src/behaviours/__snapshots__/navigate-to-extension-page.test.tsx.snap +++ b/src/behaviours/__snapshots__/navigate-to-extension-page.test.tsx.snap @@ -52,9 +52,21 @@ exports[`navigate to extension page renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -181,9 +205,21 @@ exports[`navigate to extension page when extension navigates to route with param >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -263,9 +299,21 @@ exports[`navigate to extension page when extension navigates to route without pa >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -345,9 +393,21 @@ exports[`navigate to extension page when extension navigates to route without pa >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
diff --git a/src/behaviours/__snapshots__/navigating-between-routes.test.tsx.snap b/src/behaviours/__snapshots__/navigating-between-routes.test.tsx.snap index 921adfec18..bdc7fa31c1 100644 --- a/src/behaviours/__snapshots__/navigating-between-routes.test.tsx.snap +++ b/src/behaviours/__snapshots__/navigating-between-routes.test.tsx.snap @@ -52,9 +52,21 @@ exports[`navigating between routes given route with optional path parameters whe >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -121,9 +133,21 @@ exports[`navigating between routes given route without path parameters when navi
   >
     
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
diff --git a/src/behaviours/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap b/src/behaviours/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap index 1da17bdc47..09f28e90a3 100644 --- a/src/behaviours/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap +++ b/src/behaviours/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap @@ -52,9 +52,21 @@ exports[`add-cluster - navigation using application menu renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+
+ Checking for updates... +
+
+
-
-
- - - info_outline - - -
-
- Checking for updates... -
-
- - - close - - -
-
-
+ />
`; @@ -224,95 +210,29 @@ exports[`installing update using tray when started when user checks for updates >
+ data-testid="status-bar-left" + > +
+
+ Downloading version some-version... +
+
+
+
-
-
- - - info_outline - - -
-
- Checking for updates... -
-
- - - close - - -
-
-
-
- - - info_outline - - -
-
- Download for version some-version started... -
-
- - - close - - -
-
-
+ />
`; @@ -388,135 +308,26 @@ exports[`installing update using tray when started when user checks for updates >
+ data-testid="status-bar-left" + > +
+
+ Download of update failed +
+
+
-
-
- - - info_outline - - -
-
- Checking for updates... -
-
- - - close - - -
-
-
-
- - - info_outline - - -
-
- Download for version some-version started... -
-
- - - close - - -
-
-
-
- - - info_outline - - -
-
- Download of update failed -
-
- - - close - - -
-
-
+ />
`; @@ -592,95 +403,26 @@ exports[`installing update using tray when started when user checks for updates >
+ data-testid="status-bar-left" + > +
+
+ some-version is available +
+
+
-
-
- - - info_outline - - -
-
- Checking for updates... -
-
- - - close - - -
-
-
-
- - - info_outline - - -
-
- Download for version some-version started... -
-
- - - close - - -
-
-
+ />
`; @@ -738,95 +480,26 @@ exports[`installing update using tray when started when user checks for updates >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
-
-
- - - info_outline - - -
-
- Checking for updates... -
-
- - - close - - -
-
-
-
- - - info_outline - - -
-
- No new updates available -
-
- - - close - - -
-
-
+ />
`; diff --git a/src/behaviours/application-update/__snapshots__/installing-update.test.ts.snap b/src/behaviours/application-update/__snapshots__/installing-update.test.ts.snap index d865b6ce06..e9b4ce2a94 100644 --- a/src/behaviours/application-update/__snapshots__/installing-update.test.ts.snap +++ b/src/behaviours/application-update/__snapshots__/installing-update.test.ts.snap @@ -53,9 +53,21 @@ exports[`installing update when started renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+
+ Checking for updates... +
+
+
+ data-testid="status-bar-left" + > +
+
+ Downloading version some-version... +
+
+
+
+ data-testid="status-bar-left" + > +
+
+ Download of update failed +
+
+
+ data-testid="status-bar-left" + > +
+
+ some-version is available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
{ @@ -24,19 +23,17 @@ describe("installing update", () => { let checkForPlatformUpdatesMock: AsyncFnMock; let downloadPlatformUpdateMock: AsyncFnMock; let setUpdateOnQuitMock: jest.Mock; - let showInfoNotificationMock: jest.Mock; beforeEach(() => { + jest.useFakeTimers(); + applicationBuilder = getApplicationBuilder(); - applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => { + applicationBuilder.beforeApplicationStart(({ mainDi }) => { quitAndInstallUpdateMock = jest.fn(); checkForPlatformUpdatesMock = asyncFn(); downloadPlatformUpdateMock = asyncFn(); setUpdateOnQuitMock = jest.fn(); - showInfoNotificationMock = jest.fn(() => () => {}); - - rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock); mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock); @@ -89,7 +86,7 @@ describe("installing update", () => { }); it("notifies the user that checking for updates is happening", () => { - expect(showInfoNotificationMock).toHaveBeenCalledWith("Checking for updates..."); + expect(rendered.getByTestId("app-update-checking")).toBeInTheDocument(); }); it("renders", () => { @@ -98,8 +95,6 @@ describe("installing update", () => { describe("when no new update is discovered", () => { beforeEach(async () => { - showInfoNotificationMock.mockClear(); - await checkForPlatformUpdatesMock.resolve({ updateWasDiscovered: false, }); @@ -108,7 +103,7 @@ describe("installing update", () => { }); it("notifies the user", () => { - expect(showInfoNotificationMock).toHaveBeenCalledWith("No new updates available"); + expect(rendered.getByTestId("app-update-not-available")).toBeInTheDocument(); }); it("does not start downloading update", () => { @@ -118,6 +113,12 @@ describe("installing update", () => { it("renders", () => { expect(rendered.baseElement).toMatchSnapshot(); }); + + it.skip("when 5 seconds elapses, clears the notification to the user", () => { + jest.advanceTimersByTime(6000); + + expect(rendered.getByTestId("app-update-idle")).toBeInTheDocument(); + }); }); describe("when new update is discovered", () => { @@ -135,7 +136,7 @@ describe("installing update", () => { }); it("notifies the user that download is happening", () => { - expect(showInfoNotificationMock).toHaveBeenCalledWith("Download for version some-version started..."); + expect(rendered.getByTestId("app-update-downloading")).toBeInTheDocument(); }); it("renders", () => { @@ -152,7 +153,7 @@ describe("installing update", () => { }); it("notifies the user about failed download", () => { - expect(showInfoNotificationMock).toHaveBeenCalledWith("Download of update failed"); + expect(rendered.getByTestId("app-update-download-failed")).toBeInTheDocument(); }); it("renders", () => { @@ -169,6 +170,10 @@ describe("installing update", () => { expect(quitAndInstallUpdateMock).not.toHaveBeenCalled(); }); + it("notifies the user about successful download", () => { + expect(rendered.getByTestId("app-update-available")).toBeInTheDocument(); + }); + it("renders", () => { expect(rendered.baseElement).toMatchSnapshot(); }); diff --git a/src/behaviours/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap b/src/behaviours/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap index b8c6804f4d..a549889d94 100644 --- a/src/behaviours/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/src/behaviours/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -52,9 +52,21 @@ exports[`extensions - navigation using application menu renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -914,9 +950,21 @@ exports[`preferences - closing-preferences given accessing preferences directly >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
@@ -980,9 +1028,21 @@ exports[`preferences - closing-preferences given already in a page and then navi >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+
+
+
+ + + home + + + + + arrow_back + + + + + arrow_forward + + +
+
+
+
+
+
+
+ No new updates available +
+
+
+
+ left1 +
+
+
+
+ left2 +
+
+
+
+
+
+ right3 +
+
+
+ + right2 + +
+
+ + right1 + +
+
+
+
+
+ +`; diff --git a/src/behaviours/status-bar/status-bar-items-originating-from-extensions.test.tsx b/src/behaviours/status-bar/status-bar-items-originating-from-extensions.test.tsx new file mode 100644 index 0000000000..998a8d7281 --- /dev/null +++ b/src/behaviours/status-bar/status-bar-items-originating-from-extensions.test.tsx @@ -0,0 +1,150 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import React from "react"; +import type { GetRendererExtensionFake, TestExtension } from "../../renderer/components/test-utils/get-renderer-extension-fake"; +import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake"; +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import getRandomIdInjectable from "../../common/utils/get-random-id.injectable"; + +describe("status-bar-items-originating-from-extensions", () => { + let applicationBuilder: ApplicationBuilder; + + beforeEach(() => { + applicationBuilder = getApplicationBuilder(); + + applicationBuilder.beforeApplicationStart(({ rendererDi }) => { + rendererDi.unoverride(getRandomIdInjectable); + rendererDi.permitSideEffects(getRandomIdInjectable); + }); + }); + + describe("when application starts", () => { + let rendered: RenderResult; + let getRendererExtensionFake: GetRendererExtensionFake; + + beforeEach(async () => { + rendered = await applicationBuilder.render(); + getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder); + }); + + it("when multiple extensions with status bar items are loaded, shows items in correct order", () => { + const testExtension1 = getRendererExtensionFake({ + id: "some-id", + name: "some-name", + + statusBarItems: [ + { + components: { + Item: () =>
extension1
, + position: "right", + }, + }, + ], + }); + + const testExtension2 = getRendererExtensionFake({ + id: "some-other-id", + name: "some-other-name", + statusBarItems: [ + { + components: { + Item: () =>
extension2
, + position: "right", + }, + }, + ], + }); + + applicationBuilder.extensions.renderer.enable(testExtension1, testExtension2); + + const rightSide = rendered.getByTestId("status-bar-right"); + + const actual = getTestStatusBarTexts(rightSide, [ + "extension1", + "extension2", + ]); + + expect(actual).toEqual(["extension2", "extension1"]); + }); + + describe("when extension with status bar items is loaded", () => { + let testExtension: TestExtension; + + beforeEach(() => { + testExtension = getRendererExtensionFake({ + id: "some-id", + name: "some-name", + statusBarItems: [ + { + item: () => right1, + }, + { + item: () => right2, + }, + { + components: { + Item: () =>
right3
, + position: "right", + }, + }, + { + components: { + Item: () =>
left1
, + position: "left", + }, + }, + { + components: { + Item: () =>
left2
, + position: "left", + }, + }, + ], + }); + + applicationBuilder.extensions.renderer.enable(testExtension); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("shows right side status bar items in the correct order", () => { + const rightSide = rendered.getByTestId("status-bar-right"); + + const actual = getTestStatusBarTexts(rightSide, [ + "right1", + "right2", + "right3", + ]); + + expect(actual).toEqual(["right3", "right2", "right1"]); + }); + + it("shows left side status bar items in the correct order", () => { + const leftSide = rendered.getByTestId("status-bar-left"); + + const actual = getTestStatusBarTexts(leftSide, ["left2", "left1"]); + + expect(actual).toEqual(["left1", "left2"]); + }); + + it("when the extension is removed, shows there are no extension status bar items", () => { + applicationBuilder.extensions.renderer.disable(testExtension); + + const actual = rendered.queryAllByTestId("some-testId"); + + expect(actual).toHaveLength(0); + }); + }); + }); +}); + +const getTestStatusBarTexts = (actual: HTMLElement, expectedTexts: string[]) => + Array.from(actual.children) + .map((elem) => elem.textContent) + .filter((elem) => elem && expectedTexts.includes(elem)); diff --git a/src/behaviours/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap b/src/behaviours/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap index 47b75b4303..5f76677010 100644 --- a/src/behaviours/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/src/behaviours/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -52,9 +52,21 @@ exports[`welcome - navigation using application menu renders 1`] = ` >
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
+ data-testid="status-bar-left" + > +
+
+ No new updates available +
+
+
; - -const applicationUpdateStatusChannelInjectable = getInjectable({ - id: "application-update-status-channel", - - instantiate: (): ApplicationUpdateStatusChannel => ({ - id: "application-update-status-channel", - }), - - injectionToken: messageChannelInjectionToken, -}); - -export default applicationUpdateStatusChannelInjectable; diff --git a/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts b/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts index 60557de211..29e89d3ea3 100644 --- a/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts +++ b/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts @@ -5,21 +5,21 @@ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable"; import type { UpdateChannel } from "../update-channels"; +import type { SyncBox } from "../../utils/sync-box/sync-box-injection-token"; import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token"; +export type DiscoveredUpdateVersion = SyncBox<{ version: string; updateChannel: UpdateChannel } | null>; + const discoveredUpdateVersionInjectable = getInjectable({ id: "discovered-update-version", instantiate: (di) => { const createSyncBox = di.inject(createSyncBoxInjectable); - return createSyncBox< - | { version: string; updateChannel: UpdateChannel } - | null - >( - "discovered-update-version", - null, - ); + return createSyncBox( + "discovered-update-version", + null, + ) as DiscoveredUpdateVersion; }, injectionToken: syncBoxInjectionToken, diff --git a/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts b/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts index 26ecd1d618..cf7b0cf7e0 100644 --- a/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts +++ b/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts @@ -4,12 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable"; +import type { SyncBox } from "../../utils/sync-box/sync-box-injection-token"; import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token"; export interface ProgressOfDownload { percentage: number; + failed?: string; } +export type ProgressOfUpdateDownload = SyncBox; + const progressOfUpdateDownloadInjectable = getInjectable({ id: "progress-of-update-download-state", diff --git a/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts b/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts index e1701d7952..0eae5779ed 100644 --- a/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts +++ b/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts @@ -4,8 +4,11 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable"; +import type { SyncBox } from "../../utils/sync-box/sync-box-injection-token"; import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token"; +export type UpdateIsBeingDownloaded = SyncBox; + const updateIsBeingDownloadedInjectable = getInjectable({ id: "update-is-being-downloaded", diff --git a/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts b/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts index 21f1c14bec..97ff920af4 100644 --- a/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts +++ b/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts @@ -4,8 +4,11 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable"; +import type { SyncBox } from "../../utils/sync-box/sync-box-injection-token"; import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token"; +export type UpdatesAreBeingDiscovered = SyncBox; + const updatesAreBeingDiscoveredInjectable = getInjectable({ id: "updates-are-being-discovered", diff --git a/src/main/application-update/check-for-updates/broadcast-change-in-updating-status.injectable.ts b/src/main/application-update/check-for-updates/broadcast-change-in-updating-status.injectable.ts deleted file mode 100644 index 7e9257e966..0000000000 --- a/src/main/application-update/check-for-updates/broadcast-change-in-updating-status.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 type { ApplicationUpdateStatusChannelMessage } from "../../../common/application-update/application-update-status-channel.injectable"; -import { messageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token"; -import applicationUpdateStatusChannelInjectable from "../../../common/application-update/application-update-status-channel.injectable"; - -const broadcastChangeInUpdatingStatusInjectable = getInjectable({ - id: "broadcast-change-in-updating-status", - - instantiate: (di) => { - const messageToChannel = di.inject(messageToChannelInjectionToken); - const applicationUpdateStatusChannel = di.inject(applicationUpdateStatusChannelInjectable); - - return (data: ApplicationUpdateStatusChannelMessage) => { - messageToChannel(applicationUpdateStatusChannel, data); - }; - }, -}); - -export default broadcastChangeInUpdatingStatusInjectable; diff --git a/src/main/application-update/check-for-updates/process-checking-for-updates.injectable.ts b/src/main/application-update/check-for-updates/process-checking-for-updates.injectable.ts index bd3501b5d8..73cab8d300 100644 --- a/src/main/application-update/check-for-updates/process-checking-for-updates.injectable.ts +++ b/src/main/application-update/check-for-updates/process-checking-for-updates.injectable.ts @@ -8,7 +8,6 @@ import updatesAreBeingDiscoveredInjectable from "../../../common/application-upd import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable"; import { runInAction } from "mobx"; import downloadUpdateInjectable from "../download-update/download-update.injectable"; -import broadcastChangeInUpdatingStatusInjectable from "./broadcast-change-in-updating-status.injectable"; import checkForUpdatesStartingFromChannelInjectable from "./check-for-updates-starting-from-channel.injectable"; import withOrphanPromiseInjectable from "../../../common/utils/with-orphan-promise/with-orphan-promise.injectable"; import emitEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; @@ -20,7 +19,6 @@ const processCheckingForUpdatesInjectable = getInjectable({ instantiate: (di) => { const downloadUpdate = di.inject(downloadUpdateInjectable); const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable); - const broadcastChangeInUpdatingStatus = di.inject(broadcastChangeInUpdatingStatusInjectable); const checkingForUpdatesState = di.inject(updatesAreBeingDiscoveredInjectable); const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable); const checkForUpdatesStartingFromChannel = di.inject(checkForUpdatesStartingFromChannelInjectable); @@ -34,8 +32,6 @@ const processCheckingForUpdatesInjectable = getInjectable({ params: { currentDateTime: getCurrentDateTime(), source }, }); - broadcastChangeInUpdatingStatus({ eventId: "checking-for-updates" }); - runInAction(() => { checkingForUpdatesState.set(true); }); @@ -43,8 +39,6 @@ const processCheckingForUpdatesInjectable = getInjectable({ const result = await checkForUpdatesStartingFromChannel(selectedUpdateChannel.value.get()); if (!result.updateWasDiscovered) { - broadcastChangeInUpdatingStatus({ eventId: "no-updates-available" }); - runInAction(() => { discoveredVersionState.set(null); checkingForUpdatesState.set(false); @@ -61,11 +55,6 @@ const processCheckingForUpdatesInjectable = getInjectable({ params: { version, currentDateTime: getCurrentDateTime() }, }); - broadcastChangeInUpdatingStatus({ - eventId: "download-for-update-started", - version, - }); - runInAction(() => { discoveredVersionState.set({ version, @@ -75,15 +64,7 @@ const processCheckingForUpdatesInjectable = getInjectable({ checkingForUpdatesState.set(false); }); - withOrphanPromise(async () => { - const { downloadWasSuccessful } = await downloadUpdate(); - - if (!downloadWasSuccessful) { - broadcastChangeInUpdatingStatus({ - eventId: "download-for-update-failed", - }); - } - })(); + withOrphanPromise(async () => await downloadUpdate())(); }; }, }); diff --git a/src/main/application-update/download-update/download-update.injectable.ts b/src/main/application-update/download-update/download-update.injectable.ts index f68b5166c0..4def3b493a 100644 --- a/src/main/application-update/download-update/download-update.injectable.ts +++ b/src/main/application-update/download-update/download-update.injectable.ts @@ -38,6 +38,7 @@ const downloadUpdateInjectable = getInjectable({ runInAction(() => { if (!downloadWasSuccessful) { + progressOfUpdateDownload.set({ percentage: 0, failed: "Download of update failed" }); discoveredVersionState.set(null); } diff --git a/src/renderer/application-update/application-update-status-listener.injectable.ts b/src/renderer/application-update/application-update-status-listener.injectable.ts deleted file mode 100644 index 69ba23608c..0000000000 --- a/src/renderer/application-update/application-update-status-listener.injectable.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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 type { ApplicationUpdateStatusChannel, ApplicationUpdateStatusEventId } from "../../common/application-update/application-update-status-channel.injectable"; -import applicationUpdateStatusChannelInjectable from "../../common/application-update/application-update-status-channel.injectable"; -import showInfoNotificationInjectable from "../components/notifications/show-info-notification.injectable"; -import type { MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token"; -import { messageChannelListenerInjectionToken } from "../../common/utils/channel/message-channel-listener-injection-token"; - -const applicationUpdateStatusListenerInjectable = getInjectable({ - id: "application-update-status-listener", - - instantiate: (di): MessageChannelListener => { - const channel = di.inject(applicationUpdateStatusChannelInjectable); - const showInfoNotification = di.inject(showInfoNotificationInjectable); - - const eventHandlers: Record void }> = { - "checking-for-updates": { - handle: () => { - showInfoNotification("Checking for updates..."); - }, - }, - - "no-updates-available": { - handle: () => { - showInfoNotification("No new updates available"); - }, - }, - - "download-for-update-started": { - handle: (version) => { - showInfoNotification(`Download for version ${version} started...`); - }, - }, - - "download-for-update-failed": { - handle: () => { - showInfoNotification("Download of update failed"); - }, - }, - }; - - return { - channel, - - handler: ({ eventId, version }) => { - eventHandlers[eventId].handle(version); - }, - }; - }, - - injectionToken: messageChannelListenerInjectionToken, -}); - -export default applicationUpdateStatusListenerInjectable; diff --git a/src/renderer/components/status-bar/auto-update-component.tsx b/src/renderer/components/status-bar/auto-update-component.tsx new file mode 100644 index 0000000000..5a0b7e8acf --- /dev/null +++ b/src/renderer/components/status-bar/auto-update-component.tsx @@ -0,0 +1,107 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { withInjectables } from "@ogre-tools/injectable-react"; +import { observer } from "mobx-react"; +import assert from "assert"; +import React, { useState } from "react"; +import { Spinner } from "../spinner"; +import type { ProgressOfUpdateDownload } from "../../../common/application-update/progress-of-update-download/progress-of-update-download.injectable"; +import progressOfUpdateDownloadInjectable from "../../../common/application-update/progress-of-update-download/progress-of-update-download.injectable"; +import type { DiscoveredUpdateVersion } from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable"; +import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable"; +import type { UpdateIsBeingDownloaded } from "../../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable"; +import updateIsBeingDownloadedInjectable from "../../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable"; +import type { UpdatesAreBeingDiscovered } from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable"; +import updatesAreBeingDiscoveredInjectable from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable"; +import { now as reactiveDateNow } from "mobx-utils"; + +interface Dependencies { + progressOfUpdateDownload: ProgressOfUpdateDownload; + discoveredVersionState: DiscoveredUpdateVersion; + downloadingUpdateState: UpdateIsBeingDownloaded; + checkingForUpdatesState: UpdatesAreBeingDiscovered; +} + +interface EndNoteProps { + version?: string; + note: (version: string) => JSX.Element; +} + +const EndNote = observer(({ version, note }: EndNoteProps) => { + const [start] = useState(Date.now()); + + if (start + 5000 <= reactiveDateNow()) { + return idle(); + } + + return note(version ?? ""); +}); + +const checking = () => ( + <> + +
Checking for updates...
+ +); + +const available = (version: string) =>
{`${version ?? "Update"} is available`}
; + +const notAvailable = () =>
No new updates available
; + +const downloading = (version: string) => { + return ( + <> +
{`Downloading version ${version}...`}
+ + + ); +}; + +const downloadFailed = (errMsg: string) =>
{errMsg}
; + +const idle = () =>
; + + +export const NonInjectedAutoUpdateComponent = observer(({ + progressOfUpdateDownload, + discoveredVersionState, + downloadingUpdateState, + checkingForUpdatesState, +}: Dependencies) => { + const discoveredVersion = discoveredVersionState.value.get(); + + const { failed } = progressOfUpdateDownload.value.get(); + + if (downloadingUpdateState.value.get()) { + + assert(discoveredVersion); + + return downloading(discoveredVersion.version); + } + + if (checkingForUpdatesState.value.get()) { + return checking(); + } + + if ( discoveredVersion) { + return ; + } + + if ( failed ) { + return ; + } + + return ; +}); + +export const AutoUpdateComponent = withInjectables(NonInjectedAutoUpdateComponent, { + getProps: (di, props) => ({ + progressOfUpdateDownload: di.inject(progressOfUpdateDownloadInjectable), + discoveredVersionState: di.inject(discoveredUpdateVersionInjectable), + downloadingUpdateState: di.inject(updateIsBeingDownloadedInjectable), + checkingForUpdatesState: di.inject(updatesAreBeingDiscoveredInjectable), + ...props, + }), +}); diff --git a/src/renderer/components/status-bar/auto-update-status-bar-item.injectable.ts b/src/renderer/components/status-bar/auto-update-status-bar-item.injectable.ts new file mode 100644 index 0000000000..de8f1518e8 --- /dev/null +++ b/src/renderer/components/status-bar/auto-update-status-bar-item.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { AutoUpdateComponent } from "./auto-update-component"; +import { statusBarItemInjectionToken } from "./status-bar-item-injection-token"; + +const autoUpdateStatusBarItemInjectable = getInjectable({ + id: "auto-update-status-bar-item", + + instantiate: () => ({ + component: AutoUpdateComponent, + position: "left" as const, + visible: computed(() => true), + }), + + injectionToken: statusBarItemInjectionToken, +}); + +export default autoUpdateStatusBarItemInjectable; diff --git a/src/renderer/components/status-bar/registered-status-bar-items.injectable.tsx b/src/renderer/components/status-bar/registered-status-bar-items.injectable.tsx deleted file mode 100644 index 976d36451c..0000000000 --- a/src/renderer/components/status-bar/registered-status-bar-items.injectable.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import { getInjectable } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import { computed } from "mobx"; -import type { - StatusBarItemProps, - StatusBarRegistration, -} from "./status-bar-registration"; -import statusBarItemsInjectable from "./status-bar-items.injectable"; - -export interface RegisteredStatusBarItems { - right: React.ComponentType[]; - left: React.ComponentType[]; -} - -interface Dependencies { - registrations: IComputedValue; -} - -function getRegisteredStatusBarItems({ registrations }: Dependencies): IComputedValue { - return computed(() => { - const res: RegisteredStatusBarItems = { - left: [], - right: [], - }; - - for (const registration of registrations.get()) { - if (!registration || typeof registration !== "object") { - continue; - } - - if (registration.item) { - const { item } = registration; - - // default for old API is "right" - res.right.push( - () => ( - <> - { - typeof item === "function" - ? item() - : item - } - - ), - ); - } else if (registration.components) { - const { position = "right", Item } = registration.components; - - if (position !== "left" && position !== "right") { - throw new TypeError("StatusBarRegistration.components.position must be either 'right' or 'left'"); - } - - res[position].push(Item); - } - } - - // This is done so that the first ones registered are closest to the corner - res.right.reverse(); - - return res; - }); -} - -const registeredStatusBarItemsInjectable = getInjectable({ - id: "registered-status-bar-items", - - instantiate: (di) => getRegisteredStatusBarItems({ - registrations: di.inject(statusBarItemsInjectable), - }), - -}); - -export default registeredStatusBarItemsInjectable; diff --git a/src/renderer/components/status-bar/status-bar-item-injection-token.ts b/src/renderer/components/status-bar/status-bar-item-injection-token.ts new file mode 100644 index 0000000000..0fec934077 --- /dev/null +++ b/src/renderer/components/status-bar/status-bar-item-injection-token.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import type React from "react"; + +export interface StatusBarItem { + component: React.ComponentType; + position: "left" | "right"; + visible: IComputedValue; +} + +export const statusBarItemInjectionToken = getInjectionToken({ + id: "status-bar-item", +}); diff --git a/src/renderer/components/status-bar/status-bar-item-registrator.injectable.tsx b/src/renderer/components/status-bar/status-bar-item-registrator.injectable.tsx new file mode 100644 index 0000000000..2b31c9b312 --- /dev/null +++ b/src/renderer/components/status-bar/status-bar-item-registrator.injectable.tsx @@ -0,0 +1,85 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { Injectable } from "@ogre-tools/injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token"; +import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; +import type { StatusBarItem } from "./status-bar-item-injection-token"; +import { statusBarItemInjectionToken } from "./status-bar-item-injection-token"; +import type { StatusBarRegistration } from "./status-bar-registration"; +import React from "react"; +import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { Logger } from "../../../common/logger"; + +const statusBarItemRegistratorInjectable = getInjectable({ + id: "status-bar-item-registrator", + + instantiate: (di) => (extension) => { + const rendererExtension = extension as LensRendererExtension; + const getRandomId = di.inject(getRandomIdInjectable); + const logger = di.inject(loggerInjectable); + + return rendererExtension.statusBarItems.flatMap( + toItemInjectableFor(rendererExtension, getRandomId, logger), + ); + }, + + injectionToken: extensionRegistratorInjectionToken, +}); + +export default statusBarItemRegistratorInjectable; + +const toItemInjectableFor = (extension: LensRendererExtension, getRandomId: () => string, logger: Logger) => { + return (registration: StatusBarRegistration): Injectable[] => { + const id = `${getRandomId()}-status-bar-item-for-extension-${extension.sanitizedExtensionId}`; + let component: React.ComponentType; + let position: "left" | "right"; + + if (registration?.item) { + const { item } = registration; + + position = "right"; + component = + () => ( + <> + { + typeof item === "function" + ? item() + : item + } + + ); + } else if (registration?.components) { + const { position: pos = "right", Item } = registration.components; + + if (pos !== "left" && pos !== "right") { + throw new TypeError("StatusBarRegistration.components.position must be either 'right' or 'left'"); + } + + position = pos; + component = Item; + } else { + logger.warn("StatusBarRegistration must have valid item or components field"); + + return []; + } + + return [getInjectable({ + id, + + instantiate: () => ({ + component, + position, + visible: computed(() => true), + }), + + injectionToken: statusBarItemInjectionToken, + })]; + }; +}; + + diff --git a/src/renderer/components/status-bar/status-bar-items.injectable.ts b/src/renderer/components/status-bar/status-bar-items.injectable.ts deleted file mode 100644 index 05a22b86a7..0000000000 --- a/src/renderer/components/status-bar/status-bar-items.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; - -const statusBarItemsInjectable = getInjectable({ - id: "status-bar-items", - - instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); - - return computed(() => - extensions.get().flatMap((ext) => ext.statusBarItems), - ); - }, -}); - -export default statusBarItemsInjectable; diff --git a/src/renderer/components/status-bar/status-bar-items.injectable.tsx b/src/renderer/components/status-bar/status-bar-items.injectable.tsx new file mode 100644 index 0000000000..855b8654c9 --- /dev/null +++ b/src/renderer/components/status-bar/status-bar-items.injectable.tsx @@ -0,0 +1,61 @@ +/** + * 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 type { IComputedValue } from "mobx"; +import { computed } from "mobx"; +import type { StatusBarItemProps } from "./status-bar-registration"; +import type { StatusBarItem } from "./status-bar-item-injection-token"; +import { statusBarItemInjectionToken } from "./status-bar-item-injection-token"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; + +export interface StatusBarItems { + right: React.ComponentType[]; + left: React.ComponentType[]; +} + +interface Dependencies { + registrations: IComputedValue; +} + +function getStatusBarItems({ registrations }: Dependencies): IComputedValue { + return computed(() => { + const res: StatusBarItems = { + left: [], + right: [], + }; + + for (const registration of registrations.get()) { + const { position = "right", component, visible } = registration; + + if (!visible.get()) { + continue; + } + + res[position].push(component); + } + + // This is done so that the first ones registered are closest to the corner + res.right.reverse(); + + return res; + }); +} + +const statusBarItemsInjectable = getInjectable({ + id: "status-bar-items", + + instantiate: (di) => { + const computedInjectMany = di.inject( + computedInjectManyInjectable, + ); + + return getStatusBarItems({ + registrations: computedInjectMany(statusBarItemInjectionToken), + }); + }, + +}); + +export default statusBarItemsInjectable; diff --git a/src/renderer/components/status-bar/status-bar.test.tsx b/src/renderer/components/status-bar/status-bar.test.tsx index 53920d45c7..322b37dc85 100644 --- a/src/renderer/components/status-bar/status-bar.test.tsx +++ b/src/renderer/components/status-bar/status-bar.test.tsx @@ -5,18 +5,16 @@ import React from "react"; import "@testing-library/jest-dom/extend-expect"; -import { StatusBar } from "./status-bar"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import type { DiRender } from "../test-utils/renderFor"; -import { renderFor } from "../test-utils/renderFor"; import type { IObservableArray } from "mobx"; import { computed, observable } from "mobx"; import type { DiContainer } from "@ogre-tools/injectable"; +import type { StatusBarItems } from "./status-bar-items.injectable"; import statusBarItemsInjectable from "./status-bar-items.injectable"; -import type { StatusBarRegistration } from "./status-bar-registration"; import { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; +import type { ApplicationBuilder } from "../test-utils/get-application-builder"; +import { getApplicationBuilder } from "../test-utils/get-application-builder"; +import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable"; class SomeTestExtension extends LensRendererExtension { constructor(statusBarItems: IObservableArray) { @@ -35,21 +33,29 @@ class SomeTestExtension extends LensRendererExtension { } describe("", () => { - let render: DiRender; let di: DiContainer; let statusBarItems: IObservableArray; + let applicationBuilder: ApplicationBuilder; - beforeEach(() => { + beforeEach(async () => { statusBarItems = observable.array([]); - di = getDiForUnitTesting({ doGeneralOverrides: true }); - render = renderFor(di); + + applicationBuilder = getApplicationBuilder(); + + applicationBuilder.beforeApplicationStart(({ rendererDi }) => { + rendererDi.unoverride(getRandomIdInjectable); + rendererDi.permitSideEffects(getRandomIdInjectable); + }); + + applicationBuilder.extensions.renderer.enable(new SomeTestExtension(statusBarItems)); + + di = applicationBuilder.dis.rendererDi; di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - di.override(rendererExtensionsInjectable, () => computed(() => [new SomeTestExtension(statusBarItems)])); }); - it("renders w/o errors", () => { - const { container } = render(); + it("renders w/o errors", async () => { + const { container } = await applicationBuilder.render(); expect(container).toBeInstanceOf(HTMLElement); }); @@ -62,26 +68,27 @@ describe("", () => { [], [{}], {}, - ])("renders w/o errors when registrations are not type compliant (%p)", val => { + ])("renders w/o errors when registrations are not type compliant (%p)", async val => { statusBarItems.replace([val]); - expect(() => render()).not.toThrow(); + await expect(applicationBuilder.render()).resolves.toBeTruthy(); }); - it("renders items [{item: React.ReactNode}] (4.0.0-rc.1)", () => { + it("renders items [{item: React.ReactNode}] (4.0.0-rc.1)", async () => { const testId = "testId"; const text = "heee"; - di.override(statusBarItemsInjectable, () => computed(() => [ - { item: {text} }, - ] as StatusBarRegistration[])); + di.override(statusBarItemsInjectable, () => computed(() => ({ + right: [ () => {text} ], + left: [], + }) as StatusBarItems)); - const { getByTestId } = render(); + const { getByTestId } = await applicationBuilder.render(); expect(getByTestId(testId)).toHaveTextContent(text); }); - it("renders items [{item: () => React.ReactNode}] (4.0.0-rc.1+)", () => { + it("renders items [{item: () => React.ReactNode}] (4.0.0-rc.1+)", async () => { const testId = "testId"; const text = "heee"; @@ -89,13 +96,13 @@ describe("", () => { item: () => {text}, }]); - const { getByTestId } = render(); + const { getByTestId } = await applicationBuilder.render(); expect(getByTestId(testId)).toHaveTextContent(text); }); - it("sort positioned items properly", () => { + it("sort positioned items properly", async () => { statusBarItems.replace([ { components: { @@ -122,7 +129,7 @@ describe("", () => { }, ]); - const { getAllByTestId } = render(); + const { getAllByTestId } = await applicationBuilder.render(); const elems = getAllByTestId("sortedElem"); const positions = elems.map(elem => elem.textContent); diff --git a/src/renderer/components/status-bar/status-bar.tsx b/src/renderer/components/status-bar/status-bar.tsx index 9d08cfb1fa..1214a104c7 100644 --- a/src/renderer/components/status-bar/status-bar.tsx +++ b/src/renderer/components/status-bar/status-bar.tsx @@ -8,14 +8,14 @@ import styles from "./status-bar.module.scss"; import React from "react"; import { observer } from "mobx-react"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { RegisteredStatusBarItems } from "./registered-status-bar-items.injectable"; -import registeredStatusBarItemsInjectable from "./registered-status-bar-items.injectable"; +import type { StatusBarItems } from "./status-bar-items.injectable"; +import statusBarItemsInjectable from "./status-bar-items.injectable"; import type { IComputedValue } from "mobx"; export interface StatusBarProps {} interface Dependencies { - items: IComputedValue; + items: IComputedValue; } const NonInjectedStatusBar = observer(({ items }: Dependencies & StatusBarProps) => { @@ -23,14 +23,14 @@ const NonInjectedStatusBar = observer(({ items }: Dependencies & StatusBarProps) return (
-
+
{left.map((Item, index) => (
))}
-
+
{right.map((Item, index) => (
@@ -44,7 +44,7 @@ const NonInjectedStatusBar = observer(({ items }: Dependencies & StatusBarProps) export const StatusBar = withInjectables(NonInjectedStatusBar, { getProps: (di, props) => ({ - items: di.inject(registeredStatusBarItemsInjectable), + items: di.inject(statusBarItemsInjectable), ...props, }), }); diff --git a/src/renderer/components/test-utils/get-renderer-extension-fake.ts b/src/renderer/components/test-utils/get-renderer-extension-fake.ts index fb31ee70b9..3fb720bf0f 100644 --- a/src/renderer/components/test-utils/get-renderer-extension-fake.ts +++ b/src/renderer/components/test-utils/get-renderer-extension-fake.ts @@ -17,8 +17,10 @@ export class TestExtension extends LensRendererExtension {} export type FakeExtensionData = SetRequired, "id" | "name">; -export const getRendererExtensionFakeFor = (builder: ApplicationBuilder) => ( - function getRendererExtensionFake({ id, name, ...rest }: FakeExtensionData) { +export type GetRendererExtensionFake = (fakeExtensionData: FakeExtensionData) => TestExtension; + +export const getRendererExtensionFakeFor = (builder: ApplicationBuilder): GetRendererExtensionFake => ( + function getRendererExtensionFake({ id, name, ...rest }) { const instance = new TestExtension({ id, absolutePath: "irrelevant",