Skip to content

Commit

Permalink
feat(autoupdates): sync auto updater and menu state COMPASS-7673 (#5491)
Browse files Browse the repository at this point in the history
  • Loading branch information
baileympearson committed Mar 1, 2024
1 parent 6080988 commit a385fa3
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 15 deletions.
14 changes: 14 additions & 0 deletions packages/compass/src/main/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EventEmitter } from 'events';
import type { BrowserWindow, Event } from 'electron';
import { app, safeStorage, session } from 'electron';
import { ipcMain } from 'hadron-ipc';
import type { AutoUpdateManagerState } from './auto-update-manager';
import { CompassAutoUpdateManager } from './auto-update-manager';
import { CompassLogging } from './logging';
import { CompassTelemetry } from './telemetry';
Expand Down Expand Up @@ -271,6 +272,14 @@ class CompassApplication {
event: 'check-for-updates',
handler: () => void
): typeof CompassApplication;
static on(
event: 'auto-updater:new-state',
handler: (state: AutoUpdateManagerState) => void
): typeof CompassApplication;
static on(
event: 'menu-request-restart',
handler: () => void
): typeof CompassApplication;
static on(
event: string,
handler: (...args: unknown[]) => void
Expand All @@ -283,6 +292,11 @@ class CompassApplication {
static emit(event: 'show-log-file-dialog'): boolean;
static emit(event: 'new-window', bw: BrowserWindow): boolean;
static emit(event: 'check-for-updates'): boolean;
static emit(
event: 'auto-updater:new-state',
state: AutoUpdateManagerState
): boolean;
static emit(event: 'menu-request-restart'): boolean;
static emit(event: string, ...args: unknown[]): boolean {
return this.emitter.emit(event, ...args);
}
Expand Down
9 changes: 9 additions & 0 deletions packages/compass/src/main/auto-update-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,15 @@ class CompassAutoUpdateManager {
this.setState(AutoUpdateManagerState.UserPromptedManualCheck);
});

this.on('new-state', (state: AutoUpdateManagerState) =>
compassApp.emit('auto-updater:new-state', state)
);

compassApp.on(
'menu-request-restart',
this.setState.bind(this, AutoUpdateManagerState.Restarting)
);

ipcMain?.on(
'autoupdate:update-download-restart-confirmed',
this.handleIpcUpdateDownloadRestartConfirmed.bind(this)
Expand Down
180 changes: 180 additions & 0 deletions packages/compass/src/main/menu.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import EventEmitter from 'events';
import type { MenuItemConstructorOptions } from 'electron';
import { BrowserWindow, ipcMain, Menu, app } from 'electron';
import { expect } from 'chai';
import sinon from 'sinon';
import { createSandboxFromDefaultPreferences } from 'compass-preferences-model';

import type { CompassApplication } from './application';
import type { CompassMenu as _CompassMenu } from './menu';
import { AutoUpdateManagerState } from './auto-update-manager';

function serializable<T>(obj: T): T {
try {
Expand Down Expand Up @@ -45,6 +47,7 @@ describe('CompassMenu', function () {
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: false,
isReadOnly: false,
updateManagerState: 'idle',
});
});

Expand Down Expand Up @@ -81,11 +84,13 @@ describe('CompassMenu', function () {
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: true,
isReadOnly: false,
updateManagerState: 'idle',
});
ipcMain.emit('window:hide-collection-submenu', { sender: bw.webContents });
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: false,
isReadOnly: false,
updateManagerState: 'idle',
});
ipcMain.emit(
'window:show-collection-submenu',
Expand All @@ -95,6 +100,33 @@ describe('CompassMenu', function () {
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: true,
isReadOnly: true,
updateManagerState: 'idle',
});
});

it('should change window state when window emits update-manager:new-state event', function () {
const bw = new BrowserWindow({ show: false });
App.emit('new-window', bw);
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: false,
isReadOnly: false,
updateManagerState: 'idle',
});
App.emit('auto-updater:new-state', AutoUpdateManagerState.PromptForRestart);
expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: false,
isReadOnly: false,
updateManagerState: 'ready to restart',
});
App.emit(
'auto-updater:new-state',
AutoUpdateManagerState.DownloadingUpdate
);

expect(CompassMenu['windowState'].get(bw.id)).to.deep.eq({
showCollection: false,
isReadOnly: false,
updateManagerState: 'installing updates',
});
});

Expand All @@ -111,6 +143,152 @@ describe('CompassMenu', function () {
}).not.to.throw();
});

