Files
SubMiner/src/main/assemblies/jellyfin.test.ts
T
sudacode 2924f04316 refactor(main): move domains into per-domain assemblies (#309)
* refactor(main): move the YouTube domain into an assembly

createYoutubeAssembly owns the YouTube flow, playback, media cache,
whisper subtitles, primary-subtitle notifications, and the in-app
browser, plus the browser quit-on-close state. main.ts builds it once
where the first YouTube runtime used to be and calls through the result.
Siblings built later are passed as getters or callbacks; tests pin that
construction calls none of them.

* docs(architecture): describe per-domain assemblies

* test(e2e): blank the line before mining the sub-delay cue

The previous test leaves the same cue on screen, so the overlay check
passed before the seek landed and --mine-sentence could read mpv's empty
mid-seek sub-text and skip the card.

* refactor(main): move the Jellyfin domain into an assembly

createJellyfinAssembly owns the Jellyfin composer call, its playback and
remote-progress state, the subtitle cache IO, mpv defaults, and the tray
discovery deps. main.ts builds it where the composer call used to be and
reads state through getActivePlayback/getQuitOnDisconnectArmed.

* refactor(main): move the AniList domain into an assembly

createAnilistAssembly owns the AniList setup and tracking composer calls,
the setup window, the state runtime, and the token, media-guess, in-flight
and attempted-update state they share. main.ts builds it where the setup
composer call used to be and keeps the rate limiter, which the stats
cover-art fetcher also uses.

* refactor(main): move config hot-reload, settings, and sync UI into assemblies

createSettingsAssembly owns config hot-reload and the settings window,
which share the hot-reload-applied handler; main.ts still supplies that
handler's cross-domain deps and the Yomitan deck-name helper.
createSyncAssembly owns the sync window, sync IPC runtime, and auto-sync
scheduler. Both are built where their blocks were; main.ts keeps the
registerHandlers()/start() calls at the same points.

* refactor(main): move dictionary runtimes into assemblies

createDictionariesAssembly owns the JLPT and frequency lookups and their
dictionary-root builders; main.ts still resolves the root paths at the
same point. createCharacterDictionaryAssembly owns the character
dictionary runtime, Yomitan auto-sync, and the portrait and name-candidate
lookups. Both are built where their blocks were; later siblings (Yomitan
profile policy, notifications, OSD, subtitle refresh) come in as getters
or callbacks.

* refactor(main): move overlay modal openers into an assembly

createOverlayModalsAssembly owns the shared open-with-notification helper
and the runtime options, Jimaku, TsukiHime, subtitle generation, session
help, changelog, character dictionary manager, controller, and playlist
browser openers. main.ts builds it where the openers used to live.

* refactor(logger): export the Logger type for assembly deps

The build emits declarations, and the Jellyfin assembly returns the logger
in its tray discovery deps, so tsc needs a nameable Logger type. Assemblies
now Pick from it directly.
2026-10-31 19:39:27 -07:00

48 lines
2.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createJellyfinAssembly, type JellyfinAssemblyDeps } from './jellyfin';
// Every callback throws, so a test fails if the assembly calls a dep it should only call later.
function makeDeps(): JellyfinAssemblyDeps {
const lazy = (name: string) => (): never => {
throw new Error(`${name} called unexpectedly`);
};
return {
appState: {
mpvClient: null,
mpvSocketPath: '/tmp/subminer-test.sock',
immersionTracker: null,
jellyfinRemoteSession: null,
jellyfinSetupWindow: null,
},
configService: { getConfig: lazy('getConfig'), patchRawConfig: lazy('patchRawConfig') },
jellyfinTokenStore: {
loadSession: lazy('loadSession'),
saveSession: lazy('saveSession'),
clearSession: lazy('clearSession'),
},
logger: { info: () => {}, warn: () => {}, debug: () => {}, error: () => {} },
defaultMpvLogPath: '/tmp/mpv.log',
getAppDataPath: lazy('getAppDataPath'),
getRuntimePluginEntrypoint: lazy('getRuntimePluginEntrypoint'),
getMpvPluginRuntimeConfig: lazy('getMpvPluginRuntimeConfig'),
createMpvClient: lazy('createMpvClient'),
initSubtitlePrefetch: lazy('initSubtitlePrefetch'),
showVisibleOverlay: lazy('showVisibleOverlay'),
getOverlayNotifications: lazy('getOverlayNotifications'),
updateCurrentMediaTitle: lazy('updateCurrentMediaTitle'),
ensureImmersionTrackerStarted: lazy('ensureImmersionTrackerStarted'),
refreshTrayMenu: lazy('refreshTrayMenu'),
setupPreloadPath: '/tmp/preload-jellyfin-setup.js',
createBrowserWindow: lazy('createBrowserWindow'),
ipcMain: { handle: lazy('ipcMain.handle'), removeHandler: lazy('ipcMain.removeHandler') },
};
}
// main.ts builds the assembly before several siblings exist, so construction must stay lazy.
test('createJellyfinAssembly does not call its deps while constructing', () => {
const jellyfin = createJellyfinAssembly(makeDeps());
assert.equal(jellyfin.getActivePlayback(), null);
assert.equal(jellyfin.getQuitOnDisconnectArmed(), false);
});