obsidian-integration-testing
v10.3.2
Published
Simplifies integration testing of Obsidian plugins.
Maintainers
Readme
obsidian-integration-testing
A set of helpers that simplify integration testing of Obsidian plugins against a running Obsidian instance.
Your tests run inside a real Obsidian — the real App, the real vault, the real workspace, internal APIs included. By default the harness launches and owns an isolated instance in a temporary --user-data-dir, so your own Obsidian (its config, vault registry, open window, auto-update) is never touched. The same test code runs on the desktop app over CDP and on Obsidian Mobile over Appium.
Documentation
Full documentation lives at mnaoumov.dev/obsidian-integration-testing.
- Getting started — install, wire up Vitest or Jest, first assertion.
- Writing tests — how
evalInObsidianexecutes a callback, arguments, results,ContextId, internal APIs. - Simulating user input — trusted keyboard and pointer events, and
waitUntil. - The
libbag — inject your own helpers into callbacks;createNote. - Vaults and fixtures — temporary vaults, pre-populating files, seeding a plugin's
demo-vault/, non-plugin consumers. - Transport modes — version pinning, window visibility, attaching to a running Obsidian, multi-platform runs.
- Android testing — Appium setup, AVD provisioning, troubleshooting.
- Version matrix — run the suites across the supported Obsidian range.
- Leftover cleanup — what a dead run leaks, and how the sweeps handle it.
- Ad-hoc debugging —
connectToCdpand the CLI. - API reference — every exported function, class and type, generated from the TSDoc.
Installation
npm install --save-dev obsidian-integration-testingYou also need Obsidian (the desktop app) installed, and Node.js 22+.
Quick start
The global setup expects your built plugin in dist/dev or dist/build (whichever has a newer main.js), with a manifest.json at the root of the chosen folder. The setup creates a temporary vault, copies the build into it, and enables the plugin.
Vitest
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
fileParallelism: false,
globalSetup: ['obsidian-integration-testing/vitest-global-setup-plugin']
}
});Jest
// jest.config.ts
export default {
globalSetup: 'obsidian-integration-testing/jest-global-setup-plugin',
globalTeardown: 'obsidian-integration-testing/jest-global-teardown-plugin'
};Write a test
import { describe, expect, it } from 'vitest';
import { evalInObsidian } from 'obsidian-integration-testing';
import { getTemporaryVault } from 'obsidian-integration-testing/vitest-global-setup-plugin';
describe('my-plugin', () => {
const vault = getTemporaryVault();
it('should be enabled', async () => {
const isEnabled = await evalInObsidian({
input: { pluginId: 'my-plugin' },
callback: ({ app, pluginId }) => app.plugins.enabledPlugins.has(pluginId),
vaultPath: vault.path
});
expect(isEnabled).toBe(true);
});
});The callback is serialized and executed inside Obsidian, so it must be self-contained, and both its arguments and its return value must be JSON-serializable. See Writing tests for the full rules, and Getting started for the Jest equivalents and the transport options.
Support
My other Obsidian resources
See my other Obsidian resources.
