Skip to main content
Glama
esm-mock-helper.ts7.75 kB
/** * ESM Mock Helper * * Provides utilities for ESM-compatible module mocking in Jest. * Uses vi.mock() which works with ES modules. * * @see Issue #308 - Implement ESM-compatible Jest mocking * * Usage: * ```typescript * import { setupESMMocks, resetESMMocks } from './esm-mock-helper.js'; * * describe('MyTest', () => { * let myModule: any; * * beforeAll(async () => { * await setupESMMocks({ * '../../src/utils/some-module.js': { * someFunction: vi.fn().mockReturnValue('mocked') * } * }); * myModule = await import('../../src/tools/my-tool.js'); * }); * * beforeEach(() => { * resetESMMocks(); * }); * }); * ``` */ import { vi, type Mock } from 'vitest'; /** * Type for mock module definitions */ export type MockModuleDefinition = Record<string, Mock | unknown>; /** * Registry of registered mock modules for cleanup */ const registeredMocks: Map<string, MockModuleDefinition> = new Map(); /** * Setup ESM-compatible mocks using vi.mock * * IMPORTANT: This must be called BEFORE importing the module under test. * The order matters in ESM: * 1. Reset modules * 2. Set up mocks * 3. Import the module under test * * @param mocks - Object mapping module paths to their mock implementations */ export async function setupESMMocks(mocks: Record<string, MockModuleDefinition>): Promise<void> { // Reset modules to ensure clean state vi.resetModules(); for (const [modulePath, mockImpl] of Object.entries(mocks)) { // Store for later cleanup/reset registeredMocks.set(modulePath, mockImpl); // Use unstable_mockModule for ESM compatibility vi.mock(modulePath, () => ({ __esModule: true, ...mockImpl, })); } } /** * Reset all registered mock functions * Call this in beforeEach to reset mock call counts */ export function resetESMMocks(): void { for (const mockImpl of registeredMocks.values()) { for (const value of Object.values(mockImpl)) { if (typeof value === 'function' && 'mockReset' in value) { (value as Mock).mockReset(); } } } } /** * Clear all registered mock functions * Call this in afterEach to clear mock call history */ export function clearESMMocks(): void { for (const mockImpl of registeredMocks.values()) { for (const value of Object.values(mockImpl)) { if (typeof value === 'function' && 'mockClear' in value) { (value as Mock).mockClear(); } } } } /** * Get a registered mock by module path */ export function getMock(modulePath: string): MockModuleDefinition | undefined { return registeredMocks.get(modulePath); } /** * Common mock factories for frequently mocked modules */ export const MockFactories = { /** * Create a mock ResearchOrchestrator */ createResearchOrchestrator: ( overrides?: Partial<{ answerResearchQuestion: Mock; }> ) => ({ ResearchOrchestrator: vi.fn().mockImplementation(() => ({ answerResearchQuestion: overrides?.answerResearchQuestion ?? vi.fn().mockResolvedValue({ answer: 'Mock research answer', confidence: 0.8, sources: [], metadata: { filesAnalyzed: 0, duration: 100, sourcesQueried: [] }, needsWebSearch: false, }), })), }), /** * Create a mock MemoryEntityManager */ createMemoryEntityManager: () => ({ MemoryEntityManager: vi.fn().mockImplementation(() => ({ initialize: vi.fn().mockResolvedValue(undefined), upsertEntity: vi.fn().mockResolvedValue({ id: 'mock-entity-id' }), upsertRelationship: vi.fn().mockResolvedValue(undefined), queryEntities: vi.fn().mockResolvedValue({ entities: [] }), updateEntity: vi.fn().mockResolvedValue(undefined), })), }), /** * Create a mock EnhancedLogger */ createEnhancedLogger: () => ({ EnhancedLogger: vi.fn().mockImplementation(() => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), clearLogs: vi.fn(), })), logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), clearLogs: vi.fn(), }, }), /** * Create a mock AI executor */ createAIExecutor: (overrides?: { isAvailable?: boolean }) => ({ getAIExecutor: vi.fn().mockReturnValue({ isAvailable: () => overrides?.isAvailable ?? false, executeStructuredPrompt: vi.fn().mockResolvedValue({ data: { isValid: true, confidence: 0.9, findings: [], recommendations: [] }, raw: { metadata: {} }, }), }), }), /** * Create a mock KnowledgeGraphManager */ createKnowledgeGraphManager: () => ({ KnowledgeGraphManager: vi.fn().mockImplementation(() => ({ getActiveConnections: vi.fn().mockResolvedValue([]), getPatternHistory: vi.fn().mockResolvedValue([]), addNode: vi.fn(), addEdge: vi.fn(), getNode: vi.fn(), getRelatedNodes: vi.fn().mockReturnValue([]), })), }), /** * Create mock environment analysis utilities */ createEnvironmentAnalysis: () => ({ analyzeEnvironmentSpecs: vi.fn().mockResolvedValue({ analysisPrompt: 'Environment Specification Analysis\n\nMock analysis prompt', instructions: 'Mock instructions', actualData: {}, }), detectContainerization: vi.fn().mockResolvedValue({ analysisPrompt: 'Containerization Technology Detection\n\nMock containerization analysis', instructions: 'Mock instructions', actualData: {}, }), determineEnvironmentRequirements: vi.fn().mockResolvedValue({ analysisPrompt: 'Environment Requirements from ADRs\n\nMock requirements analysis', instructions: 'Mock instructions', actualData: {}, }), assessEnvironmentCompliance: vi.fn().mockResolvedValue({ analysisPrompt: 'Environment Compliance Assessment\n\nMock compliance analysis', instructions: 'Mock instructions', actualData: {}, }), }), /** * Create mock knowledge generation */ createKnowledgeGeneration: () => ({ generateArchitecturalKnowledge: vi.fn().mockResolvedValue({ prompt: 'Generated Knowledge Prompting\n\nMock knowledge enhancement', confidence: 0.9, }), }), /** * Create mock prompt execution */ createPromptExecution: () => ({ executePromptWithFallback: vi.fn().mockResolvedValue({ content: 'Mock prompt execution result', isAIGenerated: false, }), formatMCPResponse: vi.fn((content: string) => ({ content: [{ type: 'text', text: content }], })), }), }; /** * Helper to create a complete mock setup for environment analysis tests */ export async function setupEnvironmentAnalysisMocks(): Promise<void> { await setupESMMocks({ '../../src/utils/research-orchestrator.js': MockFactories.createResearchOrchestrator(), '../../src/utils/memory-entity-manager.js': MockFactories.createMemoryEntityManager(), '../../src/utils/enhanced-logging.js': MockFactories.createEnhancedLogger(), '../../src/utils/environment-analysis.js': MockFactories.createEnvironmentAnalysis(), '../../src/utils/knowledge-generation.js': MockFactories.createKnowledgeGeneration(), '../../src/utils/prompt-execution.js': MockFactories.createPromptExecution(), }); } /** * Helper to create a complete mock setup for ADR validation tests */ export async function setupAdrValidationMocks(): Promise<void> { await setupESMMocks({ '../../src/utils/research-orchestrator.js': MockFactories.createResearchOrchestrator(), '../../src/utils/ai-executor.js': MockFactories.createAIExecutor(), '../../src/utils/knowledge-graph-manager.js': MockFactories.createKnowledgeGraphManager(), }); }

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/tosin2013/mcp-adr-analysis-server'

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