feat(FN-3398): update desktop README with multi-project setup and troublesh

The merge lands Step 7 of FN-3398, adding documentation and delivery artifacts across the mobile and desktop packages with updated READMEs, mobile-specific docs, and architecture references.

Fusion-Task-Id: FN-3398
This commit is contained in:
Fusion
2026-05-04 21:41:52 -07:00
committed by gsxdsm
parent 9699c3f2b6
commit 3feeb9026a
43 changed files with 2025 additions and 398 deletions

View File

@@ -23,26 +23,8 @@ async function importPreloadModule() {
await import("../preload.ts");
}
function getFusionApi() {
const call = mocks.contextBridge.exposeInMainWorld.mock.calls.find(
([name]) => name === "fusionAPI",
) as [string, {
minimize: () => Promise<void>;
maximize: () => Promise<boolean>;
close: () => Promise<void>;
isMaximized: () => Promise<boolean>;
getSystemInfo: () => Promise<unknown>;
checkForUpdates: () => Promise<unknown>;
getServerPort: () => Promise<number | undefined>;
updateTrayStatus: (status: string) => Promise<void>;
showExportDialog: () => Promise<string | null>;
showImportDialog: () => Promise<string | null>;
onDeepLink: (callback: (result: unknown) => void) => () => void;
onUpdateAvailable: (callback: (info: { version: string }) => void) => () => void;
onUpdateDownloaded: (callback: () => void) => () => void;
}] | undefined;
return call?.[1];
function getExposed<T = unknown>(name: string): T | undefined {
return mocks.contextBridge.exposeInMainWorld.mock.calls.find(([key]) => key === name)?.[1] as T | undefined;
}
describe("preload", () => {
@@ -51,153 +33,32 @@ describe("preload", () => {
vi.resetModules();
});
it("contextBridge.exposeInMainWorld is called with fusionAPI", async () => {
it("exposes electronAPI and fusionShell", async () => {
await importPreloadModule();
expect(mocks.contextBridge.exposeInMainWorld).toHaveBeenCalledWith(
"fusionAPI",
expect.any(Object),
);
expect(getExposed("electronAPI")).toBeTruthy();
expect(getExposed("fusionAPI")).toBeTruthy();
expect(getExposed("fusionShell")).toBeTruthy();
});
it("minimize invokes window:minimize", async () => {
it("electronAPI delegates getServerPort to IPC", async () => {
await importPreloadModule();
const api = getExposed<{ getServerPort: () => Promise<number | undefined> }>("electronAPI");
const api = getFusionApi();
await api?.minimize();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("window:minimize");
});
it("maximize invokes window:maximize", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.maximize();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("window:maximize");
});
it("close invokes window:close", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.close();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("window:close");
});
it("isMaximized invokes window:isMaximized", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.isMaximized();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("window:isMaximized");
});
it("getSystemInfo invokes app:getSystemInfo", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.getSystemInfo();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("app:getSystemInfo");
});
it("checkForUpdates invokes app:checkForUpdates", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.checkForUpdates();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("app:checkForUpdates");
});
it("getServerPort invokes app:getServerPort", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.getServerPort();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("app:getServerPort");
});
it("updateTrayStatus invokes tray:updateStatus with status argument", async () => {
it("fusionShell subscribes and unsubscribes state listener", async () => {
await importPreloadModule();
const shell = getExposed<{ subscribe: (listener: (state: unknown) => void) => () => void }>("fusionShell");
const api = getFusionApi();
await api?.updateTrayStatus("paused");
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("tray:updateStatus", "paused");
});
it("showExportDialog invokes native:showExportDialog", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.showExportDialog();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("native:showExportDialog");
});
it("showImportDialog invokes native:showImportDialog", async () => {
await importPreloadModule();
const api = getFusionApi();
await api?.showImportDialog();
expect(mocks.ipcRenderer.invoke).toHaveBeenCalledWith("native:showImportDialog");
});
it("onDeepLink subscribes to deep-link and returns unsubscribe", async () => {
await importPreloadModule();
const api = getFusionApi();
const callback = vi.fn();
const unsubscribe = api?.onDeepLink(callback);
expect(mocks.ipcRenderer.on).toHaveBeenCalledWith("deep-link", expect.any(Function));
const unsubscribe = shell?.subscribe(() => undefined);
expect(mocks.ipcRenderer.on).toHaveBeenCalledWith("shell:state", expect.any(Function));
unsubscribe?.();
expect(mocks.ipcRenderer.removeListener).toHaveBeenCalledWith(
"deep-link",
expect.any(Function),
);
});
it("onUpdateAvailable subscribes to update-available and returns unsubscribe", async () => {
await importPreloadModule();
const api = getFusionApi();
const callback = vi.fn();
const unsubscribe = api?.onUpdateAvailable(callback);
expect(mocks.ipcRenderer.on).toHaveBeenCalledWith("update-available", expect.any(Function));
unsubscribe?.();
expect(mocks.ipcRenderer.removeListener).toHaveBeenCalledWith(
"update-available",
expect.any(Function),
);
});
it("onUpdateDownloaded subscribes to update-downloaded and returns unsubscribe", async () => {
await importPreloadModule();
const api = getFusionApi();
const callback = vi.fn();
const unsubscribe = api?.onUpdateDownloaded(callback);
expect(mocks.ipcRenderer.on).toHaveBeenCalledWith("update-downloaded", expect.any(Function));
unsubscribe?.();
expect(mocks.ipcRenderer.removeListener).toHaveBeenCalledWith(
"update-downloaded",
expect.any(Function),
);
expect(mocks.ipcRenderer.removeListener).toHaveBeenCalledWith("shell:state", expect.any(Function));
});
});