mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-19 19:16:01 +00:00
163 lines
16 KiB
Markdown
163 lines
16 KiB
Markdown
# Real Obsidian E2E Runner
|
|
|
|
This directory contains the experimental real Obsidian end-to-end runner.
|
|
|
|
The generic application discovery, isolated-vault, plug-in installation, process lifecycle, CLI, CDP, and readiness implementation comes from `@vrtmrz/obsidian-test-session`. The small modules under `runner/` preserve LiveSync's existing imports and supply its plug-in ID and artefact location. LiveSync-specific fixtures, services, settings, workflows, and assertions remain in this repository.
|
|
|
|
The current smoke runner verifies the launch path and the loaded plug-in's Service Context composition:
|
|
|
|
1. create a temporary vault,
|
|
2. install the built Self-hosted LiveSync plug-in artifacts,
|
|
3. launch real Obsidian,
|
|
4. open the temporary vault through `obsidian-cli`,
|
|
5. enable Obsidian community plug-ins for the temporary app profile,
|
|
6. reload Self-hosted LiveSync through `obsidian-cli`,
|
|
7. verify through `obsidian-cli eval` that the plug-in is loaded,
|
|
8. observe event and translation results from the actual `ObsidianServiceContext`,
|
|
9. verify that the Service Hub and every exposed service retain that exact Context,
|
|
10. optionally drive a real vault or CouchDB workflow through Obsidian's own API, and
|
|
11. terminate Obsidian and remove the temporary vault.
|
|
|
|
The runner does not require Self-hosted LiveSync to expose an E2E-only bridge. Readiness is checked from outside the plug-in through Obsidian's own CLI.
|
|
|
|
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. Add generic Obsidian bootstrap improvements to Fancy Kit; keep LiveSync behaviour and scenario helpers here.
|
|
|
|
Each test vault uses an isolated Obsidian profile. The runner creates temporary directories for `HOME`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, and Electron `--user-data-dir`, writes the vault registry into those directories, pre-seeds the temporary Chromium local storage so community plug-ins are trusted for that generated vault ID, and passes the same environment to `obsidian-cli`. This is intended to keep real Obsidian E2E runs separate from a developer's daily Obsidian profile and vault registry.
|
|
|
|
## Local Setup
|
|
|
|
Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location.
|
|
|
|
For an AppImage on Linux without FUSE, use the helper script:
|
|
|
|
```bash
|
|
npm run test:e2e:obsidian:install-appimage
|
|
```
|
|
|
|
The script downloads Obsidian `1.12.7` for the current architecture, stores it in `_testdata/obsidian`, and extracts it to `_testdata/obsidian/squashfs-root`. The runner checks `_testdata/obsidian/squashfs-root/obsidian` before the AppImage path.
|
|
|
|
These tests are intended for local verification, not the default CI gate. Reuse the installed Obsidian application, or reuse the extracted AppImage directory between local runs:
|
|
|
|
- set `OBSIDIAN_BINARY` to an installed Obsidian executable,
|
|
- keep `_testdata/obsidian/squashfs-root` after running the AppImage installer, or
|
|
- run `test:e2e:obsidian:install-appimage` again only when the local Obsidian version should change.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run test:contract:contexts
|
|
npm run test:contract:context:webapp
|
|
npm run test:contract:context:cli
|
|
npm run test:contract:context:obsidian
|
|
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:dialog-mounts
|
|
npm run test:e2e:obsidian:vault-reflection
|
|
npm run test:e2e:obsidian:couchdb-upload
|
|
npm run test:e2e:obsidian:cli-to-obsidian-sync
|
|
npm run test:e2e:obsidian:minio-upload
|
|
npm run test:e2e:obsidian:startup-scan
|
|
npm run test:e2e:obsidian:two-vault-sync
|
|
npm run test:e2e:obsidian:hidden-file-snippet-sync
|
|
npm run test:e2e:obsidian:customisation-sync
|
|
npm run test:e2e:obsidian:setting-markdown-export
|
|
npm run test:e2e:obsidian:local-suite
|
|
npm run test:e2e:obsidian:local-suite:services
|
|
```
|
|
|
|
`test:contract:contexts` runs the directly observable host contract against the Obsidian, CLI, and Webapp compositions. It verifies event and translation results, host-specific capabilities, and that the CLI and Webapp Service Hubs pass one exact Context to all exposed services. `test:contract:context:webapp` runs only the Webapp part.
|
|
|
|
`test:contract:context:cli` builds the Node CLI and runs its existing Deno setup, put, read, list, information, remove, conflict-resolution, and revision workflow. `test:contract:context:obsidian` builds the plug-in and runs the real-Obsidian smoke test, including the Context inspection. These runtime scripts are local validation entry points and are not added to the default CI gate by this change.
|
|
|
|
`test:e2e:obsidian:dialog-mounts` starts a temporary real Obsidian session and exercises two representative Svelte dialogue routes: remote server selection through `SetupManager`, and Setup URI entry through the registered command. The session pre-seeds a configured, inactive plug-in state so that onboarding and migration prompts do not interfere with the dialogues under test. It requires each dialogue title and its principal controls to be visible, captures desktop and mobile screenshots, closes them through their normal user controls, and verifies that the remote-selection promise settles without an error. This covers the host, context, component, and result boundaries moved during the Commonlib package extraction without applying settings or contacting a remote service.
|
|
|
|
The mobile pass uses Obsidian's `app.emulateMobile(true)`, a 390 by 844 CSS-pixel viewport, and explicit iPhone-style safe-area insets of 47 pixels at the top and 34 pixels at the bottom. The public `@vrtmrz/obsidian-test-session` layout assertions require each modal to remain within the viewport and safe area without horizontal overflow. They also require the Obsidian Close control to remain within the safe area and provide at least a 44 by 44 CSS-pixel touch target. The runner clicks that control to verify actionability, then completes the explicit cancellation path. These simulated checks cover deterministic layout and interaction boundaries; they do not claim to reproduce a native operating-system overlay.
|
|
|
|
`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, Svelte dialogue mounting, 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, configures Self-hosted LiveSync through `obsidian-cli eval`, 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.
|
|
|
|
The same workflow checks the two remote-activity status boundaries. It first holds a real CouchDB request at the selected fetch implementation and confirms that `🌐N` is visible while `📲` is absent. It then holds the real one-shot replication immediately before its replicator call, confirms that `📲` is visible while no physical request is active, releases it, and requires the finite and bounded activity counts to return to zero, the request and response counts to balance, and both indicators to disappear. Finally, it creates a remote-only chunk, holds the real on-demand fetch immediately before its remote call, makes the same logical active and idle assertions, and verifies that the fetched chunk is written into the local database. These gates make the active states deterministic without replacing the remote request or operation.
|
|
|
|
If this status workflow fails while Obsidian is running, it writes a full-page screenshot and a JSON snapshot of the status text and counters under `/tmp/obsidian-livesync-e2e`. The dialogue-mount workflow leaves desktop and mobile screenshots for both representative Svelte routes, and the Hidden File Sync workflow captures the successfully displayed JSON Resolve dialogue before selecting an option. The suite therefore records representative evidence without capturing every interaction. Set `E2E_OBSIDIAN_DIAGNOSTICS_DIR` to use another directory.
|
|
|
|
`test:e2e:obsidian:cli-to-obsidian-sync` is the cross-runtime compatibility check for the official LiveSync CLI and the real Obsidian plug-in. Build the plug-in first, and build the local CLI too when no external CLI command is selected. The script uses E2EE, Path Obfuscation, and the current preferred chunk settings to create and synchronise a note through the CLI, starts real Obsidian with an isolated Vault and profile, synchronises the same CouchDB database, and verifies that the plug-in materialises identical note content. This covers the boundary that CLI-only and plug-in-only round trips do not exercise.
|
|
|
|
By default, the compatibility check runs `node src/apps/cli/dist/index.cjs`. Set `LIVESYNC_CLI_COMMAND` to test another CLI build or distribution. The value may be a quoted command line or a JSON array of executable and prefix arguments; the scenario arguments are appended without going through a shell.
|
|
|
|
For example, to test an executable on `PATH`:
|
|
|
|
```bash
|
|
LIVESYNC_CLI_COMMAND='livesync-cli' npm run test:e2e:obsidian:cli-to-obsidian-sync
|
|
```
|
|
|
|
On Linux, a multi-architecture published Docker image can run against the local CouchDB fixture by sharing the temporary directory, using host networking, preserving the host user's file ownership, and overriding the image entrypoint so that the runner can supply its explicit database path. Images published before ARM64 support remain AMD64-only and require configured Docker emulation on an ARM host.
|
|
|
|
```bash
|
|
LIVESYNC_CLI_COMMAND="docker run --rm --network host --user $(id -u):$(id -g) --volume /tmp:/tmp --entrypoint node ghcr.io/vrtmrz/livesync-cli:edge /app/dist/index.cjs" \
|
|
npm run test:e2e:obsidian:cli-to-obsidian-sync
|
|
```
|
|
|
|
`test:e2e:obsidian:minio-upload` reuses the Object Storage variables from `.test.env` or the process environment. It expects a reachable S3-compatible service, configures Self-hosted LiveSync for Object Storage through `obsidian-cli eval`, creates a note in real Obsidian, runs one-shot Journal Sync, and verifies through the AWS SDK that objects were written under a unique bucket prefix. Adapter tests separately observe an in-progress SDK command, while this real-runtime workflow verifies the resulting request counters advance and rebalance.
|
|
|
|
`test:e2e:obsidian:startup-scan` configures a temporary CouchDB database, stops Obsidian, writes a note directly into the vault, restarts Obsidian, and verifies from CouchDB that the boot-time scan picked up the offline file.
|
|
|
|
`test:e2e:obsidian:two-vault-sync` runs a two-vault note synchronisation workflow. It verifies note creation, update, ordinary rename, a case-only file name change within the same directory, deletion, per-device target filters where one vault ignores a note that the other vault synchronises, and a separate encrypted round-trip with Path Obfuscation enabled. Directory case changes deliberately remain outside this scenario because they require directory-aware rename handling. The optional Markdown conflict automatic merge check can be enabled with `E2E_OBSIDIAN_INCLUDE_MARKDOWN_CONFLICT=true`, but it is not part of the default local suite.
|
|
|
|
`test:e2e:obsidian:hidden-file-snippet-sync` runs a two-vault hidden file round-trip. It verifies creation and deletion of a real `.obsidian/snippets/*.css` file, automatic JSON conflict merging for a hidden file with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target patterns where one vault ignores a hidden file that the other vault synchronises.
|
|
|
|
`test:e2e:obsidian:customisation-sync` runs a two-vault Customisation Sync workflow. It scans a real snippet CSS file, config JSON file, and sample plug-in fixture into per-file Customisation Sync data, synchronises the entries through CouchDB, applies them on the second vault, verifies the resulting `.obsidian` files, propagates a snippet update, and verifies deletion of the source-vault snippet sync data without confusing it with the target vault's own applied copy.
|
|
|
|
`test:e2e:obsidian:setting-markdown-export` enables setting Markdown export, waits for the generated Markdown file in the vault, and verifies that credentials are omitted when `writeCredentialsForSettingSync=false`.
|
|
|
|
Start the local fixtures first when they are not already running:
|
|
|
|
```bash
|
|
npm run test:docker-couchdb:start
|
|
npm run test:docker-s3:start
|
|
npm run test:e2e:obsidian:local-suite
|
|
```
|
|
|
|
Or let the wrapper manage both fixtures:
|
|
|
|
```bash
|
|
npm run test:e2e:obsidian:local-suite:services
|
|
```
|
|
|
|
Useful environment variables:
|
|
|
|
- `OBSIDIAN_BINARY`: explicit Obsidian executable path.
|
|
- `E2E_OBSIDIAN_VERSION`: Obsidian AppImage version for `test:e2e:obsidian:install-appimage`; default is `1.12.7`.
|
|
- `E2E_OBSIDIAN_APPIMAGE_ARCH`: AppImage architecture override, such as `arm64` or `x86_64`.
|
|
- `E2E_OBSIDIAN_APPIMAGE_URL`: explicit AppImage URL override.
|
|
- `E2E_OBSIDIAN_DOWNLOAD_DIR`: AppImage download and extraction directory; default is `_testdata/obsidian`.
|
|
- `E2E_OBSIDIAN_FORCE_DOWNLOAD=true`: re-download the AppImage even when it exists.
|
|
- `E2E_OBSIDIAN_SKIP_EXTRACT=true`: download the AppImage without extracting it.
|
|
- `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS`: smoke timeout in milliseconds.
|
|
- `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_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_LIVESYNC_CLI_TIMEOUT_MS`: timeout for each official LiveSync CLI invocation in the CLI-to-Obsidian compatibility check; default is 60 seconds.
|
|
- `LIVESYNC_CLI_COMMAND`: optional LiveSync CLI executable and prefix arguments used by the CLI-to-Obsidian compatibility check. The default is the locally built CLI.
|
|
- `E2E_OBSIDIAN_FILE_TIMEOUT_MS`: timeout for waiting until a note created through Obsidian's vault API is reflected to disk.
|
|
- `E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS`: timeout for waiting until Self-hosted LiveSync reports that its core lifecycle and local database are ready.
|
|
- `E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS`: timeout for waiting until a file appears in Self-hosted LiveSync's local database.
|
|
- `E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS`: timeout for waiting until CouchDB contains uploaded E2E documents.
|
|
- `E2E_OBSIDIAN_REMOTE_ACTIVITY_TIMEOUT_MS`: timeout for an observed remote activity to enter or leave its status boundary; default is 30 seconds.
|
|
- `E2E_OBSIDIAN_DIAGNOSTICS_DIR`: directory for screenshots and status snapshots captured after a remote-activity failure; default is `/tmp/obsidian-livesync-e2e`.
|
|
- `E2E_OBSIDIAN_OBJECT_STORAGE_TIMEOUT_MS`: timeout for waiting until Object Storage contains uploaded E2E objects.
|
|
- `E2E_OBSIDIAN_KEEP_COUCHDB=true`: keep the temporary CouchDB database for inspection.
|
|
- `E2E_OBSIDIAN_KEEP_OBJECT_STORAGE=true`: keep the temporary Object Storage prefix for inspection.
|
|
- `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.
|
|
- `E2E_OBSIDIAN_USE_USER_DATA_DIR=false`: disable the isolated Electron `--user-data-dir` argument. This is not recommended for normal local testing.
|
|
- `E2E_OBSIDIAN_ARGS`: override the default Obsidian launch arguments.
|
|
|
|
On headless Linux, the runner automatically uses `/usr/bin/xvfb-run` when no `DISPLAY` or `WAYLAND_DISPLAY` is present.
|