Harden real-runtime workflow validation

This commit is contained in:
vorotamoroz
2026-08-31 00:35:29 +00:00
parent 3a23b7a6b0
commit 9a28d46287
8 changed files with 72 additions and 42 deletions
+23 -18
View File
@@ -12,8 +12,11 @@ import {
} from "../runner/couchdb.ts";
import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts";
import {
assertE2eCompatibilityMarker,
assertEqual,
configureCouchDb,
createE2eCouchDbPluginData,
createE2eObsidianDeviceLocalState,
prepareRemote,
pushLocalChanges,
waitForLiveSyncCoreReady,
@@ -149,31 +152,33 @@ async function startConfiguredSession(
vault: TemporaryVault,
deviceName: string
): Promise<ObsidianLiveSyncSession> {
const couchDbSettings = {
uri: context.couchDb.uri,
username: context.couchDb.username,
password: context.couchDb.password,
dbName: context.dbName,
};
const customisationSettings = {
deviceAndVaultName: deviceName,
usePluginSync: true,
usePluginSyncV2: true,
autoSweepPlugins: false,
autoSweepPluginsPeriodic: false,
syncInternalFiles: false,
};
const session = await startObsidianLiveSyncSession({
binary: context.binary,
cliBinary: context.cliBinary,
vault,
startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000),
// This scenario exercises Customisation Sync, not onboarding. Seed a
// configured Vault and its device-local compatibility acknowledgement.
pluginData: createE2eCouchDbPluginData(couchDbSettings, customisationSettings),
localStorageEntries: createE2eObsidianDeviceLocalState(vault.name),
});
await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv);
await configureCouchDb(
context.cliBinary,
session.cliEnv,
{
uri: context.couchDb.uri,
username: context.couchDb.username,
password: context.couchDb.password,
dbName: context.dbName,
},
{
deviceAndVaultName: deviceName,
usePluginSync: true,
usePluginSyncV2: true,
autoSweepPlugins: false,
autoSweepPluginsPeriodic: false,
syncInternalFiles: false,
}
);
await assertE2eCompatibilityMarker(context.cliBinary, session.cliEnv);
await configureCouchDb(context.cliBinary, session.cliEnv, couchDbSettings, customisationSettings);
await evalObsidianJson<unknown>(
context.cliBinary,
[
@@ -179,14 +179,17 @@ async function openOnboardingFromSettings(): Promise<void> {
async function dismissVisibleNotices(): Promise<void> {
await withObsidianPage(obsidianRemoteDebuggingPort(), async (page) => {
const notices = page.locator(".notice:visible");
let noticeIndex = 0;
while ((await notices.count()) > 0) {
const noticeCount = await page.locator(".notice").count();
await notices.first().click({ position: { x: 8, y: 8 }, timeout: uiTimeoutMs });
await page.waitForFunction(
(previousCount) => document.querySelectorAll(".notice").length < previousCount,
noticeCount,
{ timeout: uiTimeoutMs }
);
const marker = `livesync-e2e-notice-${noticeIndex++}`;
await notices
.first()
.evaluate((element, value) => element.setAttribute("data-livesync-e2e-notice", value), marker);
const markedNotice = page.locator(`[data-livesync-e2e-notice="${marker}"]`);
await markedNotice.click({ position: { x: 8, y: 8 }, timeout: uiTimeoutMs });
// Follow the notice which was clicked. Another concurrently added
// notice must not make a total-count wait look permanently stuck.
await markedNotice.waitFor({ state: "hidden", timeout: uiTimeoutMs });
}
});
}
@@ -241,7 +241,11 @@ async function runScenario(): Promise<void> {
const consoleErrors: string[] = [];
try {
browser = await chromium.launch({ headless: true });
const browserExecutable = process.env.E2E_PLAYWRIGHT_CHROMIUM?.trim();
browser = await chromium.launch({
headless: true,
...(browserExecutable ? { executablePath: browserExecutable } : {}),
});
const firstVault = await createTemporaryVault("obsidian-livesync-p2p-check-first-e2e-");
vaults.push(firstVault);
const page = await browser.newPage({ viewport: { width: 1440, height: 1100 } });
@@ -2,7 +2,7 @@ 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 { assertEqual, waitForLiveSyncCoreReady } from "../runner/liveSyncWorkflow.ts";
import { assertEqual } from "../runner/liveSyncWorkflow.ts";
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
import { createTemporaryVault } from "../runner/vault.ts";
@@ -75,8 +75,8 @@ async function main(): Promise<void> {
vault,
startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000),
});
await waitForLiveSyncCoreReady(cli.binary, session.cliEnv);
// The export is available while an unconfigured Vault remains outside
// application readiness; the session helper has already loaded the plug-in.
await configureSettingMarkdown(cli.binary, session.cliEnv);
const content = await waitForFileContaining(vault.path, settingPath, [
(value) => value.includes("````yaml:livesync-setting"),