Files
fusion/packages/desktop/src/__tests__/ipc.test.ts

243 lines
7.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => {
const ipcHandlers = new Map<string, (...args: unknown[]) => unknown>();
const ipcMain = {
handle: vi.fn((channel: string, handler: (...args: unknown[]) => unknown) => {
ipcHandlers.set(channel, handler);
}),
};
const app = {
getVersion: vi.fn(() => "1.2.3"),
};
const updateTrayStatus = vi.fn();
const showExportSettingsDialog = vi.fn();
const showImportSettingsDialog = vi.fn();
const setupAutoUpdater = vi.fn();
return {
ipcMain,
ipcHandlers,
app,
updateTrayStatus,
showExportSettingsDialog,
showImportSettingsDialog,
setupAutoUpdater,
};
});
vi.mock("electron", () => ({
ipcMain: mocks.ipcMain,
app: mocks.app,
}));
vi.mock("../tray.js", () => ({
updateTrayStatus: mocks.updateTrayStatus,
}));
vi.mock("../native.js", () => ({
showExportSettingsDialog: mocks.showExportSettingsDialog,
showImportSettingsDialog: mocks.showImportSettingsDialog,
setupAutoUpdater: mocks.setupAutoUpdater,
}));
function createWindowMock() {
return {
minimize: vi.fn(),
maximize: vi.fn(),
unmaximize: vi.fn(),
close: vi.fn(),
isMaximized: vi.fn(() => false),
};
}
function createTrayMock() {
return {
setToolTip: vi.fn(),
setContextMenu: vi.fn(),
};
}
async function registerHandlers() {
const { registerIpcHandlers } = await import("../ipc.ts");
const window = createWindowMock();
const tray = createTrayMock();
registerIpcHandlers(window as never, tray as never);
return { window, tray };
}
describe("ipc handlers", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
mocks.ipcHandlers.clear();
mocks.app.getVersion.mockReturnValue("1.2.3");
mocks.setupAutoUpdater.mockImplementation(() => undefined);
mocks.showExportSettingsDialog.mockResolvedValue(null);
mocks.showImportSettingsDialog.mockResolvedValue(null);
});
it("registers all expected channels", async () => {
await registerHandlers();
const channels = new Set(mocks.ipcMain.handle.mock.calls.map(([channel]) => channel));
expect(channels).toEqual(new Set([
"window:minimize",
"window:maximize",
"window:close",
"window:isMaximized",
"app:getSystemInfo",
"app:checkForUpdates",
"app:getServerPort",
"tray:updateStatus",
"native:showExportDialog",
"native:showImportDialog",
]));
});
it("window:minimize calls mainWindow.minimize", async () => {
const { window } = await registerHandlers();
const handler = mocks.ipcHandlers.get("window:minimize");
await handler?.({});
expect(window.minimize).toHaveBeenCalledTimes(1);
});
it("window:maximize maximizes when currently unmaximized", async () => {
const { window } = await registerHandlers();
window.isMaximized.mockReturnValue(false);
const handler = mocks.ipcHandlers.get("window:maximize");
const result = await handler?.({});
expect(window.maximize).toHaveBeenCalledTimes(1);
expect(window.unmaximize).not.toHaveBeenCalled();
expect(result).toBe(true);
});
it("window:maximize restores when currently maximized", async () => {
const { window } = await registerHandlers();
window.isMaximized.mockReturnValue(true);
const handler = mocks.ipcHandlers.get("window:maximize");
const result = await handler?.({});
expect(window.unmaximize).toHaveBeenCalledTimes(1);
expect(window.maximize).not.toHaveBeenCalled();
expect(result).toBe(false);
});
it("window:close calls mainWindow.close", async () => {
const { window } = await registerHandlers();
const handler = mocks.ipcHandlers.get("window:close");
await handler?.({});
expect(window.close).toHaveBeenCalledTimes(1);
});
it("window:isMaximized returns current maximized state", async () => {
const { window } = await registerHandlers();
window.isMaximized.mockReturnValue(true);
const handler = mocks.ipcHandlers.get("window:isMaximized");
const result = await handler?.({});
expect(result).toBe(true);
});
it("app:getSystemInfo returns process and app metadata", async () => {
await registerHandlers();
const handler = mocks.ipcHandlers.get("app:getSystemInfo");
const result = await handler?.({});
expect(result).toEqual({
platform: process.platform,
arch: process.arch,
electronVersion: process.versions.electron,
nodeVersion: process.versions.node,
appVersion: "1.2.3",
});
});
it("app:checkForUpdates calls setupAutoUpdater and returns checking", async () => {
const { window } = await registerHandlers();
const handler = mocks.ipcHandlers.get("app:checkForUpdates");
const result = await handler?.({});
expect(mocks.setupAutoUpdater).toHaveBeenCalledWith(window);
expect(result).toEqual({ status: "checking" });
});
it("app:checkForUpdates returns error when updater throws", async () => {
await registerHandlers();
mocks.setupAutoUpdater.mockImplementationOnce(() => {
throw new Error("updater failed");
});
const handler = mocks.ipcHandlers.get("app:checkForUpdates");
const result = await handler?.({});
expect(result).toEqual({ status: "error", error: "updater failed" });
});
it("native:showExportDialog calls showExportSettingsDialog with mainWindow", async () => {
const { window } = await registerHandlers();
mocks.showExportSettingsDialog.mockResolvedValueOnce("/path/to/file.json");
const handler = mocks.ipcHandlers.get("native:showExportDialog");
const result = await handler?.({});
expect(mocks.showExportSettingsDialog).toHaveBeenCalledWith(window);
expect(result).toBe("/path/to/file.json");
});
it("native:showImportDialog calls showImportSettingsDialog with mainWindow", async () => {
const { window } = await registerHandlers();
mocks.showImportSettingsDialog.mockResolvedValueOnce(null);
const handler = mocks.ipcHandlers.get("native:showImportDialog");
const result = await handler?.({});
expect(mocks.showImportSettingsDialog).toHaveBeenCalledWith(window);
expect(result).toBeNull();
});
it("tray:updateStatus forwards status and tray instance", async () => {
const { tray } = await registerHandlers();
const handler = mocks.ipcHandlers.get("tray:updateStatus");
await handler?.({}, "paused");
expect(mocks.updateTrayStatus).toHaveBeenCalledWith(tray, "paused");
});
it("app:getServerPort returns port from environment", async () => {
process.env.FUSION_SERVER_PORT = "4545";
await registerHandlers();
const handler = mocks.ipcHandlers.get("app:getServerPort");
const result = await handler?.({});
expect(result).toBe(4545);
delete process.env.FUSION_SERVER_PORT;
});
it("app:getServerPort returns undefined when env var not set", async () => {
delete process.env.FUSION_SERVER_PORT;
await registerHandlers();
const handler = mocks.ipcHandlers.get("app:getServerPort");
const result = await handler?.({});
expect(result).toBeUndefined();
});
});