mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor: extract ipc deps runtime service
This commit is contained in:
108
src/core/services/ipc-deps-runtime-service.test.ts
Normal file
108
src/core/services/ipc-deps-runtime-service.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createIpcDepsRuntimeService } from "./ipc-deps-runtime-service";
|
||||
|
||||
test("createIpcDepsRuntimeService maps window and mecab helpers", async () => {
|
||||
let ignoreMouse: { ignore: boolean; forward?: boolean } | null = null;
|
||||
let toggledDevTools = 0;
|
||||
let mecabEnabled: boolean | null = null;
|
||||
|
||||
const visibleWindow = {
|
||||
isDestroyed: () => false,
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||
ignoreMouse = { ignore, forward: options?.forward };
|
||||
},
|
||||
webContents: {
|
||||
toggleDevTools: () => {
|
||||
toggledDevTools += 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const deps = createIpcDepsRuntimeService({
|
||||
getInvisibleWindow: () => visibleWindow,
|
||||
getMainWindow: () => visibleWindow,
|
||||
getVisibleOverlayVisibility: () => true,
|
||||
getInvisibleOverlayVisibility: () => false,
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => ({ text: "x" }),
|
||||
getCurrentSubtitleAss: () => "ass",
|
||||
getMpvSubtitleRenderMetrics: () => ({ subPos: 100 }),
|
||||
getSubtitlePosition: () => ({ x: 1, y: 2 }),
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabTokenizer: () => ({
|
||||
getStatus: () => ({ available: true, enabled: true, path: "/usr/bin/mecab" }),
|
||||
setEnabled: (enabled: boolean) => {
|
||||
mecabEnabled = enabled;
|
||||
},
|
||||
}),
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => ({ copySubtitle: ["C"] }),
|
||||
getSecondarySubMode: () => "hidden",
|
||||
getMpvClient: () => ({ currentSecondarySubText: "secondary" }),
|
||||
runSubsyncManual: async () => ({ ok: true }),
|
||||
getAnkiConnectStatus: () => true,
|
||||
getRuntimeOptions: () => ({ values: {} }),
|
||||
setRuntimeOption: () => ({ ok: true }),
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
});
|
||||
|
||||
deps.setInvisibleIgnoreMouseEvents(true, { forward: true });
|
||||
deps.toggleDevTools();
|
||||
deps.setMecabEnabled(false);
|
||||
|
||||
assert.deepEqual(ignoreMouse, { ignore: true, forward: true });
|
||||
assert.equal(toggledDevTools, 1);
|
||||
assert.equal(mecabEnabled, false);
|
||||
assert.deepEqual(deps.getMecabStatus(), {
|
||||
available: true,
|
||||
enabled: true,
|
||||
path: "/usr/bin/mecab",
|
||||
});
|
||||
assert.equal(deps.getCurrentSecondarySub(), "secondary");
|
||||
assert.deepEqual(await deps.tokenizeCurrentSubtitle(), { text: "x" });
|
||||
});
|
||||
|
||||
test("createIpcDepsRuntimeService handles missing optional runtime resources", () => {
|
||||
const deps = createIpcDepsRuntimeService({
|
||||
getInvisibleWindow: () => null,
|
||||
getMainWindow: () => null,
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
getInvisibleOverlayVisibility: () => false,
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleAss: () => "",
|
||||
getMpvSubtitleRenderMetrics: () => null,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabTokenizer: () => null,
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => null,
|
||||
getSecondarySubMode: () => "hidden",
|
||||
getMpvClient: () => null,
|
||||
runSubsyncManual: async () => ({ ok: false }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => null,
|
||||
setRuntimeOption: () => ({ ok: false }),
|
||||
cycleRuntimeOption: () => ({ ok: false }),
|
||||
});
|
||||
|
||||
deps.setInvisibleIgnoreMouseEvents(true, { forward: true });
|
||||
deps.toggleDevTools();
|
||||
deps.setMecabEnabled(true);
|
||||
|
||||
assert.deepEqual(deps.getMecabStatus(), {
|
||||
available: false,
|
||||
enabled: false,
|
||||
path: null,
|
||||
});
|
||||
assert.equal(deps.getCurrentSecondarySub(), "");
|
||||
});
|
||||
100
src/core/services/ipc-deps-runtime-service.ts
Normal file
100
src/core/services/ipc-deps-runtime-service.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { IpcServiceDeps } from "./ipc-service";
|
||||
|
||||
interface WindowLike {
|
||||
isDestroyed: () => boolean;
|
||||
setIgnoreMouseEvents: (
|
||||
ignore: boolean,
|
||||
options?: { forward?: boolean },
|
||||
) => void;
|
||||
webContents: {
|
||||
toggleDevTools: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
interface MecabTokenizerLike {
|
||||
getStatus: () => { available: boolean; enabled: boolean; path: string | null };
|
||||
setEnabled: (enabled: boolean) => void;
|
||||
}
|
||||
|
||||
interface MpvClientLike {
|
||||
currentSecondarySubText?: string;
|
||||
}
|
||||
|
||||
export interface IpcDepsRuntimeOptions {
|
||||
getInvisibleWindow: () => WindowLike | null;
|
||||
getMainWindow: () => WindowLike | null;
|
||||
getVisibleOverlayVisibility: () => boolean;
|
||||
getInvisibleOverlayVisibility: () => boolean;
|
||||
onOverlayModalClosed: (modal: string) => void;
|
||||
openYomitanSettings: () => void;
|
||||
quitApp: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||
getCurrentSubtitleAss: () => string;
|
||||
getMpvSubtitleRenderMetrics: () => unknown;
|
||||
getSubtitlePosition: () => unknown;
|
||||
getSubtitleStyle: () => unknown;
|
||||
saveSubtitlePosition: (position: unknown) => void;
|
||||
getMecabTokenizer: () => MecabTokenizerLike | null;
|
||||
handleMpvCommand: (command: Array<string | number>) => void;
|
||||
getKeybindings: () => unknown;
|
||||
getSecondarySubMode: () => unknown;
|
||||
getMpvClient: () => MpvClientLike | null;
|
||||
runSubsyncManual: (request: unknown) => Promise<unknown>;
|
||||
getAnkiConnectStatus: () => boolean;
|
||||
getRuntimeOptions: () => unknown;
|
||||
setRuntimeOption: (id: string, value: unknown) => unknown;
|
||||
cycleRuntimeOption: (id: string, direction: 1 | -1) => unknown;
|
||||
}
|
||||
|
||||
export function createIpcDepsRuntimeService(
|
||||
options: IpcDepsRuntimeOptions,
|
||||
): IpcServiceDeps {
|
||||
return {
|
||||
getInvisibleWindow: () => options.getInvisibleWindow() as never,
|
||||
isVisibleOverlayVisible: options.getVisibleOverlayVisibility,
|
||||
setInvisibleIgnoreMouseEvents: (ignore, eventsOptions) => {
|
||||
const invisibleWindow = options.getInvisibleWindow();
|
||||
if (!invisibleWindow || invisibleWindow.isDestroyed()) return;
|
||||
invisibleWindow.setIgnoreMouseEvents(ignore, eventsOptions);
|
||||
},
|
||||
onOverlayModalClosed: options.onOverlayModalClosed,
|
||||
openYomitanSettings: options.openYomitanSettings,
|
||||
quitApp: options.quitApp,
|
||||
toggleDevTools: () => {
|
||||
const mainWindow = options.getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
},
|
||||
getVisibleOverlayVisibility: options.getVisibleOverlayVisibility,
|
||||
toggleVisibleOverlay: options.toggleVisibleOverlay,
|
||||
getInvisibleOverlayVisibility: options.getInvisibleOverlayVisibility,
|
||||
tokenizeCurrentSubtitle: options.tokenizeCurrentSubtitle,
|
||||
getCurrentSubtitleAss: options.getCurrentSubtitleAss,
|
||||
getMpvSubtitleRenderMetrics: options.getMpvSubtitleRenderMetrics,
|
||||
getSubtitlePosition: options.getSubtitlePosition,
|
||||
getSubtitleStyle: options.getSubtitleStyle,
|
||||
saveSubtitlePosition: options.saveSubtitlePosition,
|
||||
getMecabStatus: () => {
|
||||
const mecabTokenizer = options.getMecabTokenizer();
|
||||
return mecabTokenizer
|
||||
? mecabTokenizer.getStatus()
|
||||
: { available: false, enabled: false, path: null };
|
||||
},
|
||||
setMecabEnabled: (enabled) => {
|
||||
const mecabTokenizer = options.getMecabTokenizer();
|
||||
if (!mecabTokenizer) return;
|
||||
mecabTokenizer.setEnabled(enabled);
|
||||
},
|
||||
handleMpvCommand: options.handleMpvCommand,
|
||||
getKeybindings: options.getKeybindings,
|
||||
getSecondarySubMode: options.getSecondarySubMode,
|
||||
getCurrentSecondarySub: () =>
|
||||
options.getMpvClient()?.currentSecondarySubText || "",
|
||||
runSubsyncManual: options.runSubsyncManual,
|
||||
getAnkiConnectStatus: options.getAnkiConnectStatus,
|
||||
getRuntimeOptions: options.getRuntimeOptions,
|
||||
setRuntimeOption: options.setRuntimeOption,
|
||||
cycleRuntimeOption: options.cycleRuntimeOption,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user