eigent/test
Tong Chen c6ba7e0cd7
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Pre-commit / pre-commit (push) Waiting to run
Test / Run Python Tests (push) Waiting to run
feat(updater): switch app auto-update feed from GitHub Releases to CDN (#1543)
2026-04-13 15:27:25 +08:00
..
integration refactor: move privacy consent from ChatBox to Login/SignUp (#1239) 2026-03-07 18:55:48 +08:00
mocks refactor: move privacy consent from ChatBox to Login/SignUp (#1239) 2026-03-07 18:55:48 +08:00
screenshots Initial commit of eigent-main 2025-08-12 01:16:39 +02:00
unit feat(updater): switch app auto-update feed from GitHub Releases to CDN (#1543) 2026-04-13 15:27:25 +08:00
README.md chore: fix pre commit format and pipeline issue (#1144) 2026-02-04 08:06:29 +08:00
setup.ts feat: Add Lint & Format (#878) 2026-02-01 23:16:18 +08:00
vitest-jest-dom.d.ts feat: Add Lint & Format (#878) 2026-02-01 23:16:18 +08:00

Installation Flow Testing Environment

This comprehensive testing environment allows you to test all installation flows end-to-end with mocked uv sync, uvicorn, and Electron APIs. It simulates different system states and provides utilities to change the environment during tests.

Overview

The testing environment consists of three main components:

  1. Electron API Mocks (test/mocks/electronMocks.ts) - Mock Electron's preload APIs
  2. Environment State Mocks (test/mocks/environmentMocks.ts) - Mock filesystem, processes, and system state
  3. Test Scenarios - Predefined scenarios for different installation flows

Quick Start

import { setupElectronMocks, TestScenarios } from '../mocks/electronMocks';
import { setupMockEnvironment } from '../mocks/environmentMocks';

describe('My Installation Test', () => {
  let electronAPI: MockedElectronAPI;
  let mockEnv: ReturnType<typeof setupMockEnvironment>;

  beforeEach(() => {
    // Set up mocks
    const { electronAPI: api } = setupElectronMocks();
    electronAPI = api;
    mockEnv = setupMockEnvironment();
  });

  it('should handle version update', async () => {
    // Apply scenario
    TestScenarios.versionUpdate(electronAPI);

    // Your test code here
  });
});

Electron API Mocks

Available Mock Methods

  • checkAndInstallDepsOnUpdate() - Simulates dependency installation
  • getInstallationStatus() - Returns current installation status
  • exportLog() - Simulates log export functionality
  • Event listeners for installation events

Simulation Functions

// Simulate installation events
electronAPI.simulateInstallationStart();
electronAPI.simulateInstallationLog('stdout', 'Installing packages...');
electronAPI.simulateInstallationComplete(true); // or false for failure

// Simulate system changes
electronAPI.simulateVersionChange('2.0.0');
electronAPI.simulateVenvRemoval();
electronAPI.simulateUvicornStartup();

Mock State Control

// Control the mock state directly
electronAPI.mockState.venvExists = false;
electronAPI.mockState.isInstalling = true;
electronAPI.mockState.toolInstalled = false;

Environment State Mocks

Filesystem Mock

Controls file system operations:

// Control file existence
mockEnv.mockState.filesystem.venvExists = false;
mockEnv.mockState.filesystem.versionFileExists = true;
mockEnv.mockState.filesystem.installedLockExists = false;

// Control file contents
mockEnv.mockState.filesystem.versionFileContent = '0.9.0';

Process Mock

Controls process spawning and execution:

// Control tool availability
mockEnv.mockState.processes.uvAvailable = false;
mockEnv.mockState.processes.bunAvailable = true;
mockEnv.mockState.processes.uvicornRunning = false;

// Control network connectivity
mockEnv.mockState.network.canConnectToDefault = false;
mockEnv.mockState.network.canConnectToMirror = true;

Predefined Test Scenarios

Electron API Scenarios

Use TestScenarios from electronMocks.ts:

// Fresh installation - no .venv, no version file
TestScenarios.freshInstall(electronAPI);

// Version update - version file exists but version changed
TestScenarios.versionUpdate(electronAPI);

// .venv removed - version file exists but .venv is missing
TestScenarios.venvRemoved(electronAPI);

// Installation in progress - when user opens app during installation
TestScenarios.installationInProgress(electronAPI);

// Installation error scenario
TestScenarios.installationError(electronAPI);

// Uvicorn startup with dependency installation
TestScenarios.uvicornDepsInstall(electronAPI);

// All good - no installation needed
TestScenarios.allGood(electronAPI);

Environment Scenarios

Use mockEnv.scenarios from environmentMocks.ts:

// Fresh installation
mockEnv.scenarios.freshInstall();

// Version update
mockEnv.scenarios.versionUpdate('0.9.0', '1.0.0');

// .venv removed
mockEnv.scenarios.venvRemoved();

// Network issues
mockEnv.scenarios.networkIssues();

// Complete failure
mockEnv.scenarios.completeFailure();

// Uvicorn startup installation
mockEnv.scenarios.uvicornStartupInstall();

// Installation in progress
mockEnv.scenarios.installationInProgress();

Testing Different Installation States

Installation Store States

Test all possible states from installationStore.ts:

  • 'idle' - Initial state
  • 'checking-permissions' - Checking system permissions
  • 'showing-carousel' - Showing onboarding carousel
  • 'installing' - Installation in progress
  • 'error' - Installation failed
  • 'completed' - Installation successful
import { useInstallationStore } from '@/store/installationStore';

it('should transition through all states', () => {
  const store = useInstallationStore.getState();

  expect(store.state).toBe('idle');

  store.startInstallation();
  expect(store.state).toBe('installing');

  store.setError('Installation failed');
  expect(store.state).toBe('error');

  store.retryInstallation();
  expect(store.state).toBe('installing');

  store.setSuccess();
  expect(store.state).toBe('completed');
});

Specific Test Cases

1. Testing .venv Removal

it('should handle .venv removal', async () => {
  // Simulate .venv being removed
  TestScenarios.venvRemoved(electronAPI);
  // or
  mockEnv.scenarios.venvRemoved();

  // Test your component/hook
  const result = await electronAPI.checkAndInstallDepsOnUpdate();
  expect(result.success).toBe(true);
});

2. Testing Version File Changes

it('should handle version file changes', async () => {
  // Simulate version change
  TestScenarios.versionUpdate(electronAPI);
  // or
  mockEnv.scenarios.versionUpdate('0.9.0', '1.0.0');

  // Your test assertions
});

3. Testing Uvicorn Startup Installation

it('should handle uvicorn starting with dependency installation', async () => {
  // Simulate uvicorn detecting missing dependencies
  TestScenarios.uvicornDepsInstall(electronAPI);

  // Trigger uvicorn startup
  electronAPI.simulateUvicornStartup();

  // Wait for installation events
  await waitFor(() => {
    expect(mockInstallationStore.startInstallation).toHaveBeenCalled();
  });
});

4. Testing UI Installation States

it('should show correct UI for each installation state', () => {
  const { result } = renderHook(() => useInstallationStore());

  // Test idle state
  expect(result.current.state).toBe('idle');
  expect(result.current.isVisible).toBe(false);

  // Test installing state
  act(() => result.current.startInstallation());
  expect(result.current.state).toBe('installing');
  expect(result.current.isVisible).toBe(true);

  // Test error state
  act(() => result.current.setError('Installation failed'));
  expect(result.current.state).toBe('error');
  expect(result.current.error).toBe('Installation failed');

  // Test completed state
  act(() => result.current.setSuccess());
  expect(result.current.state).toBe('completed');
  expect(result.current.progress).toBe(100);
});

Advanced Testing Patterns

Testing Event Sequences

it('should handle complete installation flow', async () => {
  const events: string[] = [];

  // Set up event tracking
  electronAPI.onInstallDependenciesStart(() => events.push('start'));
  electronAPI.onInstallDependenciesLog(() => events.push('log'));
  electronAPI.onInstallDependenciesComplete(() => events.push('complete'));

  // Trigger installation
  await electronAPI.checkAndInstallDepsOnUpdate();

  // Verify event sequence
  expect(events).toEqual(['start', 'log', 'log', 'complete']);
});

Testing Error Recovery

it('should recover from installation errors', async () => {
  // Set up error scenario
  TestScenarios.installationError(electronAPI);

  const store = useInstallationStore.getState();

  // Trigger installation
  await store.performInstallation();
  expect(store.state).toBe('error');

  // Simulate retry
  TestScenarios.allGood(electronAPI); // Fix the environment
  store.retryInstallation();

  await waitFor(() => {
    expect(store.state).toBe('completed');
  });
});

Testing Concurrent Operations

it('should handle concurrent installation attempts', async () => {
  const store = useInstallationStore.getState();

  // Start multiple installations
  const promise1 = store.performInstallation();
  const promise2 = store.performInstallation();

  // Should handle gracefully
  const [result1, result2] = await Promise.all([promise1, promise2]);

  expect(store.state).toBe('completed');
});

Debugging Tests

Logging Mock State

// Log current mock state
console.log('Electron API State:', electronAPI.mockState);
console.log('Environment State:', mockEnv.mockState);

// Check what functions were called
console.log(
  'checkAndInstallDepsOnUpdate calls:',
  electronAPI.checkAndInstallDepsOnUpdate.mock.calls
);

Waiting for Async Operations

import { waitForStateChange } from '../mocks/environmentMocks';

// Wait for specific state changes
await waitForStateChange(
  () => mockEnv.mockState.processes.uvSyncInProgress,
  true,
  1000 // timeout
);

Running the Tests

# Run all installation tests
npm test test/unit/store/installationStore.test.ts
npm test test/unit/hooks/useInstallationSetup.test.ts
npm test test/unit/electron/install-deps.test.ts

# Run with coverage
npm test -- --coverage

# Run in watch mode
npm test -- --watch

Common Issues and Solutions

1. Mock Not Applied

Problem: Mock functions not being called Solution: Ensure mocks are set up before importing modules

beforeEach(async () => {
  setupMocks(); // Set up first
  const module = await import('./module'); // Import after
});

2. State Not Updating

Problem: Mock state changes not reflected Solution: Use simulation functions instead of direct state mutation

// Don't do this
electronAPI.mockState.isInstalling = true;

// Do this instead
electronAPI.simulateInstallationStart();

3. Async Operations Not Completing

Problem: Tests timeout waiting for async operations Solution: Use proper wait functions and increase timeouts

await vi.waitFor(
  () => {
    expect(condition).toBe(true);
  },
  { timeout: 2000 }
);

Best Practices

  1. Reset State: Always reset mock state between tests
  2. Use Scenarios: Prefer predefined scenarios over manual state setup
  3. Test Edge Cases: Include error conditions and edge cases
  4. Verify Events: Check that the correct events are emitted
  5. Test Cleanup: Verify that resources are properly cleaned up
  6. Integration Tests: Test the complete flow, not just individual functions

Example Test Files

  • test/unit/store/installationStore.test.ts - Store state management
  • test/unit/hooks/useInstallationSetup.test.ts - Hook behavior
  • test/unit/electron/install-deps.test.ts - Backend installation logic

These test files demonstrate all the patterns and scenarios described in this README.