Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/main/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ describe('main/config.ts', () => {
expect(WindowConfig.resizable).toBe(false);
expect(WindowConfig.skipTaskbar).toBe(true);
expect(WindowConfig.webPreferences).toBeDefined();
expect(WindowConfig.webPreferences.contextIsolation).toBe(true);
expect(WindowConfig.webPreferences.nodeIntegration).toBe(false);
expect(WindowConfig.webPreferences.backgroundThrottling).toBe(false);
expect(WindowConfig.webPreferences?.contextIsolation).toBe(true);
expect(WindowConfig.webPreferences?.nodeIntegration).toBe(false);
expect(WindowConfig.webPreferences?.backgroundThrottling).toBe(false);
});
});
16 changes: 10 additions & 6 deletions src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import type { EventData, EventType } from '../shared/events';
* @param event - The IPC channel/event name to listen on.
* @param listener - Callback invoked when the event is received.
*/
export function onMainEvent(
export function onMainEvent<T = EventData>(
event: EventType,
listener: (event: Electron.IpcMainEvent, args: EventData) => void,
listener: (event: Electron.IpcMainEvent, args: T) => void,
) {
ipcMain.on(event, listener);
ipcMain.on(event, listener as Parameters<typeof ipcMain.on>[1]);
}

/**
Expand All @@ -24,14 +24,14 @@ export function onMainEvent(
* @param event - The IPC channel/event name to handle.
* @param listener - Callback whose return value is sent back to the renderer.
*/
export function handleMainEvent(
export function handleMainEvent<T = EventData>(
event: EventType,
listener: (
event: Electron.IpcMainInvokeEvent,
data: EventData,
data: T,
) => unknown | Promise<unknown>,
) {
ipcMain.handle(event, listener);
ipcMain.handle(event, listener as Parameters<typeof ipcMain.handle>[1]);
}

/**
Expand All @@ -46,5 +46,9 @@ export function sendRendererEvent(
event: EventType,
data?: string,
) {
if (!mb.window) {
return;
}

mb.window.webContents.send(event, data);
}
4 changes: 2 additions & 2 deletions src/main/handlers/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ describe('main/handlers/app.ts', () => {
registerAppHandlers(menubar);

const registeredHandlers = handleMock.mock.calls.map(
(call: [string]) => call[0],
(call: unknown[]) => call[0],
);
const registeredEvents = onMock.mock.calls.map(
(call: [string]) => call[0],
(call: unknown[]) => call[0],
);

expect(registeredHandlers).toContain(EVENTS.VERSION);
Expand Down
12 changes: 8 additions & 4 deletions src/main/handlers/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ vi.mock('electron', () => ({
}));

const logErrorMock = vi.fn();
vi.mock('../../shared/logger', () => ({
logError: (...args: unknown[]) => logErrorMock(...args),
}));
vi.mock('../../shared/logger', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../shared/logger')>();
return {
...actual,
logError: (...args: unknown[]) => logErrorMock(...args),
};
});

describe('main/handlers/storage.ts', () => {
describe('registerStorageHandlers', () => {
Expand All @@ -29,7 +33,7 @@ describe('main/handlers/storage.ts', () => {
registerStorageHandlers();

const registeredHandlers = handleMock.mock.calls.map(
(call: [string]) => call[0],
(call: unknown[]) => call[0],
);

expect(registeredHandlers).toContain(EVENTS.SAFE_STORAGE_ENCRYPT);
Expand Down
4 changes: 2 additions & 2 deletions src/main/handlers/storage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { safeStorage } from 'electron';

import { EVENTS } from '../../shared/events';
import { logError } from '../../shared/logger';
import { logError, toError } from '../../shared/logger';

import { handleMainEvent } from '../events';

Expand All @@ -26,7 +26,7 @@ export function registerStorageHandlers(): void {
logError(
'main:safe-storage-decrypt',
'Failed to decrypt value - data may be from old build',
err,
toError(err),
);
throw err;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/handlers/system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ describe('main/handlers/system.ts', () => {
it('registers expected system IPC event handlers', () => {
registerSystemHandlers(menubar);

const onEvents = onMock.mock.calls.map((call: [string]) => call[0]);
const onEvents = onMock.mock.calls.map((call: unknown[]) => call[0]);
const handleEvents = handleMock.mock.calls.map(
(call: [string]) => call[0],
(call: unknown[]) => call[0],
);

expect(onEvents).toContain(EVENTS.OPEN_EXTERNAL);
Expand Down
4 changes: 4 additions & 0 deletions src/main/handlers/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export function registerSystemHandlers(mb: Menubar): void {
let lastRegisteredAccelerator: string | null = null;

const toggleWindow = () => {
if (!mb.window) {
return;
}

if (mb.window.isVisible()) {
mb.hideWindow();
} else {
Expand Down
12 changes: 6 additions & 6 deletions src/main/handlers/tray.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const registeredEvents = onMock.mock.calls.map(
(call: [string]) => call[0],
(call: unknown[]) => call[0],
);

expect(registeredEvents).toContain(EVENTS.USE_ALTERNATE_IDLE_ICON);
Expand All @@ -55,7 +55,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const updateColorHandler = onMock.mock.calls.find(
(call: [string]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
(call: unknown[]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
)?.[1];
updateColorHandler?.({}, 5);

Expand All @@ -66,7 +66,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const updateColorHandler = onMock.mock.calls.find(
(call: [string]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
(call: unknown[]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
)?.[1];
updateColorHandler?.({}, 0);

Expand All @@ -77,7 +77,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const updateColorHandler = onMock.mock.calls.find(
(call: [string]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
(call: unknown[]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
)?.[1];
updateColorHandler?.({}, 3);

Expand All @@ -88,7 +88,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const updateColorHandler = onMock.mock.calls.find(
(call: [string]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
(call: unknown[]) => call[0] === EVENTS.UPDATE_ICON_COLOR,
)?.[1];
updateColorHandler?.({}, -1);

Expand All @@ -99,7 +99,7 @@ describe('main/handlers/tray.ts', () => {
registerTrayHandlers(menubar);

const updateTitleHandler = onMock.mock.calls.find(
(call: [string]) => call[0] === EVENTS.UPDATE_ICON_TITLE,
(call: unknown[]) => call[0] === EVENTS.UPDATE_ICON_TITLE,
)?.[1];
updateTitleHandler?.({}, '5');

Expand Down
10 changes: 7 additions & 3 deletions src/main/lifecycle/first-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ vi.mock('electron', () => ({
vi.mock('../utils', () => ({ isDevMode: () => false }));

const logErrorMock = vi.fn();
vi.mock('../../shared/logger', () => ({
logError: (...a: unknown[]) => logErrorMock(...a),
}));
vi.mock('../../shared/logger', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../shared/logger')>();
return {
...actual,
logError: (...a: unknown[]) => logErrorMock(...a),
};
});

let mac = true;
vi.mock('../../shared/platform', () => ({ isMacOS: () => mac }));
Expand Down
8 changes: 6 additions & 2 deletions src/main/lifecycle/first-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'node:path';
import { app, dialog } from 'electron';

import { APPLICATION } from '../../shared/constants';
import { logError } from '../../shared/logger';
import { logError, toError } from '../../shared/logger';
import { isMacOS } from '../../shared/platform';

import { isDevMode } from '../utils';
Expand Down Expand Up @@ -71,7 +71,11 @@ function checkAndMarkFirstRun(): boolean {

fs.writeFileSync(configPath, '');
} catch (err) {
logError('checkAndMarkFirstRun', 'Unable to write firstRun file', err);
logError(
'checkAndMarkFirstRun',
'Unable to write firstRun file',
toError(err),
);
}

return true;
Expand Down
4 changes: 4 additions & 0 deletions src/main/lifecycle/reset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import { sendRendererEvent } from '../events';
* @param mb - The menubar instance used for the dialog parent window and quit.
*/
export function resetApp(mb: Menubar): void {
if (!mb.window) {
return;
}

const cancelButtonId = 0;
const resetButtonId = 1;

Expand Down
10 changes: 6 additions & 4 deletions src/main/lifecycle/window.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,23 @@ describe('main/lifecycle/window.ts', () => {
it('configureWindowEvents returns early if no window', () => {
const mbNoWindow = { ...menubar, window: null };

expect(() => configureWindowEvents(mbNoWindow as Menubar)).not.toThrow();
expect(() =>
configureWindowEvents(mbNoWindow as unknown as Menubar),
).not.toThrow();
});

it('configureWindowEvents registers webContents event listeners', () => {
configureWindowEvents(menubar);

expect(menubar.window.webContents.on).toHaveBeenCalledWith(
expect(menubar.window?.webContents.on).toHaveBeenCalledWith(
'before-input-event',
expect.any(Function),
);
expect(menubar.window.webContents.on).toHaveBeenCalledWith(
expect(menubar.window?.webContents.on).toHaveBeenCalledWith(
'devtools-opened',
expect.any(Function),
);
expect(menubar.window.webContents.on).toHaveBeenCalledWith(
expect(menubar.window?.webContents.on).toHaveBeenCalledWith(
'devtools-closed',
expect.any(Function),
);
Expand Down
10 changes: 9 additions & 1 deletion src/main/lifecycle/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export function configureWindowEvents(mb: Menubar): void {
* When DevTools is opened, resize and center the window for better visibility and allow resizing.
*/
mb.window.webContents.on('devtools-opened', () => {
if (!mb.window) {
return;
}

mb.window.setSize(800, 600);
mb.window.center();
mb.window.resizable = true;
Expand All @@ -36,8 +40,12 @@ export function configureWindowEvents(mb: Menubar): void {
* When DevTools is closed, restore the window to its original size and position it centered on the tray icon.
*/
mb.window.webContents.on('devtools-closed', () => {
if (!mb.window) {
return;
}

const trayBounds = mb.tray.getBounds();
mb.window.setSize(WindowConfig.width, WindowConfig.height);
mb.window.setSize(WindowConfig.width!, WindowConfig.height!);
mb.positioner.move('trayCenter', trayBounds);
mb.window.resizable = false;
});
Expand Down
14 changes: 8 additions & 6 deletions src/main/menu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe('main/menu.ts', () => {
const cfg = getMenuItemConfigByLabel('Check for updates');
expect(cfg).toBeDefined();

cfg.click();
cfg?.click?.();

expect(autoUpdater.checkForUpdatesAndNotify).toHaveBeenCalled();
});
Expand All @@ -209,7 +209,7 @@ describe('main/menu.ts', () => {
const cfg = getMenuItemConfigByLabel('Restart to install update');
expect(cfg).toBeDefined();

cfg.click();
cfg?.click?.();

expect(autoUpdater.quitAndInstall).toHaveBeenCalled();
});
Expand All @@ -220,7 +220,7 @@ describe('main/menu.ts', () => {
(item) => item?.label === 'Developer',
) as TemplateItem;
expect(devEntry).toBeDefined();
const submenu = devEntry.submenu;
const submenu = devEntry.submenu ?? [];
const clickByLabel = (label: string) =>
submenu.find((i) => i.label === label)?.click?.();

Expand All @@ -242,15 +242,15 @@ describe('main/menu.ts', () => {
it('website menu item opens external URL', () => {
const template = buildAndGetTemplate();
const item = template.find((i) => i.label === 'Visit Website');
item.click();
item?.click?.();
expect(shell.openExternal).toHaveBeenCalledWith(APPLICATION.WEBSITE);
});

it('quit menu item quits the app', () => {
const template = buildAndGetTemplate();
const item = template.find((i) => i.label === `Quit ${APPLICATION.NAME}`);

item.click();
item?.click?.();

expect(menubar.app.quit).toHaveBeenCalled();
});
Expand All @@ -260,7 +260,9 @@ describe('main/menu.ts', () => {
const devEntry = template.find(
(item) => item?.label === 'Developer',
) as TemplateItem;
const reloadItem = devEntry.submenu.find((i) => i.role === 'reload');
const reloadItem = (devEntry.submenu ?? []).find(
(i) => i.role === 'reload',
);

expect(reloadItem?.accelerator).toBe('CommandOrControl+R');
});
Expand Down
12 changes: 8 additions & 4 deletions src/main/updater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import type { Menubar } from 'menubar';
import { APPLICATION } from '../shared/constants';
import { logError, logInfo } from '../shared/logger';

vi.mock('../shared/logger', () => ({
logInfo: vi.fn(),
logError: vi.fn(),
}));
vi.mock('../shared/logger', async (importOriginal) => {
const actual = await importOriginal<typeof import('../shared/logger')>();
return {
...actual,
logInfo: vi.fn(),
logError: vi.fn(),
};
});

import MenuBuilder from './menu';
import AppUpdater from './updater';
Expand Down
Loading
Loading