mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-06-26 16:13:57 +00:00
(test): improve e2e session helper
This commit is contained in:
@@ -125,7 +125,8 @@ Initial discovery on Linux ARM64 found that:
|
||||
Current implementation status:
|
||||
|
||||
- Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling.
|
||||
- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, and `test:e2e:obsidian:install-appimage`.
|
||||
- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, and `test:e2e:obsidian:install-appimage`.
|
||||
- Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code.
|
||||
- Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution.
|
||||
- Confirmed the smoke runner on Linux ARM64 with the extracted Obsidian `1.12.7` AppImage, `xvfb-run`, and the built Self-hosted LiveSync bundle.
|
||||
- Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, open the temporary vault through `obsidian-cli`, enable community plug-ins through `app.plugins.setEnable(true)`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`.
|
||||
@@ -137,6 +138,7 @@ Current verification:
|
||||
- `npm run build` passes with existing Svelte warnings.
|
||||
- `npm run test:e2e:obsidian:discover` finds `_testdata/obsidian/squashfs-root/obsidian` when the extracted AppImage is present.
|
||||
- `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally.
|
||||
- `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian.
|
||||
- `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present.
|
||||
|
||||
Known limits:
|
||||
@@ -156,6 +158,10 @@ Known limits:
|
||||
|
||||
This validates real boot-up, settings persistence, vault file access, database writes, and restart-sensitive state.
|
||||
|
||||
Current implementation status:
|
||||
|
||||
- Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced.
|
||||
|
||||
### Phase 3: Two-Vault Synchronisation
|
||||
|
||||
- Launch two Obsidian instances with two temporary vaults.
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts",
|
||||
"test:e2e:obsidian:cli-help": "tsx test/e2e-obsidian/scripts/cli-help.ts",
|
||||
"test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts",
|
||||
"test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh",
|
||||
"test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh",
|
||||
|
||||
@@ -17,6 +17,8 @@ The runner does not require Self-hosted LiveSync to expose an E2E-only bridge. R
|
||||
|
||||
Obsidian 1.12 stores the global community plug-in switch outside `.obsidian/community-plugins.json`. The smoke runner enables it through `app.plugins.setEnable(true)` after the vault window is available.
|
||||
|
||||
Future workflows should use `startObsidianLiveSyncSession()` from `runner/session.ts` rather than repeating the launch and plug-in readiness sequence.
|
||||
|
||||
## Local Setup
|
||||
|
||||
Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location.
|
||||
@@ -42,6 +44,7 @@ npm run test:e2e:obsidian:install-appimage
|
||||
npm run test:e2e:obsidian:discover
|
||||
npm run test:e2e:obsidian:cli-help -- vaults verbose
|
||||
npm run test:e2e:obsidian:smoke
|
||||
npm run test:e2e:obsidian:vault-reflection
|
||||
```
|
||||
|
||||
Useful environment variables:
|
||||
@@ -57,6 +60,7 @@ Useful environment variables:
|
||||
- `E2E_OBSIDIAN_READY_TIMEOUT_MS`: plug-in readiness timeout in milliseconds.
|
||||
- `E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS`: timeout for waiting until the vault-side Obsidian CLI exposes the plug-in catalogue.
|
||||
- `E2E_OBSIDIAN_CLI_TIMEOUT_MS`: timeout for each `obsidian-cli` invocation.
|
||||
- `E2E_OBSIDIAN_FILE_TIMEOUT_MS`: timeout for waiting until a note created through Obsidian's vault API is reflected to disk.
|
||||
- `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds.
|
||||
- `E2E_OBSIDIAN_KEEP_VAULT=true`: keep the temporary vault for inspection.
|
||||
- `E2E_OBSIDIAN_USE_XVFB=false`: disable automatic `xvfb-run` on headless Linux.
|
||||
|
||||
@@ -7,6 +7,13 @@ export type ObsidianCliResult = {
|
||||
stderr: string;
|
||||
};
|
||||
|
||||
function parseEvalJson(stdout: string): unknown {
|
||||
const marker = "=> ";
|
||||
const markerIndex = stdout.indexOf(marker);
|
||||
const text = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length) : stdout;
|
||||
return JSON.parse(text.trim());
|
||||
}
|
||||
|
||||
export async function runObsidianCli(
|
||||
cliBinary: string,
|
||||
args: string[],
|
||||
@@ -60,3 +67,23 @@ export async function openVaultWithObsidianCli(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function evalObsidianJson<T>(
|
||||
cliBinary: string,
|
||||
code: string,
|
||||
env: NodeJS.ProcessEnv = process.env
|
||||
): Promise<T> {
|
||||
const result = await runObsidianCli(cliBinary, ["eval", `code=${code}`], env);
|
||||
if (result.code !== 0 || result.stdout.includes("Error:")) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to evaluate Obsidian JavaScript through CLI. code=${result.code}, signal=${result.signal}`,
|
||||
result.stdout ? `stdout:\n${result.stdout}` : undefined,
|
||||
result.stderr ? `stderr:\n${result.stderr}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
return parseEvalJson(result.stdout) as T;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { runObsidianCli } from "./cli.ts";
|
||||
import { evalObsidianJson } from "./cli.ts";
|
||||
|
||||
export type PluginReadiness = {
|
||||
status: "ready";
|
||||
@@ -7,13 +7,6 @@ export type PluginReadiness = {
|
||||
vaultName: string;
|
||||
};
|
||||
|
||||
function parseEvalJson(stdout: string): unknown {
|
||||
const marker = "=> ";
|
||||
const markerIndex = stdout.indexOf(marker);
|
||||
const text = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length) : stdout;
|
||||
return JSON.parse(text.trim());
|
||||
}
|
||||
|
||||
export async function waitForPluginReady(
|
||||
cliBinary: string,
|
||||
env: NodeJS.ProcessEnv,
|
||||
@@ -22,28 +15,24 @@ export async function waitForPluginReady(
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let lastOutput = "";
|
||||
while (Date.now() < deadline) {
|
||||
const result = await runObsidianCli(
|
||||
cliBinary,
|
||||
[
|
||||
"eval",
|
||||
try {
|
||||
const readiness = await evalObsidianJson<PluginReadiness>(
|
||||
cliBinary,
|
||||
[
|
||||
"code=(async()=>JSON.stringify({",
|
||||
"(async()=>JSON.stringify({",
|
||||
"status:!!app.plugins.plugins['obsidian-livesync']?'ready':'pending',",
|
||||
"pluginId:'obsidian-livesync',",
|
||||
"pluginVersion:app.plugins.manifests['obsidian-livesync']?.version,",
|
||||
"vaultName:app.vault.getName()",
|
||||
"}))()",
|
||||
].join(""),
|
||||
],
|
||||
env
|
||||
);
|
||||
lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
||||
try {
|
||||
const readiness = parseEvalJson(result.stdout) as PluginReadiness;
|
||||
env
|
||||
);
|
||||
if (readiness.status === "ready") {
|
||||
return readiness;
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
lastOutput = error instanceof Error ? error.message : String(error);
|
||||
// Keep polling until Obsidian exposes the vault-side CLI and plug-in state.
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { openVaultWithObsidianCli, runObsidianCli } from "./cli.ts";
|
||||
import { launchObsidian, type ObsidianProcess } from "./launch.ts";
|
||||
import { installBuiltPlugin, type PluginInstallResult } from "./pluginInstaller.ts";
|
||||
import { waitForPluginReady, type PluginReadiness } from "./readiness.ts";
|
||||
import type { TemporaryVault } from "./vault.ts";
|
||||
|
||||
export type ObsidianLiveSyncSession = {
|
||||
app: ObsidianProcess;
|
||||
cliEnv: NodeJS.ProcessEnv;
|
||||
install: PluginInstallResult;
|
||||
readiness: PluginReadiness;
|
||||
};
|
||||
|
||||
export type StartObsidianLiveSyncSessionOptions = {
|
||||
binary: string;
|
||||
cliBinary: string;
|
||||
vault: TemporaryVault;
|
||||
startupGraceMs?: number;
|
||||
};
|
||||
|
||||
async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise<void> {
|
||||
const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000);
|
||||
let lastOutput = "";
|
||||
while (Date.now() < deadline) {
|
||||
const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env);
|
||||
lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
||||
if (result.stdout.includes("obsidian-livesync")) {
|
||||
return;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
throw new Error(`Timed out waiting for Obsidian plug-in catalogue through CLI.\n${lastOutput}`);
|
||||
}
|
||||
|
||||
async function enableCommunityPlugins(cliBinary: string, env: NodeJS.ProcessEnv): Promise<void> {
|
||||
const result = await runObsidianCli(cliBinary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], env);
|
||||
if (result.code !== 0 || result.stdout.includes("Error:")) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to enable Obsidian community plug-ins through CLI. code=${result.code}, signal=${result.signal}`,
|
||||
result.stdout ? `stdout:\n${result.stdout}` : undefined,
|
||||
result.stderr ? `stderr:\n${result.stderr}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadLiveSyncPlugin(cliBinary: string, env: NodeJS.ProcessEnv): Promise<void> {
|
||||
const reload = await runObsidianCli(cliBinary, ["plugin:reload", "id=obsidian-livesync"], env);
|
||||
if (reload.code !== 0 || !reload.stdout.includes("Reloaded: obsidian-livesync")) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to reload Self-hosted LiveSync through Obsidian CLI. code=${reload.code}, signal=${reload.signal}`,
|
||||
reload.stdout ? `stdout:\n${reload.stdout}` : undefined,
|
||||
reload.stderr ? `stderr:\n${reload.stderr}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function startObsidianLiveSyncSession(
|
||||
options: StartObsidianLiveSyncSessionOptions
|
||||
): Promise<ObsidianLiveSyncSession> {
|
||||
const install = await installBuiltPlugin(options.vault.path);
|
||||
const app = await launchObsidian({
|
||||
binary: options.binary,
|
||||
vaultPath: options.vault.path,
|
||||
homePath: options.vault.homePath,
|
||||
xdgConfigPath: options.vault.xdgConfigPath,
|
||||
userDataPath: options.vault.userDataPath,
|
||||
startupGraceMs: options.startupGraceMs,
|
||||
});
|
||||
const cliEnv = {
|
||||
...process.env,
|
||||
HOME: options.vault.homePath,
|
||||
XDG_CONFIG_HOME: options.vault.xdgConfigPath,
|
||||
};
|
||||
|
||||
try {
|
||||
await openVaultWithObsidianCli(options.cliBinary, options.vault.path, cliEnv);
|
||||
await waitForPluginCatalogue(options.cliBinary, cliEnv);
|
||||
await enableCommunityPlugins(options.cliBinary, cliEnv);
|
||||
await reloadLiveSyncPlugin(options.cliBinary, cliEnv);
|
||||
const readiness = await waitForPluginReady(options.cliBinary, cliEnv);
|
||||
return { app, cliEnv, install, readiness };
|
||||
} catch (error) {
|
||||
await app.stop();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,6 @@
|
||||
import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts";
|
||||
import { launchObsidian } from "../runner/launch.ts";
|
||||
import { installBuiltPlugin } from "../runner/pluginInstaller.ts";
|
||||
import { waitForPluginReady } from "../runner/readiness.ts";
|
||||
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
|
||||
import { createTemporaryVault } from "../runner/vault.ts";
|
||||
import { openVaultWithObsidianCli, runObsidianCli } from "../runner/cli.ts";
|
||||
|
||||
async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise<void> {
|
||||
const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000);
|
||||
let lastOutput = "";
|
||||
while (Date.now() < deadline) {
|
||||
const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env);
|
||||
lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
||||
if (result.stdout.includes("obsidian-livesync")) {
|
||||
return;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
throw new Error(`Timed out waiting for Obsidian plug-in catalogue through CLI.\n${lastOutput}`);
|
||||
}
|
||||
|
||||
async function enableCommunityPlugins(cliBinary: string, env: NodeJS.ProcessEnv): Promise<void> {
|
||||
const result = await runObsidianCli(cliBinary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], env);
|
||||
if (result.code !== 0 || result.stdout.includes("Error:")) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to enable Obsidian community plug-ins through CLI. code=${result.code}, signal=${result.signal}`,
|
||||
result.stdout ? `stdout:\n${result.stdout}` : undefined,
|
||||
result.stderr ? `stderr:\n${result.stderr}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const binary = requireObsidianBinary();
|
||||
@@ -41,50 +9,27 @@ async function main(): Promise<void> {
|
||||
throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`);
|
||||
}
|
||||
const vault = await createTemporaryVault();
|
||||
let app;
|
||||
let session: ObsidianLiveSyncSession | undefined;
|
||||
try {
|
||||
const install = await installBuiltPlugin(vault.path);
|
||||
console.log(`Using Obsidian executable: ${binary}`);
|
||||
console.log(`Temporary vault: ${vault.path}`);
|
||||
console.log(`Installed plug-in artifacts: ${install.copied.join(", ")}`);
|
||||
|
||||
app = await launchObsidian({
|
||||
session = await startObsidianLiveSyncSession({
|
||||
binary,
|
||||
vaultPath: vault.path,
|
||||
homePath: vault.homePath,
|
||||
xdgConfigPath: vault.xdgConfigPath,
|
||||
userDataPath: vault.userDataPath,
|
||||
cliBinary: cli.binary,
|
||||
vault,
|
||||
startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000),
|
||||
});
|
||||
const cliEnv = {
|
||||
...process.env,
|
||||
HOME: vault.homePath,
|
||||
XDG_CONFIG_HOME: vault.xdgConfigPath,
|
||||
};
|
||||
await openVaultWithObsidianCli(cli.binary, vault.path, cliEnv);
|
||||
await waitForPluginCatalogue(cli.binary, cliEnv);
|
||||
await enableCommunityPlugins(cli.binary, cliEnv);
|
||||
const reload = await runObsidianCli(cli.binary, ["plugin:reload", "id=obsidian-livesync"], cliEnv);
|
||||
if (reload.code !== 0 || !reload.stdout.includes("Reloaded: obsidian-livesync")) {
|
||||
throw new Error(
|
||||
[
|
||||
`Failed to reload Self-hosted LiveSync through Obsidian CLI. code=${reload.code}, signal=${reload.signal}`,
|
||||
reload.stdout ? `stdout:\n${reload.stdout}` : undefined,
|
||||
reload.stderr ? `stderr:\n${reload.stderr}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
const readiness = await waitForPluginReady(cli.binary, cliEnv);
|
||||
console.log(`Installed plug-in artifacts: ${session.install.copied.join(", ")}`);
|
||||
const { readiness } = session;
|
||||
console.log(
|
||||
`Obsidian plug-in ready: ${readiness.pluginId}@${readiness.pluginVersion} in ${readiness.vaultName}`
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, Number(process.env.E2E_OBSIDIAN_SMOKE_TIMEOUT_MS ?? 1000)));
|
||||
console.log("Obsidian stayed alive after the plug-in readiness check.");
|
||||
} finally {
|
||||
if (app) {
|
||||
await app.stop();
|
||||
if (session) {
|
||||
await session.app.stop();
|
||||
}
|
||||
await vault.dispose();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { evalObsidianJson } from "../runner/cli.ts";
|
||||
import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts";
|
||||
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
|
||||
import { createTemporaryVault } from "../runner/vault.ts";
|
||||
|
||||
type CreatedNote = {
|
||||
path: string;
|
||||
read: string;
|
||||
exists: boolean;
|
||||
};
|
||||
|
||||
type ReadNote = {
|
||||
exists: boolean;
|
||||
read: string | null;
|
||||
};
|
||||
|
||||
const notePath = "E2E/real-vault-reflection.md";
|
||||
const noteContent = [
|
||||
"# Real Obsidian E2E",
|
||||
"",
|
||||
"This note was created through Obsidian's own vault API.",
|
||||
`Created at: ${new Date().toISOString()}`,
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
async function waitForFileContent(vaultPath: string, path: string, expectedContent: string): Promise<void> {
|
||||
const fullPath = join(vaultPath, path);
|
||||
const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000);
|
||||
let lastError: unknown;
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
const content = await readFile(fullPath, "utf-8");
|
||||
if (content === expectedContent) {
|
||||
return;
|
||||
}
|
||||
lastError = new Error(`Unexpected content in ${fullPath}`);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
}
|
||||
throw new Error(`Timed out waiting for reflected vault file: ${fullPath}\nLast error: ${String(lastError)}`);
|
||||
}
|
||||
|
||||
function assertEqual(actual: unknown, expected: unknown, message: string): void {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const binary = requireObsidianBinary();
|
||||
const cli = discoverObsidianCli();
|
||||
if (!cli.binary) {
|
||||
throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`);
|
||||
}
|
||||
|
||||
const vault = await createTemporaryVault();
|
||||
let session: ObsidianLiveSyncSession | undefined;
|
||||
try {
|
||||
console.log(`Using Obsidian executable: ${binary}`);
|
||||
console.log(`Temporary vault: ${vault.path}`);
|
||||
|
||||
session = await startObsidianLiveSyncSession({
|
||||
binary,
|
||||
cliBinary: cli.binary,
|
||||
vault,
|
||||
startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000),
|
||||
});
|
||||
|
||||
const created = await evalObsidianJson<CreatedNote>(
|
||||
cli.binary,
|
||||
[
|
||||
"(async()=>{",
|
||||
`const path=${JSON.stringify(notePath)};`,
|
||||
`const content=${JSON.stringify(noteContent)};`,
|
||||
"if(!(await app.vault.adapter.exists('E2E'))) await app.vault.createFolder('E2E');",
|
||||
"const existing=app.vault.getAbstractFileByPath(path);",
|
||||
"if(existing) await app.vault.delete(existing);",
|
||||
"const file=await app.vault.create(path,content);",
|
||||
"const read=await app.vault.read(file);",
|
||||
"return JSON.stringify({path:file.path,read,exists:await app.vault.adapter.exists(path)});",
|
||||
"})()",
|
||||
].join(""),
|
||||
session.cliEnv
|
||||
);
|
||||
|
||||
assertEqual(created.path, notePath, "Obsidian created the note at an unexpected path.");
|
||||
assertEqual(created.exists, true, "Obsidian adapter did not report the created note.");
|
||||
assertEqual(created.read, noteContent, "Obsidian did not read back the created note content.");
|
||||
|
||||
await waitForFileContent(vault.path, notePath, noteContent);
|
||||
|
||||
const readBack = await evalObsidianJson<ReadNote>(
|
||||
cli.binary,
|
||||
[
|
||||
"(async()=>{",
|
||||
`const path=${JSON.stringify(notePath)};`,
|
||||
"const file=app.vault.getAbstractFileByPath(path);",
|
||||
"return JSON.stringify({exists:!!file,read:file?await app.vault.read(file):null});",
|
||||
"})()",
|
||||
].join(""),
|
||||
session.cliEnv
|
||||
);
|
||||
assertEqual(readBack.exists, true, "Obsidian did not find the reflected note on read-back.");
|
||||
assertEqual(readBack.read, noteContent, "Obsidian read-back content did not match the reflected file.");
|
||||
|
||||
console.log(`Created and verified reflected note: ${notePath}`);
|
||||
} finally {
|
||||
if (session) {
|
||||
await session.app.stop();
|
||||
}
|
||||
await vault.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error: unknown) => {
|
||||
console.error(error instanceof Error ? error.stack : error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user