fs-extra.jsā¢10.4 kB
/**
 * Centralized fs-extra Mock for Testing Infrastructure
 * 
 * Provides consistent filesystem mocking across all test files to prevent conflicts.
 * Covers all common fs-extra operations with configurable behaviors.
 */
import { vi } from 'vitest';
// Default mock data and state
const mockFileSystem = new Map(); // Simulates filesystem state
const mockStats = new Map(); // Stores file stats
// Helper function to create mock file stats
function createMockStat(size = 1024, isDirectory = false) {
  return {
    size,
    isDirectory: () => isDirectory,
    isFile: () => !isDirectory,
    mtime: new Date(),
    ctime: new Date(),
    atime: new Date(),
    mode: isDirectory ? 0o755 : 0o644
  };
}
// Reset mock filesystem state
export function resetMockFileSystem() {
  mockFileSystem.clear();
  mockStats.clear();
}
// Add mock file to virtual filesystem
export function setMockFile(path, content = '', size = content.length) {
  mockFileSystem.set(path, content);
  mockStats.set(path, createMockStat(size, false));
}
// Add mock directory to virtual filesystem
export function setMockDirectory(path) {
  mockFileSystem.set(path, null);
  mockStats.set(path, createMockStat(0, true));
}
// Check if mock file/directory exists
export function mockExists(path) {
  return mockFileSystem.has(path);
}
// File read operations
export const readFile = vi.fn().mockImplementation(async (filePath, options) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  
  if (!mockFileSystem.has(path)) {
    const error = new Error(`ENOENT: no such file or directory, open '${path}'`);
    error.code = 'ENOENT';
    error.errno = -2;
    error.path = path;
    throw error;
  }
  
  const content = mockFileSystem.get(path);
  if (content === null) {
    const error = new Error(`EISDIR: illegal operation on a directory, read`);
    error.code = 'EISDIR';
    throw error;
  }
  
  // Handle encoding options
  if (options && options.encoding === null) {
    return Buffer.from(content);
  }
  
  return content;
});
export const readFileSync = vi.fn().mockImplementation((filePath, options) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  
  if (!mockFileSystem.has(path)) {
    const error = new Error(`ENOENT: no such file or directory, open '${path}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  const content = mockFileSystem.get(path);
  if (content === null) {
    const error = new Error(`EISDIR: illegal operation on a directory, read`);
    error.code = 'EISDIR';
    throw error;
  }
  
  return content;
});
// File write operations
export const writeFile = vi.fn().mockImplementation(async (filePath, data, options) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  const content = typeof data === 'string' ? data : data.toString();
  
  setMockFile(path, content);
  return undefined;
});
export const writeFileSync = vi.fn().mockImplementation((filePath, data, options) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  const content = typeof data === 'string' ? data : data.toString();
  
  setMockFile(path, content);
  return undefined;
});
// JSON operations
export const readJson = vi.fn().mockImplementation(async (filePath) => {
  const content = await readFile(filePath);
  try {
    return JSON.parse(content);
  } catch (error) {
    const parseError = new Error(`Invalid JSON in ${filePath}`);
    parseError.code = 'EJSONPARSE';
    throw parseError;
  }
});
export const readJsonSync = vi.fn().mockImplementation((filePath) => {
  const content = readFileSync(filePath);
  try {
    return JSON.parse(content);
  } catch (error) {
    const parseError = new Error(`Invalid JSON in ${filePath}`);
    parseError.code = 'EJSONPARSE';
    throw parseError;
  }
});
export const writeJson = vi.fn().mockImplementation(async (filePath, object, options = {}) => {
  const spaces = options.spaces || 0;
  const content = JSON.stringify(object, null, spaces);
  await writeFile(filePath, content);
  return undefined;
});
export const writeJsonSync = vi.fn().mockImplementation((filePath, object, options = {}) => {
  const spaces = options.spaces || 0;
  const content = JSON.stringify(object, null, spaces);
  writeFileSync(filePath, content);
  return undefined;
});
// Directory operations
export const ensureDir = vi.fn().mockImplementation(async (dirPath) => {
  const path = typeof dirPath === 'string' ? dirPath : dirPath.toString();
  setMockDirectory(path);
  return undefined;
});
export const ensureDirSync = vi.fn().mockImplementation((dirPath) => {
  const path = typeof dirPath === 'string' ? dirPath : dirPath.toString();
  setMockDirectory(path);
  return undefined;
});
export const mkdirp = vi.fn().mockImplementation(async (dirPath) => {
  await ensureDir(dirPath);
  return undefined;
});
export const mkdirpSync = vi.fn().mockImplementation((dirPath) => {
  ensureDirSync(dirPath);
  return undefined;
});
export const emptyDir = vi.fn().mockImplementation(async (dirPath) => {
  // In real implementation, this would empty the directory
  // For testing, we'll just ensure it exists
  await ensureDir(dirPath);
  return undefined;
});
export const emptyDirSync = vi.fn().mockImplementation((dirPath) => {
  ensureDirSync(dirPath);
  return undefined;
});
// Path operations
export const pathExists = vi.fn().mockImplementation(async (filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  return mockFileSystem.has(path);
});
export const pathExistsSync = vi.fn().mockImplementation((filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  return mockFileSystem.has(path);
});
// File stats
export const stat = vi.fn().mockImplementation(async (filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  
  if (!mockFileSystem.has(path)) {
    const error = new Error(`ENOENT: no such file or directory, stat '${path}'`);
    error.code = 'ENOENT';
    error.errno = -2;
    error.path = path;
    throw error;
  }
  
  return mockStats.get(path) || createMockStat();
});
export const statSync = vi.fn().mockImplementation((filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  
  if (!mockFileSystem.has(path)) {
    const error = new Error(`ENOENT: no such file or directory, stat '${path}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  return mockStats.get(path) || createMockStat();
});
// File operations
export const remove = vi.fn().mockImplementation(async (filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  mockFileSystem.delete(path);
  mockStats.delete(path);
  return undefined;
});
export const removeSync = vi.fn().mockImplementation((filePath) => {
  const path = typeof filePath === 'string' ? filePath : filePath.toString();
  mockFileSystem.delete(path);
  mockStats.delete(path);
  return undefined;
});
export const rename = vi.fn().mockImplementation(async (oldPath, newPath) => {
  const oldPathStr = typeof oldPath === 'string' ? oldPath : oldPath.toString();
  const newPathStr = typeof newPath === 'string' ? newPath : newPath.toString();
  
  if (!mockFileSystem.has(oldPathStr)) {
    const error = new Error(`ENOENT: no such file or directory, rename '${oldPathStr}' -> '${newPathStr}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  const content = mockFileSystem.get(oldPathStr);
  const stats = mockStats.get(oldPathStr);
  
  mockFileSystem.delete(oldPathStr);
  mockStats.delete(oldPathStr);
  
  mockFileSystem.set(newPathStr, content);
  if (stats) {
    mockStats.set(newPathStr, stats);
  }
  
  return undefined;
});
export const renameSync = vi.fn().mockImplementation((oldPath, newPath) => {
  const oldPathStr = typeof oldPath === 'string' ? oldPath : oldPath.toString();
  const newPathStr = typeof newPath === 'string' ? newPath : newPath.toString();
  
  if (!mockFileSystem.has(oldPathStr)) {
    const error = new Error(`ENOENT: no such file or directory, rename '${oldPathStr}' -> '${newPathStr}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  const content = mockFileSystem.get(oldPathStr);
  const stats = mockStats.get(oldPathStr);
  
  mockFileSystem.delete(oldPathStr);
  mockStats.delete(oldPathStr);
  
  mockFileSystem.set(newPathStr, content);
  if (stats) {
    mockStats.set(newPathStr, stats);
  }
  
  return undefined;
});
// Copy operations
export const copy = vi.fn().mockImplementation(async (src, dest, options = {}) => {
  const srcPath = typeof src === 'string' ? src : src.toString();
  const destPath = typeof dest === 'string' ? dest : dest.toString();
  
  if (!mockFileSystem.has(srcPath)) {
    const error = new Error(`ENOENT: no such file or directory, open '${srcPath}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  const content = mockFileSystem.get(srcPath);
  const stats = mockStats.get(srcPath);
  
  mockFileSystem.set(destPath, content);
  if (stats) {
    mockStats.set(destPath, { ...stats });
  }
  
  return undefined;
});
export const copySync = vi.fn().mockImplementation((src, dest, options = {}) => {
  const srcPath = typeof src === 'string' ? src : src.toString();
  const destPath = typeof dest === 'string' ? dest : dest.toString();
  
  if (!mockFileSystem.has(srcPath)) {
    const error = new Error(`ENOENT: no such file or directory, open '${srcPath}'`);
    error.code = 'ENOENT';
    throw error;
  }
  
  const content = mockFileSystem.get(srcPath);
  const stats = mockStats.get(srcPath);
  
  mockFileSystem.set(destPath, content);
  if (stats) {
    mockStats.set(destPath, { ...stats });
  }
  
  return undefined;
});
// Utility functions for tests
export const getMockFileSystem = () => new Map(mockFileSystem);
export const getMockStats = () => new Map(mockStats);
// Default export with all methods
export default {
  readFile,
  readFileSync,
  writeFile,
  writeFileSync,
  readJson,
  readJsonSync,
  writeJson,
  writeJsonSync,
  ensureDir,
  ensureDirSync,
  mkdirp,
  mkdirpSync,
  emptyDir,
  emptyDirSync,
  pathExists,
  pathExistsSync,
  stat,
  statSync,
  remove,
  removeSync,
  rename,
  renameSync,
  copy,
  copySync,
  
  // Utility methods
  resetMockFileSystem,
  setMockFile,
  setMockDirectory,
  mockExists,
  getMockFileSystem,
  getMockStats
};