1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Show notifications and dialog for downloading update

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-05-11 15:18:47 +03:00
parent b970e69165
commit 52303b170b
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
7 changed files with 126 additions and 207 deletions

View File

@ -1,35 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`installing update using tray given a non-installed update is already downloaded, when started renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given a non-installed update is already downloaded, when started when user checks for even newer update renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given a non-installed update is already downloaded, when started when user checks for even newer update when new update is discovered renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given a non-installed update is already downloaded, when started when user checks for even newer update when no new update is discovered renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given a non-installed update is already downloaded, when started when user installs the update renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given no update is already downloaded, and "latest" update channel is selected, when started renders 1`] = `
<body>
<div />
@ -54,7 +24,19 @@ exports[`installing update using tray given no update is already downloaded, and
</body>
`;
exports[`installing update using tray given no update is already downloaded, and "latest" update channel is selected, when started when user checks for updates using tray when new update is discovered when update is downloaded when user installs the update renders 1`] = `
exports[`installing update using tray given no update is already downloaded, and "latest" update channel is selected, when started when user checks for updates using tray when new update is discovered when update is downloaded when user answers not to install the update renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given no update is already downloaded, and "latest" update channel is selected, when started when user checks for updates using tray when new update is discovered when update is downloaded when user answers to install the update renders 1`] = `
<body>
<div />
</body>
`;
exports[`installing update using tray given no update is already downloaded, and "latest" update channel is selected, when started when user checks for updates using tray when new update is discovered when update is downloaded when user disregards the question and installs the update using tray renders 1`] = `
<body>
<div />
</body>

View File

@ -20,6 +20,9 @@ import progressOfUpdateDownloadInjectable from "../../main/update-app/progress-o
import type { IComputedValue } from "mobx";
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
import showApplicationWindowInjectable from "../../main/start-main-application/lens-window/show-application-window.injectable";
import showNotificationInjectable from "../../main/show-notification/show-notification.injectable";
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
describe("installing update using tray", () => {
let applicationBuilder: ApplicationBuilder;
@ -28,6 +31,8 @@ describe("installing update using tray", () => {
let downloadPlatformUpdateMock: AsyncFnMock<() => void>;
let setUpdateOnQuitMock: jest.Mock;
let showApplicationWindowMock: jest.Mock;
let showNotificationMock: jest.Mock;
let askBooleanMock: AsyncFnMock<AskBoolean>;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
@ -38,9 +43,12 @@ describe("installing update using tray", () => {
downloadPlatformUpdateMock = asyncFn();
setUpdateOnQuitMock = jest.fn();
showApplicationWindowMock = jest.fn();
showNotificationMock = jest.fn();
askBooleanMock = asyncFn();
mainDi.override(showNotificationInjectable, () => showNotificationMock);
mainDi.override(askBooleanInjectable, () => askBooleanMock);
mainDi.override(showApplicationWindowInjectable, () => showApplicationWindowMock);
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
mainDi.override(
@ -96,7 +104,9 @@ describe("installing update using tray", () => {
expect(showApplicationWindowMock).not.toHaveBeenCalled();
});
xit("notifies the user that checking for updates is happening", () => {});
it("notifies the user that checking for updates is happening", () => {
expect(showNotificationMock).toHaveBeenCalledWith("Checking for updates...");
});
it("user cannot check for updates again", () => {
expect(
@ -120,6 +130,8 @@ describe("installing update using tray", () => {
describe("when no new update is discovered", () => {
beforeEach(async () => {
showNotificationMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
@ -131,7 +143,9 @@ describe("installing update using tray", () => {
expect(showApplicationWindowMock).toHaveBeenCalled();
});
xit("notifies the user", () => {});
it("notifies the user", () => {
expect(showNotificationMock).toHaveBeenCalledWith("No new updates available");
});
it("does not start downloading update", () => {
expect(downloadPlatformUpdateMock).not.toHaveBeenCalled();
@ -176,7 +190,9 @@ describe("installing update using tray", () => {
expect(downloadPlatformUpdateMock).toHaveBeenCalled();
});
xit("notifies the user that download is happening", () => {});
it("notifies the user that download is happening", () => {
expect(showNotificationMock).toHaveBeenCalledWith("Downloading update some-version...");
});
it("user cannot check for updates again yet", () => {
expect(
@ -187,7 +203,7 @@ describe("installing update using tray", () => {
it("name of tray item for checking updates indicates that downloading is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe('Downloading update "some-version" (0%)...');
).toBe("Downloading update some-version (0%)...");
});
it("when download progresses, percentage increases", () => {
@ -199,7 +215,7 @@ describe("installing update using tray", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe('Downloading update "some-version" (42%)...');
).toBe("Downloading update some-version (42%)...");
});
it("user still cannot install update", () => {
@ -222,7 +238,7 @@ describe("installing update using tray", () => {
it("user can install update", () => {
expect(
applicationBuilder.tray.get("install-update").label.get(),
).toBe('Install update "some-version"');
).toBe("Install update some-version");
});
it("user can check for updates again", () => {
@ -241,7 +257,39 @@ describe("installing update using tray", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when user installs the update", () => {
it("asks user to install update immediately", () => {
expect(askBooleanMock).toHaveBeenCalledWith("Do you want to install update some-version?");
});
describe("when user answers to install the update", () => {
beforeEach(async () => {
await askBooleanMock.resolve(true);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("quits application and installs the update", () => {
expect(quitAndInstallUpdateMock).toHaveBeenCalled();
});
});
describe("when user answers not to install the update", () => {
beforeEach(async () => {
await askBooleanMock.resolve(false);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not quit application and install the update", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
});
});
describe("when user disregards the question and installs the update using tray", () => {
beforeEach(async () => {
await applicationBuilder.tray.click("install-update");
});
@ -250,7 +298,7 @@ describe("installing update using tray", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("quits application and installs update", () => {
it("quits application and installs the update", () => {
expect(quitAndInstallUpdateMock).toHaveBeenCalled();
});
});
@ -423,166 +471,4 @@ describe("installing update using tray", () => {
});
});
});
xdescribe("given a non-installed update is already downloaded, when started", () => {
let rendered: RenderResult;
beforeEach(async () => {
rendered = await applicationBuilder.render();
});
it("user can check for updates", () => {
expect(
applicationBuilder.tray.get("check-for-updates").enabled.get(),
).toBe(true);
});
it("user can install the update", () => {
expect(applicationBuilder.tray.get("install-update").label.get()).toBe(
'Install update "some-old-version"',
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when user installs the update", () => {
beforeEach(() => {});
it("quits application and installs update", () => {});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when user checks for even newer update", () => {
let checkForUpdatesPromise: Promise<void>;
beforeEach(() => {
checkForUpdatesPromise =
applicationBuilder.tray.click("check-for-updates");
});
it("user cannot check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates").enabled.get(),
).toBe(false);
});
it("user cannot install any update", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
it("name of tray item indicates that checking is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe("Checking for updates...");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when no new update is discovered", () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
await checkForUpdatesPromise;
});
xit("notifies the user about the old update", () => {});
it("user can check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates").enabled.get(),
).toBe(true);
});
it("user can still install the old update", () => {
expect(
applicationBuilder.tray.get("install-update").label.get(),
).toBe('Install update "some-old-version"');
});
it("name of tray item for checking update no longer indicates that checking is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe("Check for updates");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when new update is discovered", () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-newer-version",
});
await checkForUpdatesPromise;
});
it("starts downloading the update", () => {
expect(downloadPlatformUpdateMock).toHaveBeenCalled();
});
it("user cannot check for updates again yet", () => {
expect(
applicationBuilder.tray.get("check-for-updates").enabled.get(),
).toBe(false);
});
it("name of tray item still indicates that download is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe('Downloading update "some-newer-version"...');
});
it("user cannot install any update yet", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
xit("notifies the user that download of new update is happening", () => {});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when new update is downloaded", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve();
});
it("user can install the new update", () => {
expect(
applicationBuilder.tray.get("install-update").label.get(),
).toBe('Install update "some-new-version"');
});
it("user can check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates").enabled.get(),
).toBe(true);
});
it("name of tray item no longer indicates that checking is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates").label.get(),
).toBe("Check for updates");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
});
});
});
});

View File

@ -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";
export type AskBoolean = (question: string) => Promise<boolean>;
const askBooleanInjectable = getInjectable({
id: "ask-boolean",
instantiate: (di): AskBoolean => async (question: string) => false,
});
export default askBooleanInjectable;

View File

@ -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";
const showNotificationInjectable = getInjectable({
id: "show-notification",
instantiate: (di) => (message: string) => {},
});
export default showNotificationInjectable;

View File

@ -9,6 +9,9 @@ import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-ite
import versionUpdateInjectable from "./version-update.injectable";
import progressOfUpdateDownloadInjectable from "./progress-of-update-download.injectable";
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
import showNotificationInjectable from "../show-notification/show-notification.injectable";
import askBooleanInjectable from "../ask-boolean/ask-boolean.injectable";
import quitAndInstallUpdateInjectable from "../electron-app/features/quit-and-install-update.injectable";
const checkForUpdatesTrayItemInjectable = getInjectable({
id: "check-for-updates-tray-item",
@ -18,6 +21,9 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
const versionUpdate = di.inject(versionUpdateInjectable);
const progressOfUpdateDownload = di.inject(progressOfUpdateDownloadInjectable);
const showNotification = di.inject(showNotificationInjectable);
const askBoolean = di.inject(askBooleanInjectable);
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
return {
id: "check-for-updates",
@ -26,7 +32,7 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
label: computed(() => {
if (versionUpdate.downloading.get()) {
return `Downloading update "${versionUpdate.discoveredVersion.get()}" (${progressOfUpdateDownload.value.get()}%)...`;
return `Downloading update ${versionUpdate.discoveredVersion.get()} (${progressOfUpdateDownload.value.get()}%)...`;
}
if (versionUpdate.checking.get()) {
@ -41,11 +47,19 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
visible: computed(() => updatingIsEnabled),
click: async () => {
const { updateWasDiscovered } = await versionUpdate.checkForUpdates();
const { updateWasDiscovered, version } = await versionUpdate.checkForUpdates();
if (updateWasDiscovered) {
showNotification(`Downloading update ${version}...`);
// Note: intentional orphan promise to make download happen in the background
versionUpdate.downloadUpdate();
versionUpdate.downloadUpdate().then(async () => {
const userWantsToInstallUpdate = await askBoolean(`Do you want to install update ${version}?`);
if (userWantsToInstallUpdate) {
quitAndInstallUpdate();
}
});
}
await showApplicationWindow();

View File

@ -23,7 +23,7 @@ const installApplicationUpdateTrayItemInjectable = getInjectable({
label: computed(() => {
const versionToBeInstalled = versionUpdate.discoveredVersion.get();
return `Install update "${versionToBeInstalled}"`;
return `Install update ${versionToBeInstalled}`;
}),
enabled: computed(() => true),

View File

@ -10,6 +10,7 @@ import downloadPlatformUpdateInjectable from "./download-platform-update.injecta
import type { CheckForPlatformUpdates } from "./check-for-platform-updates.injectable";
import checkForPlatformUpdatesInjectable from "./check-for-platform-updates.injectable";
import type { UpdateChannel } from "./update-channels";
import showNotificationInjectable from "../show-notification/show-notification.injectable";
const versionUpdateInjectable = getInjectable({
id: "version-update",
@ -18,6 +19,7 @@ const versionUpdateInjectable = getInjectable({
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
const downloadPlatformUpdate = di.inject(downloadPlatformUpdateInjectable);
const checkForPlatformUpdates = di.inject(checkForPlatformUpdatesInjectable);
const showNotification = di.inject(showNotificationInjectable);
const discoveredVersionState = observable.box<string>();
const downloadingState = observable.box<boolean>(false);
@ -36,6 +38,7 @@ const versionUpdateInjectable = getInjectable({
checkingState,
selectedUpdateChannel.value,
discoveredFromUpdateChannelState,
showNotification,
),
downloadUpdate: downloadUpdateFor(
@ -72,6 +75,7 @@ const checkForUpdatesFor =
checkingState: IObservableValue<boolean>,
selectedUpdateChannel: IComputedValue<UpdateChannel>,
discoveredFromUpdateChannelState: IObservableValue<UpdateChannel>,
showNotification: (message: string) => void,
) =>
async () => {
runInAction(() => {
@ -81,17 +85,23 @@ const checkForUpdatesFor =
const checkForUpdatesStartingFromChannel =
checkForUpdatesStartingFromChannelFor(checkForPlatformUpdates);
showNotification("Checking for updates...");
const { updateWasDiscovered, version, actualUpdateChannel } = await checkForUpdatesStartingFromChannel(
selectedUpdateChannel.get(),
);
if (!updateWasDiscovered) {
showNotification("No new updates available");
}
runInAction(() => {
discoveredFromUpdateChannelState.set(actualUpdateChannel);
discoveredVersionState.set(version);
checkingState.set(false);
});
return { updateWasDiscovered };
return { updateWasDiscovered, version };
};