import { vi, describe, it, expect, beforeEach } from 'vitest' import { listProjects } from './desktop' import { DeepPartial } from './types' import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' // Mock the electron window global const mockElectron = { readdir: vi.fn(), path: { join: vi.fn(), basename: vi.fn(), dirname: vi.fn(), }, stat: vi.fn(), statIsDirectory: vi.fn(), exists: vi.fn(), writeFile: vi.fn(), readFile: vi.fn(), os: { isMac: false, isWindows: false, }, process: { env: {}, }, getPath: vi.fn(), kittycad: vi.fn(), } vi.stubGlobal('window', { electron: mockElectron }) describe('desktop utilities', () => { const mockConfig: DeepPartial = { settings: { project: { directory: '/test/projects', }, }, } const mockFileSystem: { [key: string]: string[] } = { '/test/projects': [ '.hidden-project', 'valid-project', '.git', 'project-without-kcl-files', 'another-valid-project', ], '/test/projects/valid-project': [ 'file1.kcl', 'file2.stp', 'file3.kcl', 'directory1', ], '/test/projects/valid-project/directory1': [], '/test/projects/project-without-kcl-files': ['file3.glb'], '/test/projects/another-valid-project': [ 'file4.kcl', 'directory2', 'directory3', ], '/test/projects/another-valid-project/directory2': [], '/test/projects/another-valid-project/directory3': [], } beforeEach(() => { vi.clearAllMocks() // Setup default mock implementations mockElectron.path.join.mockImplementation((...parts: string[]) => parts.join('/') ) mockElectron.path.basename.mockImplementation((path: string) => path.split('/').pop() ) mockElectron.path.dirname.mockImplementation((path: string) => path.split('/').slice(0, -1).join('/') ) // Mock readdir to return the entries for the given path mockElectron.readdir.mockImplementation(async (path: string) => { return mockFileSystem[path] || [] }) // Mock statIsDirectory to return true if the path exists in mockFileSystem mockElectron.statIsDirectory.mockImplementation(async (path: string) => { return path in mockFileSystem }) // Mock stat to always resolve with dummy metadata mockElectron.stat.mockResolvedValue({ mtimeMs: 123, atimeMs: 456, ctimeMs: 789, size: 100, mode: 0o666, }) mockElectron.exists.mockResolvedValue(true) mockElectron.readFile.mockResolvedValue('') mockElectron.writeFile.mockResolvedValue(undefined) mockElectron.getPath.mockResolvedValue('/appData') mockElectron.kittycad.mockResolvedValue({}) }) describe('listProjects', () => { it('does not list .git directories', async () => { const projects = await listProjects(mockConfig) expect(projects.map((p) => p.name)).not.toContain('.git') }) it('lists projects excluding hidden and without .kcl files', async () => { const projects = await listProjects(mockConfig) // Verify only non-dot projects with .kcl files were included expect(projects.map((p) => p.name)).toEqual([ 'valid-project', 'another-valid-project', ]) // Verify we didn't try to get project info for dot directories expect(mockElectron.stat).not.toHaveBeenCalledWith( expect.stringContaining('/.hidden-project') ) expect(mockElectron.stat).not.toHaveBeenCalledWith( expect.stringContaining('/.git') ) // Verify that projects without .kcl files are not included expect(projects.map((p) => p.name)).not.toContain( 'project-without-kcl-files' ) }) it('correctly counts directories and files', async () => { const projects = await listProjects(mockConfig) // Verify that directories and files are counted correctly expect(projects[0].directory_count).toEqual(1) expect(projects[0].kcl_file_count).toEqual(2) expect(projects[1].directory_count).toEqual(2) expect(projects[1].kcl_file_count).toEqual(1) }) it('handles empty project directory', async () => { // Adjust mockFileSystem to simulate empty directory mockFileSystem['/test/projects'] = [] const projects = await listProjects(mockConfig) expect(projects).toEqual([]) }) }) })