mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-10 11:22:29 +00:00
test: adopt macOS-safe real-Obsidian sessions
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { VER } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { evalObsidianJson } = vi.hoisted(() => ({
|
||||
evalObsidianJson: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./cli.ts", () => ({ evalObsidianJson }));
|
||||
|
||||
import { assertE2eCompatibilityMarker, type CompatibilityMarkerState } from "./liveSyncWorkflow.ts";
|
||||
|
||||
describe("compatibility marker persistence", () => {
|
||||
it("waits for an accepted review to reach device-local storage", async () => {
|
||||
const pending: CompatibilityMarkerState = {
|
||||
vaultName: "fixture",
|
||||
additionalSuffix: "-",
|
||||
expectedStorageKey: "fixture--database-compatibility-version",
|
||||
rawStorageValue: null,
|
||||
serviceValue: "",
|
||||
versionUpFlash: "",
|
||||
};
|
||||
const persisted: CompatibilityMarkerState = {
|
||||
...pending,
|
||||
rawStorageValue: `${VER}`,
|
||||
serviceValue: `${VER}`,
|
||||
};
|
||||
evalObsidianJson.mockResolvedValueOnce(pending).mockResolvedValueOnce(persisted);
|
||||
|
||||
await expect(
|
||||
assertE2eCompatibilityMarker("obsidian-cli", {}, { timeoutMs: 100, intervalMs: 0 })
|
||||
).resolves.toEqual(persisted);
|
||||
expect(evalObsidianJson).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
@@ -42,6 +42,11 @@ export type CompatibilityMarkerState = {
|
||||
versionUpFlash: string;
|
||||
};
|
||||
|
||||
export type CompatibilityMarkerWaitOptions = {
|
||||
timeoutMs?: number;
|
||||
intervalMs?: number;
|
||||
};
|
||||
|
||||
export type ResumeCompatibilityReviewOptions = {
|
||||
verifyMissingDeviceMarkerExplanation?: boolean;
|
||||
screenshotPrefix?: string;
|
||||
@@ -67,6 +72,7 @@ export type LocalDatabaseEntry = {
|
||||
};
|
||||
|
||||
const E2E_PREFERRED_SETTINGS = {
|
||||
displayLanguage: "def",
|
||||
liveSync: false,
|
||||
syncOnStart: false,
|
||||
syncOnSave: false,
|
||||
@@ -127,14 +133,19 @@ export async function readE2eCompatibilityMarker(
|
||||
|
||||
export async function assertE2eCompatibilityMarker(
|
||||
cliBinary: string,
|
||||
env: NodeJS.ProcessEnv
|
||||
env: NodeJS.ProcessEnv,
|
||||
options: CompatibilityMarkerWaitOptions = {}
|
||||
): Promise<CompatibilityMarkerState> {
|
||||
const state = await readE2eCompatibilityMarker(cliBinary, env);
|
||||
if (state.serviceValue !== `${VER}`) {
|
||||
throw new Error(
|
||||
`The E2E compatibility marker was not available on first plug-in load: ${JSON.stringify(state)}`
|
||||
);
|
||||
const timeoutMs = options.timeoutMs ?? Number(process.env.E2E_OBSIDIAN_UI_TIMEOUT_MS ?? 10000);
|
||||
const intervalMs = options.intervalMs ?? 100;
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let state = await readE2eCompatibilityMarker(cliBinary, env);
|
||||
while (state.serviceValue !== `${VER}` && Date.now() < deadline) {
|
||||
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
||||
state = await readE2eCompatibilityMarker(cliBinary, env);
|
||||
}
|
||||
if (state.serviceValue !== `${VER}`)
|
||||
throw new Error(`The E2E compatibility marker was not persisted before timeout: ${JSON.stringify(state)}`);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { hasExactCaseOnlyRename } from "./pathEntries.ts";
|
||||
|
||||
describe("case-only rename assertions", () => {
|
||||
it("accepts only the exact new spelling", () => {
|
||||
expect(hasExactCaseOnlyRename(["case-rename.md"], "Case-Rename.md", "case-rename.md")).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects the old spelling even when a case-insensitive lookup would resolve it", () => {
|
||||
expect(hasExactCaseOnlyRename(["Case-Rename.md"], "Case-Rename.md", "case-rename.md")).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects an ambiguous directory containing both spellings", () => {
|
||||
expect(hasExactCaseOnlyRename(["Case-Rename.md", "case-rename.md"], "Case-Rename.md", "case-rename.md")).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
import { hasExactCaseOnlyRename } from "./pathEntries.ts";
|
||||
|
||||
export async function waitForExactCaseOnlyRename(
|
||||
vaultPath: string,
|
||||
oldPath: string,
|
||||
newPath: string,
|
||||
timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000)
|
||||
): Promise<void> {
|
||||
const oldDirectory = dirname(oldPath);
|
||||
const newDirectory = dirname(newPath);
|
||||
if (oldDirectory !== newDirectory) {
|
||||
throw new Error(`Case-only rename paths must share one parent directory: ${oldPath} -> ${newPath}`);
|
||||
}
|
||||
|
||||
const oldName = basename(oldPath);
|
||||
const newName = basename(newPath);
|
||||
const directoryPath = join(vaultPath, newDirectory);
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let lastEntries: string[] = [];
|
||||
while (Date.now() < deadline) {
|
||||
lastEntries = await readdir(directoryPath);
|
||||
if (hasExactCaseOnlyRename(lastEntries, oldName, newName)) return;
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
}
|
||||
throw new Error(
|
||||
`Timed out waiting for exact case-only rename: ${oldPath} -> ${newPath}. Directory entries: ${JSON.stringify(lastEntries)}`
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function hasExactCaseOnlyRename(entries: readonly string[], oldName: string, newName: string): boolean {
|
||||
return entries.includes(newName) && !entries.includes(oldName);
|
||||
}
|
||||
Reference in New Issue
Block a user