// tests/setup.ts // vitest 글로벌 셋업: electron 모듈 모킹 import { vi } from 'vitest' import os from 'os' import path from 'path' import fs from 'fs' const TEST_USER_DATA = path.join(os.tmpdir(), 'd3ro-vitest-userData') const TEST_DOCS = path.join(os.tmpdir(), 'd3ro-vitest-docs') fs.mkdirSync(TEST_USER_DATA, { recursive: true }) fs.mkdirSync(TEST_DOCS, { recursive: true }) // electron 모듈 모킹 — 테스트에서 electron import 시 에러 방지 vi.mock('electron', () => ({ app: { getPath: vi.fn((name: string) => { if (name === 'temp') return os.tmpdir() if (name === 'documents') return TEST_DOCS return TEST_USER_DATA }), getVersion: vi.fn(() => '1.0.0'), quit: vi.fn(), on: vi.fn(), whenReady: vi.fn(() => Promise.resolve()), getLoginItemSettings: vi.fn(() => ({ openAtLogin: false })), setLoginItemSettings: vi.fn(), isPackaged: false, getAppPath: vi.fn(() => TEST_USER_DATA), }, ipcMain: { handle: vi.fn(), on: vi.fn(), removeHandler: vi.fn(), }, BrowserWindow: Object.assign(vi.fn(), { getAllWindows: vi.fn(() => []), }), Tray: vi.fn(), Menu: vi.fn(), nativeImage: { createFromPath: vi.fn(), }, dialog: { showErrorBox: vi.fn(), showSaveDialog: vi.fn(async () => ({ canceled: true, filePath: undefined })), showOpenDialog: vi.fn(async () => ({ canceled: true, filePaths: [] })), }, session: { defaultSession: { setDisplayMediaRequestHandler: vi.fn(), }, }, desktopCapturer: { getSources: vi.fn(async () => []), }, screen: { getPrimaryDisplay: vi.fn(() => ({ workArea: { x: 0, y: 0, width: 1920, height: 1080 }, })), }, clipboard: { readText: vi.fn(() => ''), writeText: vi.fn(), readHTML: vi.fn(() => ''), readRTF: vi.fn(() => ''), readImage: vi.fn(() => ({ isEmpty: () => true, toPNG: () => Buffer.alloc(0) })), write: vi.fn(), }, shell: { openExternal: vi.fn(async () => undefined), }, safeStorage: { isEncryptionAvailable: vi.fn(() => false), encryptString: vi.fn((s: string) => Buffer.from(s, 'utf-8')), decryptString: vi.fn((b: Buffer) => b.toString('utf-8')), }, Notification: vi.fn(), globalShortcut: { register: vi.fn(() => true), unregister: vi.fn(), unregisterAll: vi.fn(), isRegistered: vi.fn(() => false), }, })) // Unit tests must not load the native global keyboard hook. Linux shell runners // intentionally do not provide an X11 session or libXrandr, and product-level // hook behaviour is covered by runtime/E2E validation instead. vi.mock('uiohook-napi', () => ({ uIOhook: { on: vi.fn(), removeListener: vi.fn(), start: vi.fn(), stop: vi.fn(), }, UiohookKey: new Proxy>({}, { get: (_target, property) => Array.from(String(property)).reduce( (value, character) => ((value * 31) + character.charCodeAt(0)) & 0x7fffffff, 17, ), }), })) // WindowManager 모킹 — VoiceModeService가 import하므로 필요 vi.mock('../src/main/windows/WindowManager', () => ({ showRecordingTip: vi.fn(), hideRecordingTip: vi.fn(), updateRecordingTipState: vi.fn(), sendAudioLevelToTip: vi.fn(), sendPartialTranscriptToTip: vi.fn(), showResultPopup: vi.fn(), hideResultPopup: vi.fn(), getMainWindow: vi.fn(), createMainWindow: vi.fn(), hideCaptionOverlay: vi.fn(), showCaptionOverlay: vi.fn(), sendToCaptionOverlay: vi.fn(), reapplyThemeToAllPopups: vi.fn(), })) // electron-log 모킹 vi.mock('electron-log', () => { const noop = vi.fn() return { default: { info: noop, warn: noop, error: noop, debug: noop, create: () => ({ info: noop, warn: noop, error: noop, debug: noop, }), }, } }) // electron-store v10 ESM — 템플릿 서비스 생성자에서 동기 import vi.mock('electron-store', () => { class MemoryStore> { store: T constructor(opts?: { defaults?: T; name?: string }) { this.store = { ...(opts?.defaults as T) } } get(key: K, def?: T[K]): T[K] { return (this.store[key] ?? def) as T[K] } set(key: K, value: T[K]): void { this.store[key] = value } } return { default: MemoryStore } })