FN-6366: fix desktop Vitest constructor mocks
Stabilize the desktop Vitest suite under Vitest 4.1.8 while keeping dashboard builds complete. - Convert Electron constructor mocks to constructable function implementations for BrowserWindow, Tray, Notification, and LocalRuntimeManager.\n- Assert local runtime initialization uses the mocked home directory in desktop main-process tests.\n- Build dashboard runtime plugin packages before the desktop dashboard build. Files changed:\n packages/desktop/scripts/workspace-tools.ts | 15 ++++++++++++++\n packages/desktop/src/__tests__/deep-link.test.ts | 4 +++-\n .../desktop/src/__tests__/main-integration.test.ts | 23 ++++++++++++++--------\n .../desktop/src/__tests__/main-local-mode.test.ts | 19 ++++++++++++++----\n .../desktop/src/__tests__/main.integration.test.ts | 21 ++++++++++++--------\n packages/desktop/src/__tests__/main.test.ts | 13 +++++++++---\n packages/desktop/src/__tests__/native.test.ts | 6 ++++--\n 7 files changed, 75 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-6366 Fusion-Task-Lineage: 4e4ccdbc-845f-47d9-a565-cc5d659cf089
This commit is contained in:
@@ -45,8 +45,23 @@ export async function buildCore(): Promise<void> {
|
||||
await runWorkspaceBin("tsc", [], resolve(workspaceRoot, "packages", "core"));
|
||||
}
|
||||
|
||||
async function buildPackage(relativePath: string): Promise<void> {
|
||||
await runWorkspaceBin("tsc", [], resolve(workspaceRoot, relativePath));
|
||||
}
|
||||
|
||||
export async function buildDashboardRuntimePlugins(): Promise<void> {
|
||||
await buildPackage("packages/plugin-sdk");
|
||||
await Promise.all([
|
||||
buildPackage("plugins/fusion-plugin-dependency-graph"),
|
||||
buildPackage("plugins/fusion-plugin-hermes-runtime"),
|
||||
buildPackage("plugins/fusion-plugin-openclaw-runtime"),
|
||||
buildPackage("plugins/fusion-plugin-paperclip-runtime"),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function buildDashboard(): Promise<void> {
|
||||
const dashboardRoot = resolve(workspaceRoot, "packages", "dashboard");
|
||||
await buildDashboardRuntimePlugins();
|
||||
await runWorkspaceBin("vite", ["build"], dashboardRoot);
|
||||
await runWorkspaceBin("tsc", [], dashboardRoot);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ const mocks = vi.hoisted(() => {
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
app: mocks.app,
|
||||
BrowserWindow: vi.fn(() => mocks.browserWindow),
|
||||
BrowserWindow: vi.fn(function () {
|
||||
return mocks.browserWindow;
|
||||
}),
|
||||
}));
|
||||
|
||||
async function importDeepLinkModule() {
|
||||
|
||||
@@ -44,6 +44,7 @@ const mocks = vi.hoisted(() => {
|
||||
|
||||
const app = {
|
||||
whenReady: vi.fn(() => Promise.resolve()),
|
||||
getPath: vi.fn((name: string) => (name === "home" ? "/mock/home" : "/mock/other")),
|
||||
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
|
||||
appEvents.set(event, handler);
|
||||
}),
|
||||
@@ -51,14 +52,14 @@ const mocks = vi.hoisted(() => {
|
||||
isQuitting: false,
|
||||
};
|
||||
|
||||
const BrowserWindow = vi.fn((options: Record<string, unknown>) => {
|
||||
const BrowserWindow = vi.fn(function (options: Record<string, unknown>) {
|
||||
callLog.push("createMainWindow");
|
||||
const instance = createWindowMock();
|
||||
windowInstances.push({ instance, options });
|
||||
return instance;
|
||||
});
|
||||
|
||||
const Tray = vi.fn(() => {
|
||||
const Tray = vi.fn(function () {
|
||||
const tray = createTrayMock();
|
||||
trayInstances.push(tray);
|
||||
return tray;
|
||||
@@ -117,6 +118,14 @@ const mocks = vi.hoisted(() => {
|
||||
const getStatus = vi.fn(() => ({ source: "none", state: "stopped" }));
|
||||
|
||||
const saveWindowState = vi.fn();
|
||||
const LocalRuntimeManager = vi.fn(function () {
|
||||
return {
|
||||
startLocal,
|
||||
stopLocal,
|
||||
getStatus,
|
||||
getServerPort: vi.fn(() => 0),
|
||||
};
|
||||
});
|
||||
|
||||
const DEFAULT_WINDOW_STATE = {
|
||||
width: 1280,
|
||||
@@ -149,6 +158,7 @@ const mocks = vi.hoisted(() => {
|
||||
startLocal,
|
||||
stopLocal,
|
||||
getStatus,
|
||||
LocalRuntimeManager,
|
||||
DEFAULT_WINDOW_STATE,
|
||||
};
|
||||
});
|
||||
@@ -195,12 +205,7 @@ vi.mock("../native.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../local-runtime.js", () => ({
|
||||
LocalRuntimeManager: vi.fn(() => ({
|
||||
startLocal: mocks.startLocal,
|
||||
stopLocal: mocks.stopLocal,
|
||||
getStatus: mocks.getStatus,
|
||||
getServerPort: vi.fn(() => 0),
|
||||
})),
|
||||
LocalRuntimeManager: mocks.LocalRuntimeManager,
|
||||
}));
|
||||
|
||||
// Mock renderer module
|
||||
@@ -268,6 +273,8 @@ describe("main integration", () => {
|
||||
"setupAutoUpdater",
|
||||
"startUpdateCheckInterval",
|
||||
]);
|
||||
expect(mocks.LocalRuntimeManager).toHaveBeenCalledWith({ rootDir: "/mock/home" });
|
||||
expect(mocks.app.getPath).toHaveBeenCalledWith("home");
|
||||
});
|
||||
|
||||
it("createMainWindow uses restored window state", async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ const mocks = vi.hoisted(() => {
|
||||
const appHandlers = new Map<string, (...args: unknown[]) => void>();
|
||||
const app = {
|
||||
whenReady: vi.fn(async () => undefined),
|
||||
getPath: vi.fn((name: string) => (name === "home" ? "/mock/home" : "/mock/other")),
|
||||
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
|
||||
appHandlers.set(event, handler);
|
||||
return app;
|
||||
@@ -26,8 +27,12 @@ const mocks = vi.hoisted(() => {
|
||||
webContents: { send: vi.fn() },
|
||||
};
|
||||
|
||||
const BrowserWindow = vi.fn(() => browserWindow);
|
||||
const Tray = vi.fn(() => ({ destroy: vi.fn() }));
|
||||
const BrowserWindow = vi.fn(function () {
|
||||
return browserWindow;
|
||||
});
|
||||
const Tray = vi.fn(function () {
|
||||
return { destroy: vi.fn() };
|
||||
});
|
||||
|
||||
const localRuntimeManager = {
|
||||
startLocal: vi.fn(async () => ({ source: "embedded-local", state: "running", port: 4041 })),
|
||||
@@ -40,7 +45,11 @@ const mocks = vi.hoisted(() => {
|
||||
getAllDisplays: vi.fn(() => [{ workArea: { x: 0, y: 0, width: 1920, height: 1080 } }]),
|
||||
};
|
||||
|
||||
return { app, appHandlers, BrowserWindow, Tray, browserWindow, localRuntimeManager, screen };
|
||||
const LocalRuntimeManager = vi.fn(function () {
|
||||
return localRuntimeManager;
|
||||
});
|
||||
|
||||
return { app, appHandlers, BrowserWindow, Tray, browserWindow, localRuntimeManager, LocalRuntimeManager, screen };
|
||||
});
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
@@ -66,7 +75,7 @@ vi.mock("../native.js", () => ({
|
||||
clampWindowStateToVisibleDisplay: vi.fn((state) => state),
|
||||
}));
|
||||
vi.mock("../deep-link.js", () => ({ registerDeepLinkProtocol: vi.fn(), setupDeepLinkHandler: vi.fn() }));
|
||||
vi.mock("../local-runtime.js", () => ({ LocalRuntimeManager: vi.fn(() => mocks.localRuntimeManager) }));
|
||||
vi.mock("../local-runtime.js", () => ({ LocalRuntimeManager: mocks.LocalRuntimeManager }));
|
||||
|
||||
describe("main local mode", () => {
|
||||
beforeEach(() => {
|
||||
@@ -81,6 +90,8 @@ describe("main local mode", () => {
|
||||
const { initializeApp } = await import("../main.ts");
|
||||
await initializeApp();
|
||||
|
||||
expect(mocks.LocalRuntimeManager).toHaveBeenCalledWith({ rootDir: "/mock/home" });
|
||||
expect(mocks.app.getPath).toHaveBeenCalledWith("home");
|
||||
expect(mocks.localRuntimeManager.startLocal).toHaveBeenCalled();
|
||||
delete process.env.FUSION_DESKTOP_MODE;
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
const mocks = vi.hoisted(() => {
|
||||
const app = {
|
||||
whenReady: vi.fn(() => Promise.resolve()),
|
||||
getPath: vi.fn((name: string) => (name === "home" ? "/mock/home" : "/mock/other")),
|
||||
on: vi.fn(),
|
||||
quit: vi.fn(),
|
||||
};
|
||||
@@ -20,14 +21,18 @@ const mocks = vi.hoisted(() => {
|
||||
|
||||
return {
|
||||
app,
|
||||
BrowserWindow: vi.fn(() => browserWindow),
|
||||
Tray: vi.fn(() => ({
|
||||
destroy: vi.fn(),
|
||||
setImage: vi.fn(),
|
||||
setContextMenu: vi.fn(),
|
||||
setToolTip: vi.fn(),
|
||||
on: vi.fn(),
|
||||
})),
|
||||
BrowserWindow: vi.fn(function () {
|
||||
return browserWindow;
|
||||
}),
|
||||
Tray: vi.fn(function () {
|
||||
return {
|
||||
destroy: vi.fn(),
|
||||
setImage: vi.fn(),
|
||||
setContextMenu: vi.fn(),
|
||||
setToolTip: vi.fn(),
|
||||
on: vi.fn(),
|
||||
};
|
||||
}),
|
||||
nativeImage: {
|
||||
createEmpty: vi.fn(() => ({ id: "empty-image" })),
|
||||
},
|
||||
|
||||
@@ -34,7 +34,9 @@ const mocks = vi.hoisted(() => {
|
||||
maximize: vi.fn(),
|
||||
};
|
||||
|
||||
const BrowserWindow = vi.fn(() => browserWindowInstance) as unknown as {
|
||||
const BrowserWindow = vi.fn(function () {
|
||||
return browserWindowInstance;
|
||||
}) as unknown as {
|
||||
(...args: unknown[]): typeof browserWindowInstance;
|
||||
getAllWindows: () => unknown[];
|
||||
};
|
||||
@@ -60,7 +62,9 @@ const mocks = vi.hoisted(() => {
|
||||
on: vi.fn(),
|
||||
};
|
||||
|
||||
const Tray = vi.fn(() => trayInstance);
|
||||
const Tray = vi.fn(function () {
|
||||
return trayInstance;
|
||||
});
|
||||
const Menu = {
|
||||
buildFromTemplate: vi.fn(() => ({ id: "mock-menu" })),
|
||||
setApplicationMenu: vi.fn(),
|
||||
@@ -133,7 +137,9 @@ const mainDeps = vi.hoisted(() => {
|
||||
loadDesktopLaunchMode,
|
||||
saveDesktopLaunchMode,
|
||||
saveWindowState: vi.fn(),
|
||||
LocalRuntimeManager: vi.fn(() => ({ startLocal, stopLocal, getStatus, getServerPort })),
|
||||
LocalRuntimeManager: vi.fn(function () {
|
||||
return { startLocal, stopLocal, getStatus, getServerPort };
|
||||
}),
|
||||
startLocal,
|
||||
};
|
||||
});
|
||||
@@ -301,6 +307,7 @@ describe("main process", () => {
|
||||
|
||||
await initializeApp();
|
||||
|
||||
expect(mainDeps.LocalRuntimeManager).toHaveBeenCalledWith({ rootDir: "/mock/home" });
|
||||
expect(mainDeps.startLocal).toHaveBeenCalledTimes(1);
|
||||
expect(getCurrentDesktopLaunchMode()).toBe("local");
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const mocks = vi.hoisted(() => {
|
||||
options: Record<string, unknown>;
|
||||
}> = [];
|
||||
|
||||
const Notification = vi.fn().mockImplementation((options: Record<string, unknown>) => {
|
||||
const Notification = vi.fn().mockImplementation(function (options: Record<string, unknown>) {
|
||||
const listeners = new Map<string, () => void>();
|
||||
const instance = {
|
||||
show: vi.fn(),
|
||||
@@ -92,7 +92,9 @@ vi.mock("electron", () => ({
|
||||
app: mocks.app,
|
||||
dialog: mocks.dialog,
|
||||
Notification: mocks.Notification,
|
||||
BrowserWindow: vi.fn(() => mocks.browserWindow),
|
||||
BrowserWindow: vi.fn(function () {
|
||||
return mocks.browserWindow;
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("electron-updater", () => ({
|
||||
|
||||
Reference in New Issue
Block a user