Skip to content

Using with obsidian-typings

This package has no runtime dependency on obsidian-typings, but it works seamlessly alongside it.

obsidian-typings uses declare module 'obsidian' to augment the obsidian types with dozens of internal properties (App.internalPlugins, App.commands, and so on). That makes import('obsidian').App a superset of what obsidian.d.ts declares on its own. The mocks implement only the public API, so the two are structurally incompatible until something bridges them.

The obsidian-test-mocks/obsidian-typings/* entry points install that bridge: they define the obsidian-typings internal names on the mock prototypes, delegating to the mocks’ own __-suffixed members. Add the setup file for your runner after the main one.

Vitest:

vitest.config.ts
export default defineConfig({
test: {
setupFiles: [
'obsidian-test-mocks/vitest-setup',
'obsidian-test-mocks/obsidian-typings/vitest-setup'
]
}
});

Jest:

module.exports = {
moduleNameMapper: {
'^obsidian$': 'obsidian-test-mocks/obsidian'
},
setupFiles: [
'obsidian-test-mocks/jest-setup',
'obsidian-test-mocks/obsidian-typings/jest-setup'
]
};

Other frameworks — use the generic setup entry point:

import {
setup,
teardown
} from 'obsidian-test-mocks/obsidian-typings/setup';
beforeAll(() => setup());
afterAll(() => teardown());
Classobsidian-typings nameBacking mock member
AbstractInputSuggesttextInputEltextInputEl__
CapacitorAdapterinsensitiveinsensitive__
Component_childrenchildren__
Component_loadedloaded__
FileSystemAdapterinsensitiveinsensitive__
Menuitemsitems__
MenusetSectionSubmenusectionSubmenus__
MenuItemsetSubmenusetSubmenu__()
MenuItemsubmenusubmenu__
MetadataCachecomputeMetadataAsync(parses the buffer as markdown)
MetadataCachefileCachefileCache__
MetadataCachemetadataCachemetadataByHash__
ModalbgElbgEl__
ModalheaderElheaderEl__
SettingsetVisibility(toggles settingEl)
SuggestModalinstructionsElinstructionsEl__
TAbstractFiledeleteddeleted__
TFoldergetParentPrefixgetParentPrefix__()
VaultexistsgetAbstractFileByPathInsensitive__()
VaultgetAbstractFileByPathInsensitivegetAbstractFileByPathInsensitive__()
VaultgetAvailablePathgetAvailablePath__()
VaultgetAvailablePathForAttachmentsgetAvailablePathForAttachments__()
VaultgetConfiggetConfig__()
VaultsetConfigsetConfig__()
WorkspaceLeafonOpenTabHeaderMenu(present but inert — a no-op)

After setup, code written against the obsidian-typings names works through the strict proxy instead of throwing:

const component = Component.create__();
component.load();
// With obsidian-typings/vitest-setup, this works instead of throwing:
console.log(component._loaded); // true

The entry point also exports teardown(), which removes every bridge again.

For anything the automatic bridging does not cover, use asOriginalType__() to hand a mock to code that expects an obsidian type:

import type { App as AppOriginal } from 'obsidian';
import { App } from 'obsidian-test-mocks/obsidian';
function myPluginHelper(app: AppOriginal): void { /* ... */ }
const app = App.createConfigured__();
myPluginHelper(app.asOriginalType__());

With obsidian-typings installed the returned type includes the augmented properties, so internal members can be assigned type-safely:

const app = App.createConfigured__();
const original = app.asOriginalType__();
// Type-safe with obsidian-typings — no casts needed
original.internalPlugins = { manifests: {} };

Without obsidian-typings, assign them through a Record cast:

const app = App.createConfigured__();
(app as unknown as Record<string, unknown>)['internalPlugins'] = { manifests: {} };

Either way, reading a property that was never assigned — and is not covered by the automatic bridging — still throws a strict mock error at runtime, whether or not obsidian-typings makes it compile.