Skip to main content
Glama
file-utils.test.ts6.67 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { promises as fs } from 'fs'; import type { Stats } from 'fs'; import { join, resolve } from 'path'; // Mock Dirent interface for testing interface MockDirent { name: string; isDirectory(): boolean; isFile(): boolean; } import { fileExists, isDirectory, findTestFiles, findProjectRoot } from '../file-utils'; vi.mock('fs', () => ({ promises: { access: vi.fn(), stat: vi.fn(), readdir: vi.fn() } })); describe('file-utils', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('fileExists', () => { it('should return true when file exists', async () => { vi.mocked(fs.access).mockResolvedValue(undefined); const result = await fileExists('/path/to/file'); expect(result).toBe(true); expect(fs.access).toHaveBeenCalledWith('/path/to/file'); }); it('should return false when file does not exist', async () => { vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT')); const result = await fileExists('/path/to/nonexistent'); expect(result).toBe(false); }); }); describe('isDirectory', () => { it('should return true for directories', async () => { vi.mocked(fs.stat).mockResolvedValue({ isDirectory: () => true } as Stats); const result = await isDirectory('/path/to/dir'); expect(result).toBe(true); expect(fs.stat).toHaveBeenCalledWith('/path/to/dir'); }); it('should return false for files', async () => { vi.mocked(fs.stat).mockResolvedValue({ isDirectory: () => false } as Stats); const result = await isDirectory('/path/to/file'); expect(result).toBe(false); }); it('should return false when stat fails', async () => { vi.mocked(fs.stat).mockRejectedValue(new Error('ENOENT')); const result = await isDirectory('/path/to/nonexistent'); expect(result).toBe(false); }); }); describe('findTestFiles', () => { it('should find test files recursively', async () => { const mockDirEntries = [ { name: 'file.test.ts', isDirectory: () => false, isFile: () => true }, { name: 'file.spec.js', isDirectory: () => false, isFile: () => true }, { name: 'src', isDirectory: () => true, isFile: () => false }, { name: 'node_modules', isDirectory: () => true, isFile: () => false }, { name: 'regular.ts', isDirectory: () => false, isFile: () => true } ] as MockDirent[]; const mockSrcEntries = [ { name: 'component.test.tsx', isDirectory: () => false, isFile: () => true }, { name: '__tests__', isDirectory: () => true, isFile: () => false } ] as MockDirent[]; const mockTestsEntries = [ { name: 'unit.test.js', isDirectory: () => false, isFile: () => true } ] as MockDirent[]; vi.mocked(fs.readdir) .mockResolvedValueOnce(mockDirEntries) .mockResolvedValueOnce(mockSrcEntries) .mockResolvedValueOnce(mockTestsEntries); const result = await findTestFiles('/project'); expect(result).toHaveLength(4); expect(result[0].relativePath).toBe('file.spec.js'); expect(result[1].relativePath).toBe('file.test.ts'); expect(result[2].relativePath).toBe(join('src', '__tests__', 'unit.test.js')); expect(result[3].relativePath).toBe(join('src', 'component.test.tsx')); }); it('should determine test types correctly', async () => { const mockEntries = [ { name: 'unit.test.ts', isDirectory: () => false, isFile: () => true }, { name: 'e2e', isDirectory: () => true, isFile: () => false }, { name: 'integration', isDirectory: () => true, isFile: () => false } ] as MockDirent[]; const mockE2EEntries = [ { name: 'app.test.ts', isDirectory: () => false, isFile: () => true } ] as MockDirent[]; const mockIntegrationEntries = [ { name: 'api.test.ts', isDirectory: () => false, isFile: () => true } ] as MockDirent[]; vi.mocked(fs.readdir) .mockResolvedValueOnce(mockEntries) .mockResolvedValueOnce(mockE2EEntries) .mockResolvedValueOnce(mockIntegrationEntries); const result = await findTestFiles('/project'); expect(result.find(f => f.relativePath === 'unit.test.ts')?.type).toBe('unit'); expect(result.find(f => f.relativePath.includes('e2e'))?.type).toBe('e2e'); expect(result.find(f => f.relativePath.includes('integration'))?.type).toBe('integration'); }); it('should skip excluded directories', async () => { const mockEntries = [ { name: 'node_modules', isDirectory: () => true, isFile: () => false }, { name: 'dist', isDirectory: () => true, isFile: () => false }, { name: 'build', isDirectory: () => true, isFile: () => false }, { name: '.git', isDirectory: () => true, isFile: () => false }, { name: 'test.spec.ts', isDirectory: () => false, isFile: () => true } ] as MockDirent[]; vi.mocked(fs.readdir).mockResolvedValueOnce(mockEntries); const result = await findTestFiles('/project'); expect(result).toHaveLength(1); expect(result[0].relativePath).toBe('test.spec.ts'); expect(fs.readdir).toHaveBeenCalledTimes(1); }); it('should handle read errors gracefully', async () => { vi.mocked(fs.readdir).mockRejectedValue(new Error('Permission denied')); const result = await findTestFiles('/restricted'); expect(result).toEqual([]); }); }); describe('findProjectRoot', () => { it('should find project root with package.json', async () => { const originalCwd = process.cwd; process.cwd = vi.fn().mockReturnValue('/project/src/utils'); vi.mocked(fs.access) .mockRejectedValueOnce(new Error('Not found')) .mockRejectedValueOnce(new Error('Not found')) .mockResolvedValueOnce(undefined); const result = await findProjectRoot(); expect(result).toBe(resolve('/project')); process.cwd = originalCwd; }); it('should return start path if no package.json found', async () => { vi.mocked(fs.access).mockRejectedValue(new Error('Not found')); const result = await findProjectRoot('/some/deep/path'); expect(result).toBe('/some/deep/path'); }); it('should handle root directory correctly', async () => { vi.mocked(fs.access).mockRejectedValue(new Error('Not found')); const result = await findProjectRoot('/'); expect(result).toBe('/'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/djankies/vitest-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server