mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-22 17:16:19 -07:00
214 lines
6.1 KiB
TypeScript
214 lines
6.1 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { DEFAULT_CONFIG, type ReloadConfigStrictResult } from '../../config';
|
|
import type { ResolvedConfig } from '../../types/config';
|
|
import type { ConfigSettingsSnapshot } from '../../types/settings';
|
|
import { createSaveConfigSettingsPatchHandler } from './config-settings-save';
|
|
|
|
function snapshot(): ConfigSettingsSnapshot {
|
|
return {
|
|
configPath: '/tmp/config.jsonc',
|
|
fields: [],
|
|
values: {},
|
|
warnings: [],
|
|
};
|
|
}
|
|
|
|
test('config settings save returns hot-reloadable diff for watcher path', () => {
|
|
const calls: string[] = [];
|
|
const previous = DEFAULT_CONFIG;
|
|
const next: ResolvedConfig = {
|
|
...DEFAULT_CONFIG,
|
|
subtitleStyle: {
|
|
...DEFAULT_CONFIG.subtitleStyle,
|
|
autoPauseVideoOnHover: false,
|
|
},
|
|
};
|
|
let written = '';
|
|
const save = createSaveConfigSettingsPatchHandler({
|
|
getConfigPath: () => '/tmp/config.jsonc',
|
|
getCurrentConfig: () => previous,
|
|
getWarnings: () => [],
|
|
getSnapshot: () => snapshot(),
|
|
fileExists: () => true,
|
|
readText: () => '{}',
|
|
writeTextAtomically: (_path, content) => {
|
|
written = content;
|
|
calls.push('write');
|
|
},
|
|
reloadConfigStrict: (): ReloadConfigStrictResult => ({
|
|
ok: true,
|
|
config: next,
|
|
warnings: [],
|
|
path: '/tmp/config.jsonc',
|
|
}),
|
|
classifyDiff: () => ({
|
|
hotReloadFields: ['subtitleStyle'],
|
|
restartRequiredFields: [],
|
|
}),
|
|
getRestartRequiredSections: () => [],
|
|
});
|
|
|
|
const result = save({
|
|
operations: [
|
|
{
|
|
op: 'set',
|
|
path: 'subtitleStyle.autoPauseVideoOnHover',
|
|
value: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.match(written, /autoPauseVideoOnHover/);
|
|
assert.deepEqual(calls, ['write']);
|
|
assert.deepEqual(result.hotReloadFields, ['subtitleStyle']);
|
|
assert.deepEqual(result.restartRequiredFields, []);
|
|
});
|
|
|
|
test('config settings save immediately applies hot-reloadable subtitle CSS changes', () => {
|
|
const previous = DEFAULT_CONFIG;
|
|
const next: ResolvedConfig = {
|
|
...DEFAULT_CONFIG,
|
|
subtitleStyle: {
|
|
...DEFAULT_CONFIG.subtitleStyle,
|
|
css: {
|
|
'font-size': '50px',
|
|
},
|
|
secondary: {
|
|
...DEFAULT_CONFIG.subtitleStyle.secondary,
|
|
css: {
|
|
'font-size': '28px',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const applied: Array<{
|
|
hotReloadFields: string[];
|
|
config: ResolvedConfig;
|
|
}> = [];
|
|
const save = createSaveConfigSettingsPatchHandler({
|
|
getConfigPath: () => '/tmp/config.jsonc',
|
|
getCurrentConfig: () => previous,
|
|
getWarnings: () => [],
|
|
getSnapshot: () => snapshot(),
|
|
fileExists: () => true,
|
|
readText: () => '{}',
|
|
writeTextAtomically: () => {},
|
|
reloadConfigStrict: (): ReloadConfigStrictResult => ({
|
|
ok: true,
|
|
config: next,
|
|
warnings: [],
|
|
path: '/tmp/config.jsonc',
|
|
}),
|
|
classifyDiff: () => ({
|
|
hotReloadFields: ['subtitleStyle'],
|
|
restartRequiredFields: [],
|
|
}),
|
|
getRestartRequiredSections: () => [],
|
|
onHotReloadApplied: (diff, config) => {
|
|
applied.push({
|
|
hotReloadFields: diff.hotReloadFields,
|
|
config,
|
|
});
|
|
},
|
|
});
|
|
|
|
const result = save({
|
|
operations: [
|
|
{
|
|
op: 'set',
|
|
path: 'subtitleStyle.css',
|
|
value: { 'font-size': '50px' },
|
|
},
|
|
{
|
|
op: 'set',
|
|
path: 'subtitleStyle.secondary.css',
|
|
value: { 'font-size': '28px' },
|
|
},
|
|
],
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(applied.length, 1);
|
|
assert.deepEqual(applied[0]?.hotReloadFields, ['subtitleStyle']);
|
|
assert.equal(applied[0]?.config.subtitleStyle.css['font-size'], '50px');
|
|
assert.equal(applied[0]?.config.subtitleStyle.secondary.css['font-size'], '28px');
|
|
});
|
|
|
|
test('config settings save returns restart-required sections without applying hot reload', () => {
|
|
const calls: string[] = [];
|
|
const previous = DEFAULT_CONFIG;
|
|
const next: ResolvedConfig = {
|
|
...DEFAULT_CONFIG,
|
|
mpv: {
|
|
...DEFAULT_CONFIG.mpv,
|
|
launchMode: 'fullscreen',
|
|
},
|
|
};
|
|
const save = createSaveConfigSettingsPatchHandler({
|
|
getConfigPath: () => '/tmp/config.jsonc',
|
|
getCurrentConfig: () => previous,
|
|
getWarnings: () => [],
|
|
getSnapshot: () => snapshot(),
|
|
fileExists: () => true,
|
|
readText: () => '{}',
|
|
writeTextAtomically: () => calls.push('write'),
|
|
reloadConfigStrict: (): ReloadConfigStrictResult => ({
|
|
ok: true,
|
|
config: next,
|
|
warnings: [],
|
|
path: '/tmp/config.jsonc',
|
|
}),
|
|
classifyDiff: () => ({
|
|
hotReloadFields: [],
|
|
restartRequiredFields: ['mpv'],
|
|
}),
|
|
getRestartRequiredSections: () => ['mpv launcher'],
|
|
});
|
|
|
|
const result = save({
|
|
operations: [{ op: 'set', path: 'mpv.launchMode', value: 'fullscreen' }],
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.deepEqual(calls, ['write']);
|
|
assert.deepEqual(result.hotReloadFields, []);
|
|
assert.deepEqual(result.restartRequiredFields, ['mpv']);
|
|
assert.deepEqual(result.restartRequiredSections, ['mpv launcher']);
|
|
});
|
|
|
|
test('config settings save restores previous file content when strict reload fails', () => {
|
|
const writes: string[] = [];
|
|
const save = createSaveConfigSettingsPatchHandler({
|
|
getConfigPath: () => '/tmp/config.jsonc',
|
|
getCurrentConfig: () => DEFAULT_CONFIG,
|
|
getWarnings: () => [],
|
|
getSnapshot: () => snapshot(),
|
|
fileExists: () => true,
|
|
readText: () => '{"mpv":{"launchMode":"normal"}}\n',
|
|
writeTextAtomically: (_path, content) => {
|
|
writes.push(content);
|
|
},
|
|
reloadConfigStrict: (): ReloadConfigStrictResult => ({
|
|
ok: false,
|
|
error: 'invalid config',
|
|
path: '/tmp/config.jsonc',
|
|
}),
|
|
classifyDiff: () => {
|
|
throw new Error('Should not classify invalid config.');
|
|
},
|
|
getRestartRequiredSections: () => [],
|
|
});
|
|
|
|
const result = save({
|
|
operations: [{ op: 'set', path: 'mpv.launchMode', value: 'fullscreen' }],
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.error, 'invalid config');
|
|
assert.equal(writes.length, 2);
|
|
assert.match(writes[0] ?? '', /fullscreen/);
|
|
assert.equal(writes[1], '{"mpv":{"launchMode":"normal"}}\n');
|
|
});
|