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:
45
packages/mobile/src/__tests__/connection-profiles.test.ts
Normal file
45
packages/mobile/src/__tests__/connection-profiles.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const storage = new Map<string, string>();
|
||||
|
||||
vi.mock("@capacitor/preferences", () => ({
|
||||
Preferences: {
|
||||
get: vi.fn(async ({ key }: { key: string }) => ({ value: storage.get(key) ?? null })),
|
||||
set: vi.fn(async ({ key, value }: { key: string; value: string }) => {
|
||||
storage.set(key, value);
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("connection-profiles", () => {
|
||||
beforeEach(() => {
|
||||
storage.clear();
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("persists and lists profiles", async () => {
|
||||
const { saveShellProfile, listShellProfiles } = await import("../plugins/connection-profiles.js");
|
||||
|
||||
await saveShellProfile({ name: "Prod", serverUrl: "https://fusion.example.com/", authToken: "token" });
|
||||
const profiles = await listShellProfiles();
|
||||
|
||||
expect(profiles).toHaveLength(1);
|
||||
expect(profiles[0]).toMatchObject({
|
||||
name: "Prod",
|
||||
serverUrl: "https://fusion.example.com",
|
||||
authToken: "token",
|
||||
});
|
||||
});
|
||||
|
||||
it("clears active profile when deleted", async () => {
|
||||
const { saveShellProfile, setActiveShellProfile, loadShellProfiles, deleteShellProfile } = await import("../plugins/connection-profiles.js");
|
||||
|
||||
const profile = await saveShellProfile({ name: "Prod", serverUrl: "https://fusion.example.com" });
|
||||
await setActiveShellProfile(profile.id);
|
||||
await deleteShellProfile(profile.id);
|
||||
|
||||
const state = await loadShellProfiles();
|
||||
expect(state.activeProfileId).toBeNull();
|
||||
expect(state.profiles).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
62
packages/mobile/src/__tests__/native-shell.test.ts
Normal file
62
packages/mobile/src/__tests__/native-shell.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const state = {
|
||||
activeProfileId: null as string | null,
|
||||
profiles: [] as Array<{ id: string; name: string; serverUrl: string; authToken?: string | null; createdAt: string; updatedAt: string; lastUsedAt?: string | null }>,
|
||||
};
|
||||
|
||||
vi.mock("../plugins/connection-profiles.js", () => ({
|
||||
loadShellProfiles: vi.fn(async () => state),
|
||||
listShellProfiles: vi.fn(async () => state.profiles),
|
||||
saveShellProfile: vi.fn(async (profile: { name: string; serverUrl: string }) => {
|
||||
const saved = {
|
||||
id: "p1",
|
||||
name: profile.name,
|
||||
serverUrl: profile.serverUrl,
|
||||
authToken: null,
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
lastUsedAt: null,
|
||||
};
|
||||
state.profiles = [saved];
|
||||
return saved;
|
||||
}),
|
||||
deleteShellProfile: vi.fn(async () => {
|
||||
state.profiles = [];
|
||||
state.activeProfileId = null;
|
||||
}),
|
||||
setActiveShellProfile: vi.fn(async (profileId: string | null) => {
|
||||
state.activeProfileId = profileId;
|
||||
return state;
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("MobileNativeShellBridge", () => {
|
||||
const scanner = { scanConnection: vi.fn(async () => ({ serverUrl: "https://fusion.example.com", authToken: null })) };
|
||||
|
||||
beforeEach(() => {
|
||||
state.activeProfileId = null;
|
||||
state.profiles = [];
|
||||
scanner.scanConnection.mockClear();
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("emits state updates to subscribers", async () => {
|
||||
const { MobileNativeShellBridge } = await import("../plugins/native-shell.js");
|
||||
const bridge = new MobileNativeShellBridge(scanner as never);
|
||||
const listener = vi.fn();
|
||||
|
||||
const unsubscribe = bridge.subscribe(listener);
|
||||
await bridge.saveProfile({ name: "Prod", serverUrl: "https://fusion.example.com" });
|
||||
|
||||
expect(listener).toHaveBeenCalled();
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("rejects desktop mode switch", async () => {
|
||||
const { MobileNativeShellBridge } = await import("../plugins/native-shell.js");
|
||||
const bridge = new MobileNativeShellBridge(scanner as never);
|
||||
|
||||
await expect(bridge.setDesktopMode("local")).rejects.toThrow("Desktop mode is not supported");
|
||||
});
|
||||
});
|
||||
19
packages/mobile/src/__tests__/qr-scanner.test.ts
Normal file
19
packages/mobile/src/__tests__/qr-scanner.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { QrScanner, parseQrConnectionPayload } from "../plugins/qr-scanner.js";
|
||||
|
||||
describe("qr-scanner", () => {
|
||||
it("parses JSON payload", () => {
|
||||
const parsed = parseQrConnectionPayload('{"serverUrl":"https://fusion.example.com","authToken":"abc"}');
|
||||
expect(parsed).toEqual({ serverUrl: "https://fusion.example.com", authToken: "abc" });
|
||||
});
|
||||
|
||||
it("parses URL payload", () => {
|
||||
const parsed = parseQrConnectionPayload("https://fusion.example.com/dashboard?authToken=abc");
|
||||
expect(parsed).toEqual({ serverUrl: "https://fusion.example.com", authToken: "abc" });
|
||||
});
|
||||
|
||||
it("uses adapter scanning", async () => {
|
||||
const scanner = new QrScanner({ scan: vi.fn(async () => "https://fusion.example.com") });
|
||||
await expect(scanner.scanConnection()).resolves.toEqual({ serverUrl: "https://fusion.example.com", authToken: null });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user