203 lines
5.9 KiB
TypeScript
203 lines
5.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
// Mock api module
|
|
vi.mock('@renderer/api', () => ({
|
|
api: {
|
|
cliInstaller: {
|
|
getStatus: vi.fn(),
|
|
install: vi.fn(),
|
|
onProgress: vi.fn(() => vi.fn()),
|
|
},
|
|
// Minimal stubs for other api methods referenced by store slices
|
|
getProjects: vi.fn(() => Promise.resolve([])),
|
|
getSessions: vi.fn(() => Promise.resolve([])),
|
|
notifications: {
|
|
get: vi.fn(() =>
|
|
Promise.resolve({
|
|
notifications: [],
|
|
total: 0,
|
|
totalCount: 0,
|
|
unreadCount: 0,
|
|
hasMore: false,
|
|
})
|
|
),
|
|
getUnreadCount: vi.fn(() => Promise.resolve(0)),
|
|
onNew: vi.fn(),
|
|
onUpdated: vi.fn(),
|
|
onClicked: vi.fn(),
|
|
},
|
|
config: { get: vi.fn(() => Promise.resolve({})) },
|
|
updater: { check: vi.fn(), onStatus: vi.fn() },
|
|
context: {
|
|
getActive: vi.fn(() => Promise.resolve('local')),
|
|
list: vi.fn(),
|
|
onChanged: vi.fn(),
|
|
},
|
|
teams: {
|
|
list: vi.fn(() => Promise.resolve([])),
|
|
onTeamChange: vi.fn(),
|
|
onProvisioningProgress: vi.fn(),
|
|
},
|
|
ssh: { onStatus: vi.fn() },
|
|
onFileChange: vi.fn(),
|
|
onTodoChange: vi.fn(),
|
|
getAppVersion: vi.fn(() => Promise.resolve('1.0.0')),
|
|
},
|
|
isElectronMode: () => true,
|
|
}));
|
|
|
|
import { api } from '@renderer/api';
|
|
import { useStore } from '@renderer/store';
|
|
|
|
import type { CliInstallationStatus } from '@shared/types';
|
|
|
|
describe('cliInstallerSlice', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
// Reset store state
|
|
useStore.setState({
|
|
cliStatus: null,
|
|
cliInstallerState: 'idle',
|
|
cliDownloadProgress: 0,
|
|
cliDownloadTransferred: 0,
|
|
cliDownloadTotal: 0,
|
|
cliInstallerError: null,
|
|
cliCompletedVersion: null,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('initial state', () => {
|
|
it('has correct defaults', () => {
|
|
const state = useStore.getState();
|
|
expect(state.cliStatus).toBeNull();
|
|
expect(state.cliInstallerState).toBe('idle');
|
|
expect(state.cliDownloadProgress).toBe(0);
|
|
expect(state.cliInstallerError).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('fetchCliStatus', () => {
|
|
it('updates cliStatus from API', async () => {
|
|
const mockStatus: CliInstallationStatus = {
|
|
flavor: 'claude',
|
|
displayName: 'Claude CLI',
|
|
supportsSelfUpdate: true,
|
|
showVersionDetails: true,
|
|
showBinaryPath: true,
|
|
installed: true,
|
|
installedVersion: '2.1.59',
|
|
binaryPath: '/usr/local/bin/claude',
|
|
latestVersion: '2.1.59',
|
|
updateAvailable: false,
|
|
authLoggedIn: false,
|
|
authStatusChecking: false,
|
|
authMethod: null,
|
|
providers: [],
|
|
};
|
|
vi.mocked(api.cliInstaller.getStatus).mockResolvedValue(mockStatus);
|
|
|
|
await useStore.getState().fetchCliStatus();
|
|
|
|
expect(useStore.getState().cliStatus).toEqual(mockStatus);
|
|
});
|
|
|
|
it('handles API errors gracefully', async () => {
|
|
vi.mocked(api.cliInstaller.getStatus).mockRejectedValue(new Error('Network error'));
|
|
|
|
await useStore.getState().fetchCliStatus();
|
|
|
|
// Should not throw, status remains null
|
|
expect(useStore.getState().cliStatus).toBeNull();
|
|
});
|
|
|
|
it('detects update available', async () => {
|
|
const mockStatus: CliInstallationStatus = {
|
|
flavor: 'claude',
|
|
displayName: 'Claude CLI',
|
|
supportsSelfUpdate: true,
|
|
showVersionDetails: true,
|
|
showBinaryPath: true,
|
|
installed: true,
|
|
installedVersion: '2.1.34',
|
|
binaryPath: '/usr/local/bin/claude',
|
|
latestVersion: '2.1.59',
|
|
updateAvailable: true,
|
|
authLoggedIn: true,
|
|
authStatusChecking: false,
|
|
authMethod: 'oauth_token',
|
|
providers: [],
|
|
};
|
|
vi.mocked(api.cliInstaller.getStatus).mockResolvedValue(mockStatus);
|
|
|
|
await useStore.getState().fetchCliStatus();
|
|
|
|
expect(useStore.getState().cliStatus?.updateAvailable).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('installCli', () => {
|
|
it('sets state to checking and calls API', () => {
|
|
vi.mocked(api.cliInstaller.install).mockResolvedValue(undefined);
|
|
|
|
useStore.getState().installCli();
|
|
|
|
expect(useStore.getState().cliInstallerState).toBe('checking');
|
|
expect(useStore.getState().cliInstallerError).toBeNull();
|
|
expect(api.cliInstaller.install).toHaveBeenCalled();
|
|
});
|
|
|
|
it('resets download progress on new install', () => {
|
|
useStore.setState({
|
|
cliDownloadProgress: 50,
|
|
cliDownloadTransferred: 100_000_000,
|
|
cliDownloadTotal: 200_000_000,
|
|
});
|
|
|
|
vi.mocked(api.cliInstaller.install).mockResolvedValue(undefined);
|
|
|
|
useStore.getState().installCli();
|
|
|
|
expect(useStore.getState().cliDownloadProgress).toBe(0);
|
|
expect(useStore.getState().cliDownloadTransferred).toBe(0);
|
|
expect(useStore.getState().cliDownloadTotal).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('progress event handling', () => {
|
|
it('updates download progress from events', () => {
|
|
useStore.setState({
|
|
cliInstallerState: 'downloading',
|
|
cliDownloadProgress: 50,
|
|
cliDownloadTransferred: 100_000_000,
|
|
cliDownloadTotal: 200_000_000,
|
|
});
|
|
|
|
const state = useStore.getState();
|
|
expect(state.cliInstallerState).toBe('downloading');
|
|
expect(state.cliDownloadProgress).toBe(50);
|
|
});
|
|
|
|
it('tracks completed version', () => {
|
|
useStore.setState({
|
|
cliInstallerState: 'completed',
|
|
cliCompletedVersion: '2.1.59',
|
|
});
|
|
|
|
expect(useStore.getState().cliCompletedVersion).toBe('2.1.59');
|
|
});
|
|
|
|
it('tracks error state', () => {
|
|
useStore.setState({
|
|
cliInstallerState: 'error',
|
|
cliInstallerError: 'SHA256 checksum mismatch',
|
|
});
|
|
|
|
expect(useStore.getState().cliInstallerState).toBe('error');
|
|
expect(useStore.getState().cliInstallerError).toBe('SHA256 checksum mismatch');
|
|
});
|
|
});
|
|
});
|