diff --git a/packages/desktop/scripts/workspace-tools.ts b/packages/desktop/scripts/workspace-tools.ts index 585c6af50a..7b948524ef 100644 --- a/packages/desktop/scripts/workspace-tools.ts +++ b/packages/desktop/scripts/workspace-tools.ts @@ -45,8 +45,23 @@ export async function buildCore(): Promise { await runWorkspaceBin("tsc", [], resolve(workspaceRoot, "packages", "core")); } +async function buildPackage(relativePath: string): Promise { + await runWorkspaceBin("tsc", [], resolve(workspaceRoot, relativePath)); +} + +export async function buildDashboardRuntimePlugins(): Promise { + 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 { const dashboardRoot = resolve(workspaceRoot, "packages", "dashboard"); + await buildDashboardRuntimePlugins(); await runWorkspaceBin("vite", ["build"], dashboardRoot); await runWorkspaceBin("tsc", [], dashboardRoot); } diff --git a/packages/desktop/src/__tests__/deep-link.test.ts b/packages/desktop/src/__tests__/deep-link.test.ts index e1d7b19136..bd3bc0a107 100644 --- a/packages/desktop/src/__tests__/deep-link.test.ts +++ b/packages/desktop/src/__tests__/deep-link.test.ts @@ -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() { diff --git a/packages/desktop/src/__tests__/main-integration.test.ts b/packages/desktop/src/__tests__/main-integration.test.ts index 78ff36f449..945f6fdc51 100644 --- a/packages/desktop/src/__tests__/main-integration.test.ts +++ b/packages/desktop/src/__tests__/main-integration.test.ts @@ -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) => { + const BrowserWindow = vi.fn(function (options: Record) { 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 () => { diff --git a/packages/desktop/src/__tests__/main-local-mode.test.ts b/packages/desktop/src/__tests__/main-local-mode.test.ts index 91535f665d..6652046ff1 100644 --- a/packages/desktop/src/__tests__/main-local-mode.test.ts +++ b/packages/desktop/src/__tests__/main-local-mode.test.ts @@ -4,6 +4,7 @@ const mocks = vi.hoisted(() => { const appHandlers = new Map 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; }); diff --git a/packages/desktop/src/__tests__/main.integration.test.ts b/packages/desktop/src/__tests__/main.integration.test.ts index 9295c30940..ad1335b75e 100644 --- a/packages/desktop/src/__tests__/main.integration.test.ts +++ b/packages/desktop/src/__tests__/main.integration.test.ts @@ -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" })), }, diff --git a/packages/desktop/src/__tests__/main.test.ts b/packages/desktop/src/__tests__/main.test.ts index 9940f6bdc5..f1cc7c20cc 100644 --- a/packages/desktop/src/__tests__/main.test.ts +++ b/packages/desktop/src/__tests__/main.test.ts @@ -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"); }); diff --git a/packages/desktop/src/__tests__/native.test.ts b/packages/desktop/src/__tests__/native.test.ts index 9273220848..47ff30b809 100644 --- a/packages/desktop/src/__tests__/native.test.ts +++ b/packages/desktop/src/__tests__/native.test.ts @@ -24,7 +24,7 @@ const mocks = vi.hoisted(() => { options: Record; }> = []; - const Notification = vi.fn().mockImplementation((options: Record) => { + const Notification = vi.fn().mockImplementation(function (options: Record) { const listeners = new Map 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", () => ({