Files
SubMiner/src/main/runtime/config-hot-reload-main-deps.test.ts

108 lines
3.9 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import type { ReloadConfigStrictResult } from '../../config';
import type { ResolvedConfig } from '../../types';
import {
createBuildConfigHotReloadAppliedMainDepsHandler,
createBuildConfigHotReloadRuntimeMainDepsHandler,
createWatchConfigPathHandler,
} from './config-hot-reload-main-deps';
test('watch config path handler watches file directly when config exists', () => {
const calls: string[] = [];
const watchConfigPath = createWatchConfigPathHandler({
fileExists: () => true,
dirname: (path) => path.split('/').slice(0, -1).join('/'),
watchPath: (targetPath, nextListener) => {
calls.push(`watch:${targetPath}`);
nextListener('change', 'ignored');
return { close: () => calls.push('close') };
},
});
const watcher = watchConfigPath('/tmp/config.jsonc', () => calls.push('change'));
watcher.close();
assert.deepEqual(calls, ['watch:/tmp/config.jsonc', 'change', 'close']);
});
test('watch config path handler filters directory events to config files only', () => {
const calls: string[] = [];
const watchConfigPath = createWatchConfigPathHandler({
fileExists: () => false,
dirname: (path) => path.split('/').slice(0, -1).join('/'),
watchPath: (_targetPath, nextListener) => {
nextListener('change', 'foo.txt');
nextListener('change', 'config.json');
nextListener('change', 'config.jsonc');
nextListener('change', null);
return { close: () => {} };
},
});
watchConfigPath('/tmp/config.jsonc', () => calls.push('change'));
assert.deepEqual(calls, ['change', 'change', 'change']);
});
test('config hot reload applied main deps builder maps callbacks', () => {
const calls: string[] = [];
const deps = createBuildConfigHotReloadAppliedMainDepsHandler({
setKeybindings: () => calls.push('keybindings'),
refreshGlobalAndOverlayShortcuts: () => calls.push('refresh-shortcuts'),
setSecondarySubMode: () => calls.push('set-secondary'),
broadcastToOverlayWindows: (channel) => calls.push(`broadcast:${channel}`),
applyAnkiRuntimeConfigPatch: () => calls.push('apply-anki'),
})();
deps.setKeybindings([]);
deps.refreshGlobalAndOverlayShortcuts();
deps.setSecondarySubMode('hover');
deps.broadcastToOverlayWindows('config:hot-reload', {});
deps.applyAnkiRuntimeConfigPatch({ ai: {} as never });
assert.deepEqual(calls, [
'keybindings',
'refresh-shortcuts',
'set-secondary',
'broadcast:config:hot-reload',
'apply-anki',
]);
});
test('config hot reload runtime main deps builder maps runtime callbacks', () => {
const calls: string[] = [];
const deps = createBuildConfigHotReloadRuntimeMainDepsHandler({
getCurrentConfig: () => ({ id: 1 } as never as ResolvedConfig),
reloadConfigStrict: () =>
({
ok: true,
config: { id: 1 } as never as ResolvedConfig,
warnings: [],
path: '/tmp/config.jsonc',
}) as ReloadConfigStrictResult,
watchConfigPath: (_configPath, _onChange) => ({ close: () => calls.push('close') }),
setTimeout: (callback) => {
callback();
return 1 as never;
},
clearTimeout: () => calls.push('clear-timeout'),
debounceMs: 250,
onHotReloadApplied: () => calls.push('hot-reload'),
onRestartRequired: () => calls.push('restart-required'),
onInvalidConfig: () => calls.push('invalid-config'),
onValidationWarnings: () => calls.push('validation-warnings'),
})();
assert.deepEqual(deps.getCurrentConfig(), { id: 1 });
assert.deepEqual(deps.reloadConfigStrict(), {
ok: true,
config: { id: 1 },
warnings: [],
path: '/tmp/config.jsonc',
});
assert.equal(deps.debounceMs, 250);
deps.onHotReloadApplied({} as never, {} as never);
deps.onRestartRequired([]);
deps.onInvalidConfig('bad');
deps.onValidationWarnings('/tmp/config.jsonc', []);
assert.deepEqual(calls, ['hot-reload', 'restart-required', 'invalid-config', 'validation-warnings']);
});