AppiumTransport
Transport that communicates with Obsidian Mobile via Appium WebView JS injection.
Evaluates expressions by switching to the WEBVIEW_md.obsidian context and calling execute(). Manages vaults by writing to the WebView’s localStorage (which Obsidian uses as its vault registry on mobile) and pushing files to the device.
Import:
import { AppiumTransport } from 'obsidian-integration-testing';Signature:
export class AppiumTransport implements ObsidianTransportImplements: ObsidianTransport
Constructor
new AppiumTransport(config: AppiumTransportConfig)Creates a new Appium transport.
Properties
| Property | Type | Description |
|---|---|---|
| isMobile | true | Indicates whether this transport is for a mobile platform. Always true for this transport. |
Methods
| Method | Returns | Description |
|---|---|---|
| beginConsoleCapture() | Promise<ConsoleCaptureHandle> | Begins a console capture by stamping the device log with a unique marker. Uses adb shell log to write a marker into logcat at capture start so a later AppiumTransport.readConsoleCaptureSince can slice out everything the WebView logged afterwards — without the invasive logcat -c buffer clear. |
| captureScreenshot(_params) | Promise<Uint8Array> | Captures a PNG screenshot of the device's screen. The image is always the device's native framebuffer — there is no mobile equivalent of the desktop viewport override, so CaptureScreenshotParams.widthInPixels / CaptureScreenshotParams.heightInPixels are ignored. Size a mobile capture by choosing an AVD whose screen geometry already matches what the image has to be. |
| captureScreenshot(params) | Promise<Uint8Array> | Captures a PNG screenshot of the running Obsidian instance. Optional and platform-specific, like the other members here. Implemented by DesktopCdpTransport (CDP Page.captureScreenshot, optionally with the viewport pinned to an exact size) and by AppiumTransport (takeScreenshot, always the device's native framebuffer — the size knobs are ignored, so a mobile capture is sized by choosing an AVD with the wanted screen geometry).(Inherited from ObsidianTransport) |
| dispose() | Promise<void> | Ends the Appium session. Only deletes the session if this transport owns it. Transports created via attach() (test workers reusing the global setup's session) skip deletion so the session remains available for the owning process. |
| disposeSync() | void | Synchronous disposal for use in process.on('exit') handlers where async work is not possible.Implementations should perform only synchronous cleanup here (e.g. killing child processes). Async operations like unregistering vaults are skipped. (Inherited from ObsidianTransport) |
| evaluate(expression, _options) | Promise<string> | Evaluates a JavaScript expression inside Obsidian Mobile's WebView. Switches to the WEBVIEW_md.obsidian context, executes the expression, and returns the result string. |
| evaluate(expression, options) | Promise<string> | Evaluates a JavaScript expression string inside Obsidian and returns the raw string result. The transport normalizes the output — stripping transport-specific prefixes and handling transport-specific errors — so callers receive a clean result string (e.g. a JSON string, or (no output)).(Inherited from ObsidianTransport) |
| getSessionInfo() | AppiumSessionInfo | Returns the session connection info needed to reattach to this session from another process (e.g. a test worker). |
| preflightCheck(_vaultPath) | Promise<void> | Verifies that the Obsidian app is running and the WebView is available. |
| preflightCheck(vaultPath) | Promise<void> | Runs transport-specific preflight checks before evaluation. For example, the CLI transport verifies that the vault is registered, the CLI is enabled, and the CLI binary is in PATH. (Inherited from ObsidianTransport) |
| pushFiles(vaultPath, _files) | Promise<void> | Pushes vault files to the device via compressed adb push.Creates a tar.gz archive of the local vault directory, pushes it to the device as a single file, and extracts it in-place. This avoids the webdriver RangeError on large base64 payloads and is significantly faster than per-file browser.pushFile() calls. |
| pushFiles(vaultPath, files) | Promise<void> | Pushes files into a vault directory on the target device. On desktop this is a no-op (files are written to the local filesystem directly). On mobile this uses the device's file transfer mechanism (e.g. Appium pushFile).(Inherited from ObsidianTransport) |
| readConsoleCaptureSince(handle) | Promise<string | undefined> | Dumps adb logcat and returns the WebView console/error output logged since the marker from AppiumTransport.beginConsoleCapture.Filters to the Chromium/WebView tags that carry JS console.* output and uncaught errors, and caps the result length. Bounded, post-hoc, failure-path-only — never a live monitor. |
| registerVault(vaultPath) | Promise<void> | Registers a vault on mobile by pushing files and configuring localStorage. The registration flow: 1. Push a minimal .obsidian/app.json to the device so Obsidian recognizes the vault 2. Switch to the WebView context 3. Add the vault to localStorage (mobile-external-vaults, mobile-selected-vault, enable-plugin-<path>) 4. Trigger location.reload() so Obsidian re-reads localStorage and opens the vault 5. Wait for app.workspace.layoutReadyExisting vault registrations in localStorage are preserved (append, not overwrite) — except the **harness's own** temp-vault-* registrations from earlier runs, which are pruned when AppiumTransportConfig.shouldSweepLeftovers is on. Their directories are already gone (the factory sweeps them before the app launches), and it is this registry — not the filesystem — that Obsidian enumerates at startup, so leaving them is what makes each failed run slow the next one down. |
| unregisterVault(vaultPath) | Promise<void> | Unregisters a vault on mobile by removing it from localStorage. Preserves other vault registrations. If the unregistered vault was selected, switches to the first remaining vault (or clears the selection). The vault's **files are then removed from the device over adb, whether or not the localStorage step succeeded** — deliberately, because the localStorage step goes through the WebView and a dead WebView is exactly what most Android failures are. Routing the removal through the app was what made every failed run leak its vault (Vault cleanup error (non-fatal): no such window), so the filesystem-level removal must not depend on it. |
Links to this page: