d3ro-voice/apps/desktop/tests/main/windows/main-window-close-redteam-r1-0.test.ts

136 lines
4.7 KiB
TypeScript

// closeToTray=false 로 메인 창을 닫으면 창 없는 프로세스로 남지 않고 종료한다. 창이 사라진 뒤 트레이
// '표시'·두 번째 실행은 창을 다시 만든다.
import { beforeEach, describe, expect, it, vi } from 'vitest'
type Listener = (...args: unknown[]) => void
const state = vi.hoisted(() => ({
windows: [] as Array<{ listeners: Map<string, Listener>; destroyed: boolean }>,
quit: vi.fn(),
}))
vi.unmock('../../../src/main/windows/WindowManager')
vi.mock('electron', () => {
const quit = state.quit
class FakeBrowserWindow {
listeners = new Map<string, Listener>()
destroyed = false
webContents = {
on: vi.fn(),
setWindowOpenHandler: vi.fn(),
openDevTools: vi.fn(),
insertCSS: vi.fn(async () => ''),
send: vi.fn(),
}
constructor() {
state.windows.push(this)
}
on(event: string, listener: Listener): this {
this.listeners.set(event, listener)
return this
}
once(event: string, listener: Listener): this {
return this.on(event, listener)
}
setSkipTaskbar = vi.fn()
setMenu = vi.fn()
loadURL = vi.fn(async () => undefined)
loadFile = vi.fn(async () => undefined)
center = vi.fn()
restore = vi.fn()
show = vi.fn()
focus = vi.fn()
hide = vi.fn()
setAlwaysOnTop = vi.fn()
flashFrame = vi.fn()
isMinimized = vi.fn(() => false)
isDestroyed(): boolean {
return this.destroyed
}
}
return {
app: { quit, getPath: vi.fn(() => '.'), on: vi.fn(), getVersion: vi.fn(() => '1.0.0'), isPackaged: false },
BrowserWindow: Object.assign(FakeBrowserWindow, { getAllWindows: vi.fn(() => []) }),
screen: {
getPrimaryDisplay: vi.fn(() => ({ workAreaSize: { width: 1920, height: 1080 }, workArea: { x: 0, y: 0, width: 1920, height: 1080 } })),
getCursorScreenPoint: vi.fn(() => ({ x: 0, y: 0 })),
getDisplayNearestPoint: vi.fn(() => ({ workArea: { x: 0, y: 0, width: 1920, height: 1080 } })),
},
ipcMain: { on: vi.fn(), handle: vi.fn(), removeHandler: vi.fn() },
Menu: { setApplicationMenu: vi.fn(), buildFromTemplate: vi.fn() },
clipboard: { writeText: vi.fn() },
shell: { openExternal: vi.fn(async () => undefined) },
safeStorage: { isEncryptionAvailable: vi.fn(() => false) },
nativeImage: { createFromPath: vi.fn(), createEmpty: vi.fn() },
Tray: vi.fn(),
dialog: { showErrorBox: vi.fn() },
}
})
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('../../../src/main/lifecycle', () => {
let quitting = false
return {
getIsQuitting: () => quitting,
setIsQuitting: (value: boolean) => {
quitting = value
},
}
})
const config: Record<string, unknown> = { closeToTray: false, theme: 'dark', language: 'ko' }
vi.mock('../../../src/main/services/ConfigService', () => ({
configGet: (key: string) => config[key],
configSet: (key: string, value: unknown) => {
config[key] = value
},
}))
beforeEach(async () => {
state.windows.length = 0
state.quit.mockClear()
const lifecycle = await import('../../../src/main/lifecycle')
lifecycle.setIsQuitting(false)
})
describe('main window close', () => {
it('closeToTray=false 이면 창 닫기가 앱 종료로 이어진다', async () => {
config.closeToTray = false
const { createMainWindow } = await import('../../../src/main/windows/WindowManager')
createMainWindow()
const win = state.windows[0]
const event = { preventDefault: vi.fn() }
win.listeners.get('close')?.(event)
expect(event.preventDefault).not.toHaveBeenCalled()
expect(state.quit).toHaveBeenCalledTimes(1)
})
it('closeToTray=true 이면 숨기기만 하고 종료하지 않는다', async () => {
vi.resetModules()
config.closeToTray = true
const { createMainWindow } = await import('../../../src/main/windows/WindowManager')
createMainWindow()
const win = state.windows[0]
const event = { preventDefault: vi.fn() }
win.listeners.get('close')?.(event)
expect(event.preventDefault).toHaveBeenCalled()
expect(state.quit).not.toHaveBeenCalled()
})
it('창이 파괴된 뒤 showMainWindow 는 창을 다시 만든다(부트스트랩 전에는 만들지 않는다)', async () => {
vi.resetModules()
const { createMainWindow, showMainWindow, getMainWindow } = await import('../../../src/main/windows/WindowManager')
expect(showMainWindow()).toBeNull()
expect(state.windows).toHaveLength(0)
createMainWindow()
const first = state.windows[0]
first.destroyed = true
first.listeners.get('closed')?.()
expect(getMainWindow()).toBeNull()
expect(showMainWindow()).not.toBeNull()
expect(state.windows).toHaveLength(2)
})
})