describe('manual update menu item', function () {
const bw = new BrowserWindow({ show: false });
beforeEach(() => {
App.emit('new-window', bw);
});
describe('darwin', () => {
beforeEach(function () {
if (process.platform !== 'darwin') this.skip();
});
describe('when the auto updater is in an idle state', () => {
it('displays `Checking for updates...` in the menu', () => {
const idleStates = [
AutoUpdateManagerState.Initial,
AutoUpdateManagerState.Disabled,
AutoUpdateManagerState.UserPromptedManualCheck,
AutoUpdateManagerState.CheckingForUpdatesForManualCheck,
AutoUpdateManagerState.CheckingForUpdatesForAutomaticCheck,
AutoUpdateManagerState.NoUpdateAvailable,
AutoUpdateManagerState.UpdateAvailable,
AutoUpdateManagerState.UpdateDismissed,
AutoUpdateManagerState.DownloadingError,
AutoUpdateManagerState.Restarting,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[0].submenu as any
)?.[1] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Check for updates…');
}
});
});

describe('when the auto updater is in an downloading an update', () => {
it('displays `Installing update...` in the menu', () => {
sinon.stub(process, 'platform').value('darwin');

const idleStates = [
AutoUpdateManagerState.ManualDownload,
AutoUpdateManagerState.DownloadingUpdate,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[0].submenu as any
)?.[1] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Installing updates…');
expect(updateItem.enabled).to.be.false;
}
});
});

describe('when the auto updater is in ready for restart', () => {
it('displays `Restart` in the menu', () => {
sinon.stub(process, 'platform').value('darwin');

const idleStates = [
AutoUpdateManagerState.PromptForRestart,
AutoUpdateManagerState.RestartDismissed,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[0].submenu as any
)?.[1] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Restart');
}
});
});
});

for (const platform of ['linux', 'win32']) {
describe(platform, () => {
beforeEach(function () {
if (process.platform !== platform) this.skip();
});
describe('when the auto updater is in an idle state', () => {
it('displays `Checking for updates...` in the menu', () => {
const idleStates = [
AutoUpdateManagerState.Initial,
AutoUpdateManagerState.Disabled,
AutoUpdateManagerState.UserPromptedManualCheck,
AutoUpdateManagerState.CheckingForUpdatesForManualCheck,
AutoUpdateManagerState.CheckingForUpdatesForAutomaticCheck,
AutoUpdateManagerState.NoUpdateAvailable,
AutoUpdateManagerState.UpdateAvailable,
AutoUpdateManagerState.UpdateDismissed,
AutoUpdateManagerState.DownloadingError,
AutoUpdateManagerState.Restarting,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[3].submenu as any
)?.[8] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Check for updates…');
}
});
});
});

describe('when the auto updater is in an downloading an update', () => {
it('displays `Installing update...` in the menu', () => {
sinon.stub(process, 'platform').value(platform);

const idleStates = [
AutoUpdateManagerState.ManualDownload,
AutoUpdateManagerState.DownloadingUpdate,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[3].submenu as any
)?.[8] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Installing updates…');
expect(updateItem.enabled).to.be.false;
}
});
});

describe('when the auto updater is in ready for restart', () => {
it('displays `Restart` in the menu', () => {
sinon.stub(process, 'platform').value(platform);

const idleStates = [
AutoUpdateManagerState.PromptForRestart,
AutoUpdateManagerState.RestartDismissed,
];
for (const state of idleStates) {
App.emit('auto-updater:new-state', state);
const menu = serializable(CompassMenu.getTemplate(bw.id));
const updateItem = (
menu[3].submenu as any
)?.[8] as MenuItemConstructorOptions;
expect(updateItem.label).to.equal('Restart');
}
});
});
}
});

it('should generate a menu template for darwin', function () {
sinon.stub(process, 'platform').value('darwin');
expect(serializable(CompassMenu.getTemplate(0))).to.deep.equal([
Expand Down Expand Up @@ -275,6 +453,7 @@ describe('CompassMenu', function () {
CompassMenu['windowState'].set(0, {
showCollection: true,
isReadOnly: false,
updateManagerState: 'idle',
});
expect(
// Contains functions, so we can't easily deep equal it without
Expand Down Expand Up @@ -308,6 +487,7 @@ describe('CompassMenu', function () {
CompassMenu['windowState'].set(0, {
showCollection: true,
isReadOnly: true,
updateManagerState: 'idle',
});
expect(
// Contains functions, so we can't easily deep equal it without
Expand Down
Loading

0 comments on commit a385fa3

Please sign in to comment.