mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-30 08:23:01 +00:00
refactor: compose browser application runtimes
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
||||
import { REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
WEBPEER_SETTINGS_KEY,
|
||||
createWebPeerPersistence,
|
||||
} from "@/apps/webpeer/src/WebPeerPersistence";
|
||||
|
||||
function createMemoryStore(initial: Record<string, unknown> = {}): SimpleStore<unknown> {
|
||||
const values = new Map(Object.entries(initial));
|
||||
return {
|
||||
db: Promise.resolve(undefined),
|
||||
get: async (key) => values.get(key),
|
||||
set: async (key, value) => {
|
||||
values.set(key, value);
|
||||
},
|
||||
delete: async (key) => {
|
||||
values.delete(key);
|
||||
},
|
||||
keys: async (from, to, count) => {
|
||||
const selected = [...values.keys()]
|
||||
.sort()
|
||||
.filter((key) => (from === undefined || key >= from) && (to === undefined || key <= to));
|
||||
return count === undefined ? selected : selected.slice(0, count);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("WebPeer settings persistence", () => {
|
||||
it("loads P2P-only defaults and saves the complete settings object", async () => {
|
||||
const store = createMemoryStore({
|
||||
[WEBPEER_SETTINGS_KEY]: {
|
||||
P2P_AppID: "custom-app",
|
||||
P2P_roomID: "room-42",
|
||||
P2P_Enabled: true,
|
||||
},
|
||||
});
|
||||
const persistence = createWebPeerPersistence(store);
|
||||
|
||||
const loaded = await persistence.settings.load();
|
||||
|
||||
expect(loaded).toEqual(
|
||||
expect.objectContaining({
|
||||
P2P_AppID: "custom-app",
|
||||
P2P_roomID: "room-42",
|
||||
P2P_Enabled: true,
|
||||
remoteType: REMOTE_P2P,
|
||||
isConfigured: true,
|
||||
additionalSuffixOfDatabaseName: "",
|
||||
suspendParseReplicationResult: true,
|
||||
})
|
||||
);
|
||||
const updated = { ...loaded!, P2P_AutoStart: true };
|
||||
await persistence.settings.save(updated);
|
||||
await expect(store.get(WEBPEER_SETTINGS_KEY)).resolves.toEqual(updated);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
import { createServiceContext } from "@vrtmrz/livesync-commonlib/context";
|
||||
import { DEFAULT_SETTINGS } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
||||
import { EVENT_LAYOUT_READY } from "@vrtmrz/livesync-commonlib/compat/events/coreEvents";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { WebPeerRuntime } from "@/apps/webpeer/src/WebPeerRuntime";
|
||||
|
||||
function createMemoryStore(): SimpleStore<unknown> {
|
||||
const values = new Map<string, unknown>();
|
||||
return {
|
||||
db: Promise.resolve(undefined),
|
||||
get: async (key) => values.get(key),
|
||||
set: async (key, value) => {
|
||||
values.set(key, value);
|
||||
},
|
||||
delete: async (key) => {
|
||||
values.delete(key);
|
||||
},
|
||||
keys: async () => [...values.keys()],
|
||||
};
|
||||
}
|
||||
|
||||
describe("WebPeer runtime composition", () => {
|
||||
it("preserves one context and live P2P result across the service graph and pane host", () => {
|
||||
const context = createServiceContext({
|
||||
translate: (key) => `webpeer:${key}`,
|
||||
});
|
||||
const runtime = new WebPeerRuntime({
|
||||
context,
|
||||
store: createMemoryStore(),
|
||||
});
|
||||
|
||||
expect(runtime.context).toBe(context);
|
||||
expect(runtime.services.context).toBe(context);
|
||||
expect(runtime.events).toBe(context.events);
|
||||
expect(runtime.currentReplicator).toBe(runtime.p2p.replicator);
|
||||
expect(runtime.paneHost.services).toBe(runtime.services);
|
||||
expect(runtime.paneHost.p2p).toBe(runtime.p2p);
|
||||
expect(runtime.paneHost.showPeerMenu).toBeTypeOf("function");
|
||||
expect("controller" in runtime).toBe(false);
|
||||
});
|
||||
|
||||
it("loads settings and opens the local database before announcing layout readiness", async () => {
|
||||
const runtime = new WebPeerRuntime({
|
||||
store: createMemoryStore(),
|
||||
});
|
||||
const loadSettings = vi.spyOn(runtime.services.setting, "loadSettings").mockResolvedValue(undefined);
|
||||
vi.spyOn(runtime.services.setting, "currentSettings").mockReturnValue({
|
||||
...DEFAULT_SETTINGS,
|
||||
P2P_AutoStart: false,
|
||||
P2P_Enabled: false,
|
||||
});
|
||||
const openDatabase = vi.spyOn(runtime.services.database, "openDatabase").mockResolvedValue(true);
|
||||
const markIsReady = vi.spyOn(runtime.services.appLifecycle, "markIsReady");
|
||||
const layoutReady = vi.fn();
|
||||
runtime.events.onEvent(EVENT_LAYOUT_READY, layoutReady);
|
||||
|
||||
await expect(runtime.start()).resolves.toBe(runtime);
|
||||
|
||||
expect(loadSettings).toHaveBeenCalledOnce();
|
||||
expect(openDatabase).toHaveBeenCalledOnce();
|
||||
expect(markIsReady).toHaveBeenCalledOnce();
|
||||
expect(layoutReady).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("rejects start when the browser-local database cannot be opened", async () => {
|
||||
const runtime = new WebPeerRuntime({
|
||||
store: createMemoryStore(),
|
||||
});
|
||||
vi.spyOn(runtime.services.setting, "loadSettings").mockResolvedValue(undefined);
|
||||
vi.spyOn(runtime.services.database, "openDatabase").mockResolvedValue(false);
|
||||
|
||||
await expect(runtime.start()).rejects.toThrow("WebPeer local database could not be opened");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user