import { describe, it, expect, vi, beforeEach } from 'vitest' import { PluginLoader } from '../plugin-loader' vi.mock('../../../app/capabilities', async importOriginal => ({ ...await importOriginal(), areThirdPartyPluginsEnabled: () => true, })) import { PluginRegistry } from '../plugin-registry' import { PluginSettingsStore } from '../plugin-settings-store' import { EnabledStateStore } from '../enabled-state-store' import { createTestApp, createTestManifest, createMockLocalStorage, createMockImporter } from './test-utils' import type { App } from '../../app' describe('PluginLoader.enablePlugin — onUserEnable', () => { let app: App let registry: PluginRegistry let settingsStore: PluginSettingsStore let enabledStore: EnabledStateStore beforeEach(async () => { const mockLocalStorage = createMockLocalStorage() vi.stubGlobal('localStorage', mockLocalStorage) app = await createTestApp() registry = new PluginRegistry() settingsStore = new PluginSettingsStore(app.reamsStateData!) enabledStore = new EnabledStateStore() }) async function createPluginWithOnUserEnable(opts?: { throws?: boolean }) { const { Plugin: PluginBase } = await import('../../plugin') let called = false class TestPlugin extends PluginBase { onload() { /* noop */ } onUserEnable() { called = true if (opts?.throws) throw new Error('onUserEnable boom') } } return { TestPlugin, wasCalled: () => called } } it('calls onUserEnable when userInitiated is true', async () => { const { TestPlugin, wasCalled } = await createPluginWithOnUserEnable() const mockImporter = createMockImporter(new Map([['/test.js', TestPlugin as any]])) const loader = new PluginLoader(app, registry, settingsStore, enabledStore, mockImporter) registry.register(createTestManifest({ id: 'test-plugin' }), '/test.js') await loader.enablePlugin('test-plugin', { userInitiated: true }) expect(wasCalled()).toBe(true) expect(loader.getPluginState('test-plugin')).toBe('active') }) it('does NOT call onUserEnable when userInitiated is false (default)', async () => { const { TestPlugin, wasCalled } = await createPluginWithOnUserEnable() const mockImporter = createMockImporter(new Map([['/test.js', TestPlugin as any]])) const loader = new PluginLoader(app, registry, settingsStore, enabledStore, mockImporter) registry.register(createTestManifest({ id: 'test-plugin' }), '/test.js') await loader.enablePlugin('test-plugin') expect(wasCalled()).toBe(false) }) it('does NOT call onUserEnable during enableStoredPlugins', async () => { const { TestPlugin, wasCalled } = await createPluginWithOnUserEnable() const mockImporter = createMockImporter(new Map([['/test.js', TestPlugin as any]])) enabledStore.setEnabled('test-plugin', true) const loader = new PluginLoader(app, registry, settingsStore, enabledStore, mockImporter) registry.register(createTestManifest({ id: 'test-plugin' }), '/test.js') await loader.enableStoredPlugins() expect(wasCalled()).toBe(false) }) it('catches and logs errors from onUserEnable without blocking enable', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) const { TestPlugin } = await createPluginWithOnUserEnable({ throws: true }) const mockImporter = createMockImporter(new Map([['/test.js', TestPlugin as any]])) const loader = new PluginLoader(app, registry, settingsStore, enabledStore, mockImporter) registry.register(createTestManifest({ id: 'test-plugin' }), '/test.js') // Should NOT throw — errors are caught await loader.enablePlugin('test-plugin', { userInitiated: true }) expect(loader.getPluginState('test-plugin')).toBe('active') expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('onUserEnable'), expect.any(Error) ) consoleSpy.mockRestore() }) })