Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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