// @vitest-environment node import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const originalPlatform = process.platform; const { configManagerMock, electronAppMock, loggerMock, mkdirMock, rmMock, setLoginItemSettingsMock, testHome, writeFileMock, } = vi.hoisted(() => { const suffix = Math.random().toString(36).slice(2); const setLoginItemSettingsMock = vi.fn(); const mkdirMock = vi.fn(); const rmMock = vi.fn(); const writeFileMock = vi.fn(); const configManagerMock = { get: vi.fn(), }; const loggerMock = { info: vi.fn(), warn: vi.fn(), error: vi.fn(), }; const electronAppMock = { isPackaged: true, getName: () => 'zn-ai', getPath: (name: string) => (name === 'home' ? `/tmp/zn-ai-launch-startup-${suffix}` : '/tmp'), setLoginItemSettings: setLoginItemSettingsMock, }; return { configManagerMock, electronAppMock, loggerMock, mkdirMock, rmMock, setLoginItemSettingsMock, testHome: `/tmp/zn-ai-launch-startup-${suffix}`, writeFileMock, }; }); vi.mock('electron', () => ({ app: electronAppMock, })); vi.mock('@electron/service/config-service', () => ({ default: configManagerMock, })); vi.mock('@electron/service/logger', () => ({ logManager: loggerMock, default: loggerMock, })); vi.mock('node:fs/promises', () => ({ mkdir: mkdirMock, rm: rmMock, writeFile: writeFileMock, })); vi.mock('node:path', () => ({ dirname: (path: string) => path.slice(0, path.lastIndexOf('/')) || '/', join: (...parts: string[]) => parts.join('/').replace(/\/+/g, '/'), })); function setPlatform(platform: string): void { Object.defineProperty(process, 'platform', { value: platform, writable: true }); } describe('launch-at-startup service', () => { beforeEach(async () => { vi.resetModules(); vi.clearAllMocks(); configManagerMock.get.mockReturnValue(false); electronAppMock.isPackaged = true; }); afterEach(() => { Object.defineProperty(process, 'platform', { value: originalPlatform, writable: true }); }); it('uses login item settings on Windows', async () => { setPlatform('win32'); const { applyLaunchAtStartupSetting } = await import('@electron/service/launch-at-startup'); await applyLaunchAtStartupSetting(true); expect(setLoginItemSettingsMock).toHaveBeenCalledWith({ openAtLogin: true, openAsHidden: false, }); }); it('uses login item settings on macOS', async () => { setPlatform('darwin'); const { applyLaunchAtStartupSetting } = await import('@electron/service/launch-at-startup'); await applyLaunchAtStartupSetting(false); expect(setLoginItemSettingsMock).toHaveBeenCalledWith({ openAtLogin: false, openAsHidden: false, }); }); it('creates and removes a Linux autostart desktop entry', async () => { setPlatform('linux'); const { applyLaunchAtStartupSetting } = await import('@electron/service/launch-at-startup'); const autostartPath = `${testHome}/.config/autostart/zn-ai.desktop`; await applyLaunchAtStartupSetting(true); expect(mkdirMock).toHaveBeenCalledWith(`${testHome}/.config/autostart`, { recursive: true }); expect(writeFileMock).toHaveBeenCalledTimes(1); expect(writeFileMock).toHaveBeenCalledWith( autostartPath, expect.stringContaining('[Desktop Entry]'), 'utf8', ); expect(writeFileMock.mock.calls[0]?.[1]).toContain('Name=zn-ai'); expect(writeFileMock.mock.calls[0]?.[1]).toContain('Exec='); await applyLaunchAtStartupSetting(false); expect(rmMock).toHaveBeenCalledWith(autostartPath, { force: true }); }); it('syncs the persisted setting from config on startup', async () => { setPlatform('win32'); configManagerMock.get.mockReturnValue(true); const { syncLaunchAtStartupSettingFromConfig } = await import('@electron/service/launch-at-startup'); await syncLaunchAtStartupSettingFromConfig(); expect(configManagerMock.get).toHaveBeenCalledWith('launchAtStartup'); expect(setLoginItemSettingsMock).toHaveBeenCalledWith({ openAtLogin: true, openAsHidden: false, }); }); it('does not throw on unsupported platforms', async () => { setPlatform('freebsd'); const { applyLaunchAtStartupSetting } = await import('@electron/service/launch-at-startup'); await expect(applyLaunchAtStartupSetting(true)).resolves.toBeUndefined(); }); });