- Added a new setting for "launch at startup" in the GeneralSettingsPanel. - Integrated the setting with the existing settings store and IPC mechanisms. - Implemented platform-specific logic for enabling/disabling startup behavior in the main process. - Created a new service for managing launch at startup settings, including Linux desktop entry creation. - Added unit tests for the new functionality and ensured existing tests are updated accordingly. - Updated i18n messages for the new setting in English, Chinese, and Japanese.
156 lines
4.3 KiB
TypeScript
156 lines
4.3 KiB
TypeScript
// @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();
|
|
});
|
|
});
|