import { IAutomationEngine, ValidationResult } from '../../../packages/application/ports/IAutomationEngine'; import { HostedSessionConfig } from '../../../packages/domain/entities/HostedSessionConfig'; import { StepId } from '../../../packages/domain/value-objects/StepId'; import { IBrowserAutomation } from '../../../packages/application/ports/IBrowserAutomation'; import { ISessionRepository } from '../../../packages/application/ports/ISessionRepository'; import { getStepName } from './selectors/IRacingSelectorMap'; export class MockAutomationEngineAdapter implements IAutomationEngine { private automationInterval: NodeJS.Timeout | null = null; constructor( private readonly browserAutomation: IBrowserAutomation, private readonly sessionRepository: ISessionRepository ) {} async validateConfiguration(config: HostedSessionConfig): Promise { if (!config.sessionName || config.sessionName.trim() === '') { return { isValid: false, error: 'Session name is required' }; } if (!config.trackId || config.trackId.trim() === '') { return { isValid: false, error: 'Track ID is required' }; } if (!config.carIds || config.carIds.length === 0) { return { isValid: false, error: 'At least one car must be selected' }; } return { isValid: true }; } async executeStep(stepId: StepId, config: HostedSessionConfig): Promise { const sessions = await this.sessionRepository.findAll(); const session = sessions[0]; if (!session) { throw new Error('No active session found'); } // Start session if it's at step 1 and pending if (session.state.isPending() && stepId.value === 1) { session.start(); await this.sessionRepository.update(session); // Start automated progression this.startAutomation(config); } } private startAutomation(config: HostedSessionConfig): void { this.automationInterval = setInterval(async () => { try { const sessions = await this.sessionRepository.findAll(); const session = sessions[0]; if (!session || !session.state.isInProgress()) { if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } return; } const currentStep = session.currentStep; // Execute current step using the browser automation if (this.browserAutomation.executeStep) { // Use real workflow automation with IRacingSelectorMap const result = await this.browserAutomation.executeStep(currentStep, config as Record); if (!result.success) { const errorMessage = `Step ${currentStep.value} (${getStepName(currentStep.value)}) failed: ${result.error}`; console.error(errorMessage); // Stop automation and mark session as failed if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } session.fail(errorMessage); await this.sessionRepository.update(session); return; } } else { // Fallback for adapters without executeStep (e.g., MockBrowserAutomationAdapter) await this.browserAutomation.navigateToPage(`step-${currentStep.value}`); } // Transition to next step if not final if (!currentStep.isFinalStep()) { session.transitionToStep(currentStep.next()); await this.sessionRepository.update(session); } else { // Stop at step 18 if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } } } catch (error) { console.error('Automation error:', error); if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } } }, 500); // Execute each step every 500ms } public stopAutomation(): void { if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } } }