mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-23 23:10:17 +05:30
Add context.formats.registerExportFormat(id, opts) to PluginContext,
backed by a new FormatRegistry (mirrors PluginRegistry's Map-based
shape). Plugins register namespaced (${pluginId}:${id}) export
formats with a label/extension/handler; the writing-studio built-in
plugin registers a trivial "sprint-summary" .txt export as a
worked example.
The plugin system lives entirely in the renderer process while the
Export menu is built in main.js, so wiring formats into the menu
required a small IPC round-trip: renderer sends format metadata to
main after plugin load (main rebuilds the menu via the already-
idempotent createMenu()), and a menu click sends the resolved save
path back to the renderer, which is the only process holding the
plugin's handler function.
Amit Haridas
106 lines
3.8 KiB
JavaScript
106 lines
3.8 KiB
JavaScript
const { PluginContext } = require('../src/plugins/plugin-context');
|
|
const { EventBus } = require('../src/plugins/event-bus');
|
|
|
|
describe('PluginContext', () => {
|
|
let context;
|
|
let mockDeps;
|
|
|
|
beforeEach(() => {
|
|
mockDeps = {
|
|
pluginId: 'test-plugin',
|
|
sidebar: { registerPanel: jest.fn() },
|
|
commands: { register: jest.fn() },
|
|
statusBar: { registerIndicator: jest.fn() },
|
|
eventBus: new EventBus(),
|
|
settings: { get: jest.fn(), set: jest.fn(), onChanged: jest.fn() },
|
|
editor: {
|
|
getContent: jest.fn(),
|
|
getSelection: jest.fn(),
|
|
insertAtCursor: jest.fn(),
|
|
onContentChanged: jest.fn(),
|
|
},
|
|
ipc: { invoke: jest.fn(), on: jest.fn() },
|
|
exportHooks: { preHooks: [], postHooks: [] },
|
|
formatRegistry: { register: jest.fn(), get: jest.fn(), getAll: jest.fn() },
|
|
};
|
|
context = new PluginContext(mockDeps);
|
|
});
|
|
|
|
test('exposes sidebar.registerPanel with namespaced id', () => {
|
|
const handler = jest.fn();
|
|
context.sidebar.registerPanel('my-panel', { icon: 'test', title: 'Test', render: handler });
|
|
expect(mockDeps.sidebar.registerPanel).toHaveBeenCalledWith('test-plugin:my-panel', {
|
|
icon: 'test',
|
|
title: 'Test',
|
|
render: handler,
|
|
});
|
|
});
|
|
|
|
test('exposes commands.register with crash-safe wrapper', () => {
|
|
const badHandler = () => {
|
|
throw new Error('boom');
|
|
};
|
|
context.commands.register('bad-cmd', 'Bad', badHandler, 'Ctrl+Alt+T');
|
|
const registeredHandler = mockDeps.commands.register.mock.calls[0][2];
|
|
expect(() => registeredHandler()).not.toThrow();
|
|
});
|
|
|
|
test('exposes settings.get/set scoped to plugin', () => {
|
|
context.settings.get('myKey');
|
|
context.settings.set('myKey', 'myValue');
|
|
expect(mockDeps.settings.get).toHaveBeenCalledWith('plugins.test-plugin.myKey');
|
|
expect(mockDeps.settings.set).toHaveBeenCalledWith('plugins.test-plugin.myKey', 'myValue');
|
|
});
|
|
|
|
test('exposes editor methods', () => {
|
|
context.editor.getContent();
|
|
context.editor.getSelection();
|
|
expect(mockDeps.editor.getContent).toHaveBeenCalled();
|
|
expect(mockDeps.editor.getSelection).toHaveBeenCalled();
|
|
});
|
|
|
|
test('exposes events via event bus', () => {
|
|
const handler = jest.fn();
|
|
context.events.on('test:event', handler);
|
|
context.events.emit('test:event', { data: 1 });
|
|
expect(handler).toHaveBeenCalledWith({ data: 1 });
|
|
});
|
|
|
|
test('exposes events.hasHandler', () => {
|
|
expect(context.events.hasHandler('no:such')).toBe(false);
|
|
context.events.on('exists:event', () => {});
|
|
expect(context.events.hasHandler('exists:event')).toBe(true);
|
|
});
|
|
|
|
test('exposes ipc.invoke', () => {
|
|
context.ipc.invoke('test:channel', { a: 1 });
|
|
expect(mockDeps.ipc.invoke).toHaveBeenCalledWith('test:channel', { a: 1 });
|
|
});
|
|
|
|
test('exposes exports.registerPreHook', () => {
|
|
const handler = jest.fn();
|
|
context.exports.registerPreHook(handler);
|
|
expect(mockDeps.exportHooks.preHooks).toContain(handler);
|
|
});
|
|
|
|
test('exposes exports.registerPostHook', () => {
|
|
const handler = jest.fn();
|
|
context.exports.registerPostHook(handler);
|
|
expect(mockDeps.exportHooks.postHooks).toContain(handler);
|
|
});
|
|
|
|
test('exposes formats.registerExportFormat with namespaced id', () => {
|
|
const handler = jest.fn();
|
|
const opts = { label: 'My Format', extension: 'txt', handler };
|
|
context.formats.registerExportFormat('my-format', opts);
|
|
expect(mockDeps.formatRegistry.register).toHaveBeenCalledWith('test-plugin:my-format', opts);
|
|
});
|
|
|
|
test('formats.registerExportFormat is a no-op when no formatRegistry is injected', () => {
|
|
const noRegistryContext = new PluginContext({ ...mockDeps, formatRegistry: undefined });
|
|
expect(() =>
|
|
noRegistryContext.formats.registerExportFormat('x', { handler: jest.fn() })
|
|
).not.toThrow();
|
|
});
|
|
});
|