mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-23 13:02:58 +00:00
Keep P2P consumers on the current transport
This commit is contained in:
@@ -64,6 +64,7 @@ npm run test:e2e:obsidian:onboarding-invitation
|
||||
npm run test:e2e:obsidian:dialog-mounts
|
||||
npm run test:e2e:obsidian:settings-ui
|
||||
npm run test:e2e:obsidian:review-harness
|
||||
npm run test:e2e:obsidian:p2p-pane
|
||||
npm run test:e2e:obsidian:vault-reflection
|
||||
npm run test:e2e:obsidian:couchdb-upload
|
||||
npm run test:e2e:obsidian:cli-to-obsidian-sync
|
||||
@@ -91,7 +92,9 @@ The mobile pass uses Obsidian's `app.emulateMobile(true)`, a 390 by 844 CSS-pixe
|
||||
|
||||
`test:e2e:obsidian:review-harness` exercises only the boundaries owned by the opt-in maintainer Harness. It retains a real compatibility pause, uses the fixed Harness restart action to persist a device-local continuation and reload Obsidian, and requires the Harness to delete that state before reopening. It also runs the bounded local observations, confirms the dedicated Vault fixture root is removed, captures the copied privacy-bounded Markdown report, and checks the Harness layout and touch targets in mobile test mode. Compatibility explanation and persistence details remain owned by `settings-ui`, real P2P transfer remains owned by the Compose suite, and general Vault reflection remains owned by `vault-reflection`; the Harness test does not duplicate those workflows.
|
||||
|
||||
`test:e2e:obsidian:local-suite` builds the plug-in and, unless `LIVESYNC_CLI_COMMAND` selects an external CLI, the local LiveSync CLI. It then runs discovery, smoke, the onboarding invitation, Svelte dialogue mounting, settings UI, the Review Harness, Vault reflection, CouchDB upload, CLI-to-Obsidian synchronisation, Object Storage upload, startup scan, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export in sequence. Start the local CouchDB and MinIO fixtures before running it, or use `test:e2e:obsidian:local-suite:services` to let the wrapper stop leftover fixtures, start fresh fixtures, and stop them again after the run.
|
||||
`test:e2e:obsidian:p2p-pane` opens the P2P status view in a temporary real Obsidian session, verifies its principal connection control and horizontal layout, and captures a screenshot. It deliberately uses no relay or peer: current-replicator replacement is covered by focused unit tests, while the Deno and Compose CLI P2P lifecycle suite owns real peer discovery, replacement, relay disconnection, and transfer behaviour.
|
||||
|
||||
`test:e2e:obsidian:local-suite` builds the plug-in and, unless `LIVESYNC_CLI_COMMAND` selects an external CLI, the local LiveSync CLI. It then runs discovery, smoke, the onboarding invitation, Svelte dialogue mounting, settings UI, the Review Harness, the P2P status pane, Vault reflection, CouchDB upload, CLI-to-Obsidian synchronisation, Object Storage upload, startup scan, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export in sequence. Start the local CouchDB and MinIO fixtures before running it, or use `test:e2e:obsidian:local-suite:services` to let the wrapper stop leftover fixtures, start fresh fixtures, and stop them again after the run.
|
||||
|
||||
`test:e2e:obsidian:couchdb-upload` reuses the CouchDB variables from `.test.env` or the process environment. It expects a reachable CouchDB service, creates a unique database, starts from configured plug-in data without the device-local compatibility marker, and verifies the copied-or-restored Vault explanation in the actual compatibility dialogue. It captures the summary and details, resumes explicitly, confirms that the marker was recorded, creates a note in real Obsidian, commits the note into the local database, runs one-shot synchronisation, and verifies that the remote database contains both the metadata document and its chunk documents.
|
||||
|
||||
@@ -157,6 +160,7 @@ Useful environment variables:
|
||||
- `E2E_OBSIDIAN_DIALOG_TIMEOUT_MS`: timeout for a representative Svelte dialogue to mount, expose its principal controls, and close; default is 10 seconds.
|
||||
- `E2E_OBSIDIAN_SETTINGS_TIMEOUT_MS`: timeout for the settings pane and its deletion controls to become visible; default is 10 seconds.
|
||||
- `E2E_OBSIDIAN_REVIEW_HARNESS_TIMEOUT_MS`: timeout for Review Harness view and action boundaries; default is 15 seconds.
|
||||
- `E2E_OBSIDIAN_P2P_PANE_TIMEOUT_MS`: timeout for the P2P status pane and its principal connection control; default is 10 seconds.
|
||||
- `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.
|
||||
|
||||
@@ -10,7 +10,7 @@ export {
|
||||
withObsidianPage,
|
||||
} from "@vrtmrz/obsidian-test-session";
|
||||
|
||||
export async function captureObsidianDialogue(
|
||||
export async function captureObsidianPage(
|
||||
port: number,
|
||||
filename: string,
|
||||
assertReady: (page: Page) => Promise<void>
|
||||
@@ -25,7 +25,7 @@ export async function captureObsidianDialogue(
|
||||
} catch (error) {
|
||||
const failurePath = screenshotPath.replace(/\.png$/u, ".failure.png");
|
||||
await page.screenshot({ path: failurePath, fullPage: true });
|
||||
console.error(`Dialogue failure screenshot: ${failurePath}`);
|
||||
console.error(`UI failure screenshot: ${failurePath}`);
|
||||
throw error;
|
||||
}
|
||||
await page.screenshot({ path: screenshotPath, fullPage: true });
|
||||
@@ -34,6 +34,14 @@ export async function captureObsidianDialogue(
|
||||
return screenshotPath;
|
||||
}
|
||||
|
||||
export async function captureObsidianDialogue(
|
||||
port: number,
|
||||
filename: string,
|
||||
assertReady: (page: Page) => Promise<void>
|
||||
): Promise<string> {
|
||||
return await captureObsidianPage(port, filename, assertReady);
|
||||
}
|
||||
|
||||
export async function captureJsonResolveDialogue(port: number): Promise<string> {
|
||||
return await captureObsidianDialogue(port, "hidden-file-json-resolve-dialogue.png", async (page) => {
|
||||
const optionAB = page.locator('label:has(input[name="disp"][value="AB"])');
|
||||
|
||||
@@ -17,6 +17,7 @@ const testSteps: Step[] = [
|
||||
{ name: "Svelte dialogue mounts", args: ["run", "test:e2e:obsidian:dialog-mounts"] },
|
||||
{ name: "settings UI", args: ["run", "test:e2e:obsidian:settings-ui"] },
|
||||
{ name: "Review Harness", args: ["run", "test:e2e:obsidian:review-harness"] },
|
||||
{ name: "P2P status pane", args: ["run", "test:e2e:obsidian:p2p-pane"] },
|
||||
{ name: "vault reflection", args: ["run", "test:e2e:obsidian:vault-reflection"] },
|
||||
{ name: "CouchDB upload", args: ["run", "test:e2e:obsidian:couchdb-upload"] },
|
||||
{
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { assertNoHorizontalOverflow } from "@vrtmrz/obsidian-test-session";
|
||||
import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts";
|
||||
import { createE2eObsidianDeviceLocalState, waitForLiveSyncCoreReady } from "../runner/liveSyncWorkflow.ts";
|
||||
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
|
||||
import { captureObsidianPage, obsidianRemoteDebuggingPort, withObsidianPage } from "../runner/ui.ts";
|
||||
import { createTemporaryVault } from "../runner/vault.ts";
|
||||
|
||||
const uiTimeoutMs = Number(process.env.E2E_OBSIDIAN_P2P_PANE_TIMEOUT_MS ?? 10000);
|
||||
|
||||
type ObsidianTestApp = {
|
||||
commands?: { executeCommandById(commandId: string): boolean };
|
||||
};
|
||||
|
||||
type ObsidianTestGlobal = typeof globalThis & { app?: ObsidianTestApp };
|
||||
|
||||
async function openP2PStatusPane(): Promise<void> {
|
||||
const opened = await withObsidianPage(obsidianRemoteDebuggingPort(), async (page) => {
|
||||
return await page.evaluate(
|
||||
(commandId) => (globalThis as ObsidianTestGlobal).app?.commands?.executeCommandById(commandId) === true,
|
||||
"obsidian-livesync:open-p2p-server-status"
|
||||
);
|
||||
});
|
||||
if (!opened) {
|
||||
throw new Error("The P2P status command was not registered or could not be executed.");
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyP2PStatusPane(): Promise<string> {
|
||||
await openP2PStatusPane();
|
||||
return await captureObsidianPage(obsidianRemoteDebuggingPort(), "p2p-status-pane.png", async (page) => {
|
||||
const heading = page.getByRole("heading", { name: "Signalling Status" }).last();
|
||||
await heading.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||
const pane = heading.locator(
|
||||
"xpath=ancestor::*[contains(concat(' ', normalize-space(@class), ' '), ' workspace-leaf-content ')][1]"
|
||||
);
|
||||
await pane.getByText("Connection:", { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||
await pane.getByRole("button", { name: "Open connection" }).waitFor({
|
||||
state: "visible",
|
||||
timeout: uiTimeoutMs,
|
||||
});
|
||||
await assertNoHorizontalOverflow(page, pane, { label: "P2P status pane" });
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
session = await startObsidianLiveSyncSession({
|
||||
binary,
|
||||
cliBinary: cli.binary,
|
||||
vault,
|
||||
startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000),
|
||||
pluginData: {
|
||||
doctorProcessedVersion: "0.25.27",
|
||||
isConfigured: true,
|
||||
liveSync: false,
|
||||
notifyThresholdOfRemoteStorageSize: -1,
|
||||
periodicReplication: false,
|
||||
P2P_Enabled: false,
|
||||
P2P_AutoStart: false,
|
||||
syncAfterMerge: false,
|
||||
syncOnEditorSave: false,
|
||||
syncOnFileOpen: false,
|
||||
syncOnSave: false,
|
||||
syncOnStart: false,
|
||||
},
|
||||
localStorageEntries: createE2eObsidianDeviceLocalState(vault.name),
|
||||
});
|
||||
await waitForLiveSyncCoreReady(cli.binary, session.cliEnv);
|
||||
const screenshot = await verifyP2PStatusPane();
|
||||
console.log(`P2P status pane mounted without network fixtures. Screenshot: ${screenshot}`);
|
||||
} 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