Skip to content

Lib

The shared library bag injected into every evalInObsidian callback as CommonArguments.lib.

Two layers compose into this one bag. The base — the harness-provided renderer-driving helpers declared below (the trusted-input primitives and Lib.waitUntil) — is always present. On top, provider packages register a renderer-side resolver via registerLibResolver to Object.assign their whole renderer-safe library at runtime, and augment this augmentable interface (the i18next CustomTypeOptions idiom) via declare module 'obsidian-integration-testing' to type it. Multiple providers compose: their exports merge at runtime and their augmentations merge in the type system (interface Lib extends …).

Import:

import type { Lib } from 'obsidian-integration-testing';

Example:

declare module 'obsidian-integration-testing' {
interface Lib extends (typeof import('obsidian-dev-utils/__merged')) {}
}

Signature:

export interface Lib

Methods

MethodReturnsDescription
createNote(this, params)Promise<TFile>Creates a note and does not return until its content is verifiably on disk, rewriting it if it is not.

Use this instead of app.vault.create in any suite that may run on Android. The emulator transport loses roughly **0.9 %** of vault.create writes (measured: 7 lost in 800 creates): the file lands **0 bytes** on disk while Obsidian's in-memory TFile.stat reports the full byte count, and it does not heal on its own. A suite doing ~34 creates per run therefore has a ~26 % chance of at least one lost write, and whichever test loses that lottery fails on a waitUntil for content that was never written — which is why it reads as an unrelated per-test flake rather than one shared cause.

Verification is by **reading the note back**, never by inspecting TFile.stat: stat is exactly the field that lies here. A rewrite through vault.modify with the same content lands correctly (also measured), so a lost write costs a retry rather than a failure. A note whose content still does not match after the bounded retries throws, naming the path and both lengths, so a genuinely broken write fails loudly instead of spinning.

Harmless everywhere else — on a transport that does not lose writes the read-back matches first time and nothing is rewritten.
hoverElement(this, params)Promise<void>Moves the mouse pointer to the center of an element using **trusted** Electron pointer input, then polls until the element actually matches :hover.

Because the move is trusted (see Lib.moveMouse), the real :hover state takes effect in the CSS engine — real theme var() values and real compositing — instead of a hand-simulated cascade. It polls the live element.matches(':hover') state (not a fixed delay), so it is robust under shared-instance load. It targets the single shared window's **global** pointer, so only one element is hovered at a time.
moveMouse(this, params)voidMoves the mouse pointer to the given web-contents coordinates using a **trusted** Electron pointer move.

A trusted move (injected via Electron's webContents.sendInputEvent) updates the real pointer state in the CSS engine, so :hover rules genuinely apply — unlike dispatchEvent(new MouseEvent('mouseover')), which is untrusted and never sets :hover. It targets the single shared window's **global** pointer, so only one element is hovered at a time.

This is the low-level primitive: it performs a single move and does **not** wait for any state to settle (callers poll their own readiness signal). Prefer Lib.hoverElement / Lib.unhoverElement for element-relative moves; use moveMouse directly when an element-relative target does not fit (e.g. an element spanning the full viewport width).

Synchronous: injecting the trusted move does no real awaiting, so the caller does not need to await it.
pressKey(this, params)voidPresses a single key (optionally with modifiers) using **trusted** Electron keyboard input, firing the full real key pipeline — keydownkeypressbeforeinputinputkeyup.

This is the key-press analog of Lib.typeIntoEditor: it injects a trusted keyDowncharkeyUp sequence via Electron's webContents.sendInputEvent, so it is delivered to the window's DOM-focused element and flows through the real input pipeline — unlike dispatchEvent(new KeyboardEvent(...)), which is untrusted (isTrusted: false) and ignored by CodeMirror and most key handlers. Use it for special keys ('Enter', 'Escape', 'Tab', arrow keys) and modifier combinations (Shift+Enter, Ctrl+A) that Lib.typeIntoEditor (which types printable text) does not cover.

This is the low-level primitive: it injects the key press and does **not** poll for any effect (a key press has no universal observable outcome — Enter edits the document, Escape closes a modal, ArrowDown moves the selection). The caller focuses the intended target first, then awaits the expected effect via Lib.waitUntil. It targets the single shared window's **global** focus, so only the DOM-focused element receives the key.

Synchronous: injecting the trusted key press does no real awaiting, so the caller does not need to await it.
typeIntoEditor(this, params)Promise<void>Types text into a CodeMirror Editor using **trusted** Electron keyboard input.

Typing is pressing each character key in turn: this focuses the editor (caret to end) and presses every code point of text via Lib.pressKey — the same trusted keyDowncharkeyUp a real user produces. Each keystroke is delivered to the window's DOM-focused element and flows through CodeMirror's real input pipeline, so the typed text reaches the document **only if the editor genuinely holds focus**. This makes "the user typed into the editor" a faithful end-to-end check, unlike dispatchEvent(new KeyboardEvent(...)) (untrusted — ignored by CodeMirror) or execCommand('insertText') (mutates the selection even when the editor is not focused, masking focus bugs as false positives).

After injecting the keystrokes it polls until the document reflects the input, or a bounded timeout elapses (the expected outcome when the editor is read-only and rejects the input, or when focus was stolen).
unhoverElement(this, params)Promise<void>Moves the mouse pointer to a point just outside an element's bounding box using a **trusted** Electron pointer move, then polls until the element no longer matches :hover.

The inverse of Lib.hoverElement. It targets the single shared window's **global** pointer, so only one element is hovered at a time. When an element spans the full viewport (no point outside its box is reachable), use Lib.moveMouse directly to move the pointer to a known empty coordinate instead.
waitUntil(this, params)Promise<void>Polls a predicate until it becomes truthy, or rejects once a bounded timeout elapses.

Integration-test evalInObsidian callbacks routinely need to wait for an asynchronous effect to settle (a view to open, a DOM node to appear, a setting to apply). Because the callback is serialized via toString() and cannot import modules, it can't reuse obsidian-dev-utils' retryWithTimeout / runWithTimeout. This helper is the shared, injected replacement for the per-closure poll loops consumers would otherwise hand-roll.

The predicate may be synchronous or asynchronous — it is awaited on every poll. It is checked immediately, then re-checked every intervalInMilliseconds until it returns truthy or timeoutInMilliseconds elapses, at which point the returned Promise rejects (the error includes message when provided).