243 lines
7.0 KiB
TypeScript
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();
|
|
});
|
|
});
|