diff --git a/package.json b/package.json index de36685..c56a626 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flycli", - "version": "1.3.1", + "version": "1.4.0", "description": "A reliable CLI tool for Betaflight flight controller interaction and automation.", "license": "MIT", "keywords": [ diff --git a/src/infrastructure/cad/EnvironmentManager.js b/src/infrastructure/cad/EnvironmentManager.js index ea7b445..4d45a83 100644 --- a/src/infrastructure/cad/EnvironmentManager.js +++ b/src/infrastructure/cad/EnvironmentManager.js @@ -1,4 +1,5 @@ import fs from 'fs/promises'; +import path from 'path'; /** * @module EnvironmentManager @@ -16,6 +17,10 @@ const WINDOWS_PATHS = [ 'C:\\Program Files\\FreeCAD 0.21\\bin\\FreeCAD.exe', 'C:\\Program Files\\FreeCAD 0.20\\bin\\FreeCAD.exe', 'C:\\Program Files\\FreeCAD 1.0\\bin\\FreeCAD.exe', + // Per-user installations + path.join(process.env.LOCALAPPDATA || '', 'Programs', 'FreeCAD 1.0', 'bin', 'freecad.exe'), + path.join(process.env.LOCALAPPDATA || '', 'Programs', 'FreeCAD 0.21', 'bin', 'freecad.exe'), + path.join(process.env.LOCALAPPDATA || '', 'Programs', 'FreeCAD 0.22', 'bin', 'freecad.exe'), ]; const UNIX_PATHS = [ diff --git a/test/unit/EnvironmentManager.test.js b/test/unit/EnvironmentManager.test.js new file mode 100644 index 0000000..900034d --- /dev/null +++ b/test/unit/EnvironmentManager.test.js @@ -0,0 +1,57 @@ +import { jest } from '@jest/globals'; + +// Mock fs/promises +const mockStat = jest.fn(); +jest.unstable_mockModule('fs/promises', () => ({ + default: { + stat: mockStat, + }, +})); + +const { EnvironmentManager } = await import('../../src/infrastructure/cad/EnvironmentManager.js'); + +describe('EnvironmentManager', () => { + beforeEach(() => { + jest.clearAllMocks(); + delete process.env.FREECAD_PATH; + }); + + it('should find FreeCAD using FREECAD_PATH override', async () => { + const customPath = '/custom/path/freecad'; + process.env.FREECAD_PATH = customPath; + mockStat.mockResolvedValue({ isFile: () => true }); + + const env = new EnvironmentManager({ info: jest.fn() }); + const found = await env.ensureEnvironmentReady(); + + expect(found).toBe(customPath); + expect(mockStat).toHaveBeenCalledWith(customPath); + }); + + it('should find FreeCAD in standard Windows paths', async () => { + // Simulate Windows platform for this test if needed, + // but the list is always present in the code. + + // Mock the first path as missing and the second as existing + // C:\Program Files\... + mockStat + .mockRejectedValueOnce(new Error('Not found')) + .mockRejectedValueOnce(new Error('Not found')) + .mockRejectedValueOnce(new Error('Not found')) + // AppData path + .mockResolvedValueOnce({ isFile: () => true }); + + const env = new EnvironmentManager({ info: jest.fn() }); + const found = await env.ensureEnvironmentReady(); + + expect(found).toBeDefined(); + expect(mockStat).toHaveBeenCalled(); + }); + + it('should throw error if FreeCAD is not found', async () => { + mockStat.mockRejectedValue(new Error('Not found')); + + const env = new EnvironmentManager({ info: jest.fn() }); + await expect(env.ensureEnvironmentReady()).rejects.toThrow(/FreeCAD installation not found/); + }); +});