diff --git a/src/behaviours/application-update/analytics-for-installing-update.test.ts b/src/behaviours/application-update/analytics-for-installing-update.test.ts new file mode 100644 index 0000000000..07eebccf1f --- /dev/null +++ b/src/behaviours/application-update/analytics-for-installing-update.test.ts @@ -0,0 +1,158 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable"; +import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable"; +import type { AsyncFnMock } from "@async-fn/jest"; +import asyncFn from "@async-fn/jest"; +import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable"; +import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable"; +import appEventBusInjectable from "../../common/app-event-bus/app-event-bus.injectable"; +import type { DiContainer } from "@ogre-tools/injectable"; +import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable"; +import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable"; +import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable"; +import quitAndInstallUpdateInjectable from "../../main/application-update/quit-and-install-update.injectable"; +import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable"; + +describe("analytics for installing update", () => { + let applicationBuilder: ApplicationBuilder; + let checkForPlatformUpdatesMock: AsyncFnMock; + let downloadPlatformUpdateMock: AsyncFnMock; + let analyticsListenerMock: jest.Mock; + let mainDi: DiContainer; + + beforeEach(async () => { + global.Date.now = () => new Date("2015-10-21T07:28:00Z").getTime(); + + applicationBuilder = getApplicationBuilder(); + + analyticsListenerMock = jest.fn(); + + applicationBuilder.beforeApplicationStart(({ mainDi }) => { + mainDi.override(appVersionInjectable, () => "42.0.0"); + + checkForPlatformUpdatesMock = asyncFn(); + + mainDi.override( + checkForPlatformUpdatesInjectable, + () => checkForPlatformUpdatesMock, + ); + + downloadPlatformUpdateMock = asyncFn(); + + mainDi.override(downloadPlatformUpdateInjectable, () => downloadPlatformUpdateMock); + mainDi.override(electronUpdaterIsActiveInjectable, () => true); + + mainDi.override(publishIsConfiguredInjectable, () => true); + + const eventBus = mainDi.inject(appEventBusInjectable); + + eventBus.addListener(analyticsListenerMock); + }); + + mainDi = applicationBuilder.dis.mainDi; + + await applicationBuilder.render(); + }); + + it("sends event to analytics about the current version", () => { + expect(analyticsListenerMock).toHaveBeenCalledWith({ + name: "app", + action: "current-version", + + params: { + version: "42.0.0", + currentDateTime: "2015-10-21T07:28:00Z", + }, + }); + }); + + describe("when checking for updates", () => { + beforeEach(() => { + analyticsListenerMock.mockClear(); + + const processCheckingForUpdates = mainDi.inject(processCheckingForUpdatesInjectable); + + processCheckingForUpdates(); + }); + + it("sends event to analytics about checking for updates", () => { + expect(analyticsListenerMock.mock.calls).toEqual([ + [ + { + name: "app", + action: "checking-for-updates", + + params: { + currentDateTime: "2015-10-21T07:28:00Z", + }, + }, + ], + ]); + }); + + describe("when check for updates resolves with new update being available", () => { + beforeEach(async () => { + analyticsListenerMock.mockClear(); + + await checkForPlatformUpdatesMock.resolve({ + updateWasDiscovered: true, + version: "43.0.0", + }); + }); + + it("sends event to analytics about new update being available", () => { + expect(analyticsListenerMock.mock.calls).toEqual([ + [ + { + name: "app", + action: "update-was-discovered", + + params: { + version: "43.0.0", + currentDateTime: "2015-10-21T07:28:00Z", + }, + }, + ], + ]); + }); + + describe("given update is downloaded", () => { + beforeEach(async () => { + analyticsListenerMock.mockClear(); + + await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true }); + }); + + it("does not send event to analytics about update downloaded being successful", () => { + expect(analyticsListenerMock).not.toHaveBeenCalled(); + }); + + it("when installing the update, sends event to analytics about installing the update", () => { + const quitAndInstallUpdate = mainDi.inject(quitAndInstallUpdateInjectable); + + quitAndInstallUpdate(); + + expect(analyticsListenerMock.mock.calls).toEqual([ + [ + { + name: "app", + action: "start-installing-update", + + params: { + version: "43.0.0", + currentDateTime: "2015-10-21T07:28:00Z", + updateChannel: "latest", + }, + }, + ], + ]); + }); + }); + }); + }); +}); diff --git a/src/behaviours/application-update/downgrading-version-update.test.ts b/src/behaviours/application-update/downgrading-version-update.test.ts index e8e5635fb3..e0b154bb21 100644 --- a/src/behaviours/application-update/downgrading-version-update.test.ts +++ b/src/behaviours/application-update/downgrading-version-update.test.ts @@ -22,8 +22,6 @@ describe("downgrading version update", () => { let mainDi: DiContainer; beforeEach(() => { - jest.useFakeTimers(); - applicationBuilder = getApplicationBuilder(); applicationBuilder.beforeApplicationStart(({ mainDi }) => { diff --git a/src/behaviours/application-update/installing-update.test.ts b/src/behaviours/application-update/installing-update.test.ts index 3fec5f6d27..2d7a749b2d 100644 --- a/src/behaviours/application-update/installing-update.test.ts +++ b/src/behaviours/application-update/installing-update.test.ts @@ -4,7 +4,7 @@ */ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable"; +import quitAndInstallUpdateInjectable from "../../main/application-update/quit-and-install-update.injectable"; import type { RenderResult } from "@testing-library/react"; import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable"; import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable"; diff --git a/src/behaviours/application-update/selection-of-update-stability.test.ts b/src/behaviours/application-update/selection-of-update-stability.test.ts index 1792fcd484..49eb68469c 100644 --- a/src/behaviours/application-update/selection-of-update-stability.test.ts +++ b/src/behaviours/application-update/selection-of-update-stability.test.ts @@ -4,7 +4,7 @@ */ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable"; +import quitAndInstallUpdateInjectable from "../../main/application-update/quit-and-install-update.injectable"; import type { RenderResult } from "@testing-library/react"; import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable"; import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable"; diff --git a/src/common/app-event-bus/emit-event.injectable.ts b/src/common/app-event-bus/emit-event.injectable.ts new file mode 100644 index 0000000000..47bf2ca691 --- /dev/null +++ b/src/common/app-event-bus/emit-event.injectable.ts @@ -0,0 +1,13 @@ +/** + * 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 appEventBusInjectable from "./app-event-bus.injectable"; + +const emitEventInjectable = getInjectable({ + id: "emit-event", + instantiate: (di) => di.inject(appEventBusInjectable).emit, +}); + +export default emitEventInjectable; 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 2688d00d4a..840095789d 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,11 +8,13 @@ import updatesAreBeingDiscoveredInjectable from "../../../common/application-upd import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable"; import { runInAction } from "mobx"; import askBooleanInjectable from "../../ask-boolean/ask-boolean.injectable"; -import quitAndInstallUpdateInjectable from "../../electron-app/features/quit-and-install-update.injectable"; 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"; +import { getCurrentDateTime } from "../../../common/utils/date/get-current-date-time"; +import quitAndInstallUpdateInjectable from "../quit-and-install-update.injectable"; const processCheckingForUpdatesInjectable = getInjectable({ id: "process-checking-for-updates", @@ -27,8 +29,15 @@ const processCheckingForUpdatesInjectable = getInjectable({ const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable); const checkForUpdatesStartingFromChannel = di.inject(checkForUpdatesStartingFromChannelInjectable); const withOrphanPromise = di.inject(withOrphanPromiseInjectable); + const emitEvent = di.inject(emitEventInjectable); return async () => { + emitEvent({ + name: "app", + action: "checking-for-updates", + params: { currentDateTime: getCurrentDateTime() }, + }); + broadcastChangeInUpdatingStatus({ eventId: "checking-for-updates" }); runInAction(() => { @@ -50,6 +59,12 @@ const processCheckingForUpdatesInjectable = getInjectable({ const { version, actualUpdateChannel } = result; + emitEvent({ + name: "app", + action: "update-was-discovered", + params: { version, currentDateTime: getCurrentDateTime() }, + }); + broadcastChangeInUpdatingStatus({ eventId: "download-for-update-started", version, diff --git a/src/main/application-update/emit-current-version-to-analytics.injectable.ts b/src/main/application-update/emit-current-version-to-analytics.injectable.ts new file mode 100644 index 0000000000..fd190ebf72 --- /dev/null +++ b/src/main/application-update/emit-current-version-to-analytics.injectable.ts @@ -0,0 +1,36 @@ +/** + * 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 { afterApplicationIsLoadedInjectionToken } from "../start-main-application/runnable-tokens/after-application-is-loaded-injection-token"; +import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable"; +import { getCurrentDateTime } from "../../common/utils/date/get-current-date-time"; +import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable"; + +const emitCurrentVersionToAnalyticsInjectable = getInjectable({ + id: "emit-current-version-to-analytics", + + instantiate: (di) => { + const emitEvent = di.inject(emitEventInjectable); + const appVersion = di.inject(appVersionInjectable); + + return { + run: () => { + emitEvent({ + name: "app", + action: "current-version", + + params: { + version: appVersion, + currentDateTime: getCurrentDateTime(), + }, + }); + }, + }; + }, + + injectionToken: afterApplicationIsLoadedInjectionToken, +}); + +export default emitCurrentVersionToAnalyticsInjectable; diff --git a/src/main/application-update/install-application-update-tray-item.injectable.ts b/src/main/application-update/install-application-update-tray-item.injectable.ts index 2f938964f8..a7a63d1a1b 100644 --- a/src/main/application-update/install-application-update-tray-item.injectable.ts +++ b/src/main/application-update/install-application-update-tray-item.injectable.ts @@ -5,12 +5,12 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token"; -import quitAndInstallUpdateInjectable from "../electron-app/features/quit-and-install-update.injectable"; import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable"; import updateIsBeingDownloadedInjectable from "../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable"; import { withErrorSuppression } from "../../common/utils/with-error-suppression/with-error-suppression"; import { pipeline } from "@ogre-tools/fp"; import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable"; +import quitAndInstallUpdateInjectable from "./quit-and-install-update.injectable"; const installApplicationUpdateTrayItemInjectable = getInjectable({ id: "install-update-tray-item", diff --git a/src/main/application-update/quit-and-install-update.injectable.ts b/src/main/application-update/quit-and-install-update.injectable.ts new file mode 100644 index 0000000000..0ba82feac2 --- /dev/null +++ b/src/main/application-update/quit-and-install-update.injectable.ts @@ -0,0 +1,45 @@ +/** + * 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 electronQuitAndInstallUpdateInjectable from "../electron-app/features/electron-quit-and-install-update.injectable"; +import { getCurrentDateTime } from "../../common/utils/date/get-current-date-time"; +import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable"; +import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable"; + +const quitAndInstallUpdateInjectable = getInjectable({ + id: "quit-and-install-update", + + instantiate: (di) => { + const electronQuitAndInstallUpdate = di.inject( + electronQuitAndInstallUpdateInjectable, + ); + + const emitEvent = di.inject(emitEventInjectable); + const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable); + + return () => { + const discoveredVersion = discoveredUpdateVersion.value.get(); + + if (!discoveredVersion) { + throw new Error("Tried to install update but no update was discovered."); + } + + emitEvent({ + name: "app", + action: "start-installing-update", + + params: { + version: discoveredVersion.version, + updateChannel: discoveredVersion.updateChannel.id, + currentDateTime: getCurrentDateTime(), + }, + }); + + electronQuitAndInstallUpdate(); + }; + }, +}); + +export default quitAndInstallUpdateInjectable; diff --git a/src/main/electron-app/features/quit-and-install-update.injectable.ts b/src/main/electron-app/features/electron-quit-and-install-update.injectable.ts similarity index 73% rename from src/main/electron-app/features/quit-and-install-update.injectable.ts rename to src/main/electron-app/features/electron-quit-and-install-update.injectable.ts index 6b313e21b0..fb7f2fb994 100644 --- a/src/main/electron-app/features/quit-and-install-update.injectable.ts +++ b/src/main/electron-app/features/electron-quit-and-install-update.injectable.ts @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import electronUpdaterInjectable from "./electron-updater.injectable"; -const quitAndInstallUpdateInjectable = getInjectable({ - id: "quit-and-install-update", +const electronQuitAndInstallUpdateInjectable = getInjectable({ + id: "electron-quit-and-install-update", instantiate: (di) => { const electronUpdater = di.inject(electronUpdaterInjectable); @@ -17,4 +17,4 @@ const quitAndInstallUpdateInjectable = getInjectable({ }, }); -export default quitAndInstallUpdateInjectable; +export default electronQuitAndInstallUpdateInjectable; diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 43f61c22cc..4e30baa6c0 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -72,7 +72,7 @@ import getElectronThemeInjectable from "./electron-app/features/get-electron-the import syncThemeFromOperatingSystemInjectable from "./electron-app/features/sync-theme-from-operating-system.injectable"; import platformInjectable from "../common/vars/platform.injectable"; import productNameInjectable from "./app-paths/app-name/product-name.injectable"; -import quitAndInstallUpdateInjectable from "./electron-app/features/quit-and-install-update.injectable"; +import electronQuitAndInstallUpdateInjectable from "./electron-app/features/electron-quit-and-install-update.injectable"; import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable"; import publishIsConfiguredInjectable from "./application-update/publish-is-configured.injectable"; import checkForPlatformUpdatesInjectable from "./application-update/check-for-platform-updates/check-for-platform-updates.injectable"; @@ -248,7 +248,7 @@ const overrideElectronFeatures = (di: DiContainer) => { di.override(ipcMainInjectable, () => ({})); di.override(getElectronThemeInjectable, () => () => "dark"); di.override(syncThemeFromOperatingSystemInjectable, () => ({ start: () => {}, stop: () => {} })); - di.override(quitAndInstallUpdateInjectable, () => () => {}); + di.override(electronQuitAndInstallUpdateInjectable, () => () => {}); di.override(setUpdateOnQuitInjectable, () => () => {}); di.override(downloadPlatformUpdateInjectable, () => async () => ({ downloadWasSuccessful: true }));