mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-24 10:03:20 +00:00
PluginDataExDisplayV2.mtime applied `~~` to the sum of millisecond timestamps before dividing, which keeps only the low 32 bits. The freshness chip and "Select All Shiny" then compared wrapped values and could offer an older copy as newer. Fixes #1194
136 lines
4.6 KiB
TypeScript
136 lines
4.6 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("@/common/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", () => {
|
|
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,
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|