mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-11-15 04:30:12 +00:00
Separate host-independent helpers under common/core and colocate feature support code with its owners. Update imports, tests, and source-layout guidance while preserving runtime behaviour.
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("@/deps.ts", () => ({
|
|
addIcon: vi.fn(),
|
|
diff_match_patch: class DiffMatchPatch {},
|
|
normalizePath: vi.fn((path: string) => path),
|
|
parseYaml: vi.fn(),
|
|
Platform: {},
|
|
}));
|
|
vi.mock("./PluginDialogModal.ts", () => ({
|
|
PluginDialogModal: class PluginDialogModal {},
|
|
}));
|
|
vi.mock("@/features/HiddenFileCommon/JsonResolveModal.ts", () => ({
|
|
JsonResolveModal: class JsonResolveModal {},
|
|
}));
|
|
vi.mock("@/modules/features/InteractiveConflictResolving/ConflictResolveModal.ts", () => ({
|
|
ConflictResolveModal: class ConflictResolveModal {},
|
|
}));
|
|
vi.mock("@/features/LiveSyncCommands.ts", () => ({
|
|
LiveSyncCommands: class LiveSyncCommands {
|
|
core!: { services: unknown };
|
|
get services() {
|
|
return this.core.services;
|
|
}
|
|
},
|
|
}));
|
|
vi.mock("@/common/types.ts", () => ({
|
|
ICXHeader: "ix:",
|
|
PERIODIC_PLUGIN_SWEEP: 60,
|
|
}));
|
|
vi.mock("@/common/utils.ts", () => ({
|
|
cancelTask: vi.fn(),
|
|
EVEN: Symbol("even"),
|
|
isCustomisationSyncMetadata: vi.fn(),
|
|
isPluginMetadata: vi.fn(),
|
|
scheduleTask: vi.fn(),
|
|
}));
|
|
vi.mock("@/common/PeriodicProcessor.ts", () => ({
|
|
PeriodicProcessor: class PeriodicProcessor {},
|
|
}));
|
|
vi.mock("@/common/events.ts", () => ({
|
|
EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG: "open-plugin-sync",
|
|
eventHub: {
|
|
onEvent: vi.fn(),
|
|
},
|
|
}));
|
|
vi.mock("@/common/translation", () => ({
|
|
$msg: vi.fn((message: string) => message),
|
|
}));
|
|
vi.mock("@/features/HiddenFileCommon/obsidianCommunityPlugins.ts", () => ({
|
|
getObsidianCommunityPluginManager: vi.fn(),
|
|
}));
|
|
|
|
import { cancelTask } from "@/common/utils.ts";
|
|
import { ConfigSync, PluginDataExDisplayV2, type IPluginDataExDisplay } from "./CmdConfigSync";
|
|
|
|
describe("ConfigSync commands", () => {
|
|
it("shows the Customisation Sync command only whilst the feature is enabled", () => {
|
|
const commands: Array<{
|
|
id: string;
|
|
checkCallback?: (checking: boolean) => boolean | void;
|
|
}> = [];
|
|
const settings = {
|
|
usePluginSync: false,
|
|
};
|
|
const showPluginSyncModal = vi.fn();
|
|
const configSync = Object.create(ConfigSync.prototype) as ConfigSync;
|
|
Object.assign(configSync, {
|
|
core: {
|
|
settings,
|
|
services: {
|
|
API: {
|
|
addCommand: vi.fn((command) => commands.push(command)),
|
|
},
|
|
},
|
|
},
|
|
addRibbonIcon: vi.fn(() => ({
|
|
addClass: vi.fn(),
|
|
})),
|
|
showPluginSyncModal,
|
|
});
|
|
|
|
configSync.onload();
|
|
|
|
const command = commands.find(({ id }) => id === "livesync-plugin-dialog-ex");
|
|
expect(command?.checkCallback?.(true)).toBe(false);
|
|
|
|
settings.usePluginSync = true;
|
|
expect(command?.checkCallback?.(true)).toBe(true);
|
|
expect(command?.checkCallback?.(false)).toBe(true);
|
|
expect(showPluginSyncModal).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("cancels the pending configuration Notice before releasing its owned UI", async () => {
|
|
const notices = { hide: vi.fn() };
|
|
const periodicPluginSweepProcessor = { disable: vi.fn() };
|
|
const configSync = Object.create(ConfigSync.prototype) as ConfigSync;
|
|
Object.assign(configSync, {
|
|
core: {
|
|
services: {
|
|
context: { notices },
|
|
},
|
|
},
|
|
periodicPluginSweepProcessor,
|
|
pluginList: [],
|
|
pluginListGeneration: 0,
|
|
loadedManifest_mTime: new Map(),
|
|
pluginScanProcessor: { clearPending: vi.fn(), stop: vi.fn(async () => {}) },
|
|
});
|
|
|
|
await configSync.onunload();
|
|
|
|
expect(cancelTask).toHaveBeenCalledWith("config-sync:updated-configuration");
|
|
expect(notices.hide).toHaveBeenCalledWith("config-sync:updated-configuration");
|
|
expect(periodicPluginSweepProcessor.disable).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("PluginDataExDisplayV2", () => {
|
|
const entryWithModifiedTimes = (mtimes: number[]) =>
|
|
new PluginDataExDisplayV2({
|
|
documentPath: "ix:device/plugin_main/example",
|
|
category: "PLUGIN_MAIN",
|
|
name: "example",
|
|
term: "device",
|
|
files: mtimes.map((mtime, index) => ({ filename: `file-${index}`, mtime, data: [] })),
|
|
} as unknown as IPluginDataExDisplay);
|
|
|
|
it("averages millisecond modification times without truncating them to 32 bits", () => {
|
|
const mtime = Date.UTC(2026, 8, 15);
|
|
expect(entryWithModifiedTimes([mtime, mtime, mtime]).mtime).toBe(mtime);
|
|
});
|
|
|
|
it("keeps a newer copy newer than an older one", () => {
|
|
const newer = entryWithModifiedTimes([Date.UTC(2026, 8, 15)]);
|
|
const older = entryWithModifiedTimes([Date.UTC(2026, 8, 2)]);
|
|
expect(newer.mtime).toBeGreaterThan(older.mtime);
|
|
});
|
|
});
|