import { join } from 'path'; import { readFileSync, existsSync } from 'fs'; export interface TestDataFixture { name: string; description?: string; data: any; type: 'json' | 'yaml' | 'text' | 'binary'; } export interface FixtureLoader { loadFixture(name: string): TestDataFixture; loadFixtures(pattern: string): TestDataFixture[]; getFixturePath(name: string): string; fixtureExists(name: string): boolean; } export class FileSystemFixtureLoader implements FixtureLoader { private readonly fixturesPath: string; private readonly cache: Map = new Map(); constructor(fixturesPath?: string) { this.fixturesPath = fixturesPath || join(__dirname, '../fixtures'); } loadFixture(name: string): TestDataFixture { // Check cache first if (this.cache.has(name)) { return this.cache.get(name)!; } const fixturePath = this.getFixturePath(name); if (!this.fixtureExists(name)) { throw new Error(`Fixture not found: ${name} at path ${fixturePath}`); } try { const content = readFileSync(fixturePath, 'utf-8'); const fixture = this.parseFixtureContent(name, content); // Cache the fixture this.cache.set(name, fixture); return fixture; } catch (error) { throw new Error(`Failed to load fixture ${name}: ${error}`); } } loadFixtures(_pattern: string): TestDataFixture[] { // For now, implement basic pattern matching // In a real implementation, you might use glob patterns const fixtures: TestDataFixture[] = []; // This is a simplified implementation // You could extend this to use glob patterns or other matching logic return fixtures; } getFixturePath(name: string): string { // Support different file extensions const extensions = ['.json', '.yaml', '.yml', '.txt', '.xml']; for (const ext of extensions) { const fullPath = join(this.fixturesPath, `${name}${ext}`); if (existsSync(fullPath)) { return fullPath; } } // Default to .json if no extension provided return join(this.fixturesPath, name.includes('.') ? name : `${name}.json`); } fixtureExists(name: string): boolean { return existsSync(this.getFixturePath(name)); } private parseFixtureContent(name: string, content: string): TestDataFixture { const fixturePath = this.getFixturePath(name); const extension = fixturePath.split('.').pop()?.toLowerCase(); let data: any; let type: TestDataFixture['type']; switch (extension) { case 'json': try { data = JSON.parse(content); type = 'json'; } catch (error) { throw new Error(`Invalid JSON in fixture ${name}: ${error}`); } break; case 'yaml': case 'yml': // For YAML parsing, you might want to add a YAML library like 'js-yaml' // For now, treat as text data = content; type = 'yaml'; break; case 'txt': case 'md': data = content; type = 'text'; break; default: data = content; type = 'text'; } return { name, data, type, description: `Test fixture: ${name}` }; } clearCache(): void { this.cache.clear(); } } // Global fixture loader instance export const fixtureLoader = new FileSystemFixtureLoader();