Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436import { describe, it, expect, beforeEach, vi } from 'vitest'import { createTestApp, createMockPluginClass, createTestManifest } from '../loader/__tests__/test-utils'import type { App } from '../app'import type { Plugin } from '../plugin'import type { PluginState } from '../loader/types'
/** * Creates a base mock PluginLoader object with all required methods. * Override specific methods as needed for each test. */function createBaseMockLoader(overrides: Record<string, unknown> = {}) { return { getPlugin: vi.fn().mockReturnValue(null), getPluginIds: vi.fn().mockReturnValue([]), getPluginState: vi.fn().mockReturnValue(null), enablePlugin: vi.fn().mockResolvedValue(undefined), disablePlugin: vi.fn(), on: vi.fn().mockReturnValue({ id: Symbol() }), offref: vi.fn(), ...overrides, }}
describe('App.internalPlugins', () => { let app: App
beforeEach(async () => { app = await createTestApp() })
describe('getPluginById', () => { it('returns null for unknown Obsidian internal plugin id', () => { const result = app.internalPlugins.getPluginById('unknown-plugin') expect(result).toBeNull() })
it('returns null for known id when no plugin loader is set', () => { // No plugin loader set yet const result = app.internalPlugins.getPluginById('daily-notes') expect(result).toBeNull() })
it('returns null for known id when Reams plugin is not loaded', () => { // Set up a mock plugin loader with no plugins const mockLoader = createBaseMockLoader() app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('daily-notes') expect(result).toBeNull() })
it('returns adapter for daily-notes when reams-daily-notes is active', async () => { // Create a mock DailyNotesPlugin const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: 'daily', dateFormat: 'YYYY-MM-DD', templatePath: 'templates/daily.md', }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockImplementation((id: string): PluginState | null => { if (id === 'reams-daily-notes') return 'active' return null }), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('daily-notes') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) expect(result!.instance).not.toBeNull() // Periodic Notes expects: { folder, format, template } expect((result!.instance as { options: Record<string, unknown> }).options).toEqual({ folder: 'daily', format: 'YYYY-MM-DD', template: 'templates/daily.md', }) })
it('returns adapter with enabled=false when reams-daily-notes is inactive', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockImplementation((id: string): PluginState | null => { if (id === 'reams-daily-notes') return 'disabled' return null }), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('daily-notes') expect(result).not.toBeNull() expect(result!.enabled).toBe(false) expect(result!.instance).toBeNull() })
it('provides stable identity across calls', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const first = app.internalPlugins.getPluginById('daily-notes') const second = app.internalPlugins.getPluginById('daily-notes') expect(first).toBe(second) // Same object reference })
it('enable() dispatches to app.plugins.enablePlugin', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('disabled' as PluginState), }) app.setPluginLoader(mockLoader)
const adapter = app.internalPlugins.getPluginById('daily-notes') expect(adapter).not.toBeNull() adapter!.enable()
// Wait for the async call await new Promise(resolve => setTimeout(resolve, 0)) expect(mockLoader.enablePlugin).toHaveBeenCalled() expect(mockLoader.enablePlugin.mock.calls[0][0]).toBe('reams-daily-notes') })
it('disable() dispatches to app.plugins.disablePlugin', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const adapter = app.internalPlugins.getPluginById('daily-notes') expect(adapter).not.toBeNull() adapter!.disable()
expect(mockLoader.disablePlugin).toHaveBeenCalledWith('reams-daily-notes') })
it('maps templates to reams-templates', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-templates' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: 'templates', dateFormat: 'YYYY-MM-DD', timeFormat: 'HH:mm:ss', }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-templates') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-templates']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('templates') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) expect((result!.instance as { options: Record<string, unknown> }).options).toEqual({ folder: 'templates', dateFormat: 'YYYY-MM-DD', timeFormat: 'HH:mm:ss', }) })
it('maps file-explorer to reams-file-tree', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-file-tree' }) const plugin = new MockPlugin(app, manifest)
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-file-tree') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-file-tree']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('file-explorer') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) // file-explorer doesn't have settings to map expect((result!.instance as { options: Record<string, unknown> }).options).toEqual({}) })
it('maps global-search to reams-quick-search', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-quick-search' }) const plugin = new MockPlugin(app, manifest)
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-quick-search') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-quick-search']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('global-search') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) })
it('maps switcher to reams-quick-search', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-quick-search' }) const plugin = new MockPlugin(app, manifest)
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-quick-search') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-quick-search']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getPluginById('switcher') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) })
it('switcher and global-search have separate cache entries but share same plugin', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-quick-search' }) const plugin = new MockPlugin(app, manifest)
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-quick-search') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-quick-search']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const switcherAdapter = app.internalPlugins.getPluginById('switcher') const globalSearchAdapter = app.internalPlugins.getPluginById('global-search')
// They should be different adapter objects (different cache entries) expect(switcherAdapter).not.toBe(globalSearchAdapter)
// But both should be enabled (both point to the same underlying plugin) expect(switcherAdapter!.enabled).toBe(true) expect(globalSearchAdapter!.enabled).toBe(true) })
it('updates enabled state dynamically when plugin state changes', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
let currentState: PluginState = 'active' const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockImplementation((): PluginState => currentState), }) app.setPluginLoader(mockLoader)
// Initially active const adapter = app.internalPlugins.getPluginById('daily-notes') expect(adapter!.enabled).toBe(true)
// Simulate state change currentState = 'disabled'
// Same adapter should now show disabled const adapterAgain = app.internalPlugins.getPluginById('daily-notes') expect(adapterAgain).toBe(adapter) // Same reference expect(adapterAgain!.enabled).toBe(false) })
it('reflects current settings in instance.options on each call', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: 'daily', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
// Get initial options const adapter1 = app.internalPlugins.getPluginById('daily-notes') expect((adapter1!.instance as { options: Record<string, unknown> }).options.folder).toBe('daily')
// Change settings plugin.settings.folder = 'journal'
// Get again - should reflect new settings const adapter2 = app.internalPlugins.getPluginById('daily-notes') expect(adapter2).toBe(adapter1) // Same reference expect((adapter2!.instance as { options: Record<string, unknown> }).options.folder).toBe('journal') }) })
describe('getEnabledPluginById', () => { it('returns null for unknown id', () => { const result = app.internalPlugins.getEnabledPluginById('unknown-plugin') expect(result).toBeNull() })
it('returns null when plugin is disabled', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: '', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('disabled' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getEnabledPluginById('daily-notes') expect(result).toBeNull() })
it('returns adapter when plugin is enabled', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: 'daily', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const result = app.internalPlugins.getEnabledPluginById('daily-notes') expect(result).not.toBeNull() expect(result!.enabled).toBe(true) expect((result!.instance as { options: Record<string, unknown> }).options.folder).toBe('daily') })
it('returns null when plugin loader is not set', () => { const result = app.internalPlugins.getEnabledPluginById('daily-notes') expect(result).toBeNull() })
it('returns same adapter reference as getPluginById', async () => { const MockPlugin = await createMockPluginClass() const manifest = createTestManifest({ id: 'reams-daily-notes' }) const plugin = new MockPlugin(app, manifest) as Plugin & { settings: Record<string, unknown> } plugin.settings = { folder: 'daily', dateFormat: 'YYYY-MM-DD', templatePath: '' }
const mockLoader = createBaseMockLoader({ getPlugin: vi.fn().mockImplementation((id: string) => { if (id === 'reams-daily-notes') return plugin return null }), getPluginIds: vi.fn().mockReturnValue(['reams-daily-notes']), getPluginState: vi.fn().mockReturnValue('active' as PluginState), }) app.setPluginLoader(mockLoader)
const fromGetPluginById = app.internalPlugins.getPluginById('daily-notes') const fromGetEnabledPluginById = app.internalPlugins.getEnabledPluginById('daily-notes') expect(fromGetPluginById).toBe(fromGetEnabledPluginById) }) })})