This commit is contained in:
2025-12-01 17:27:56 +01:00
parent e7ada8aa23
commit 98a09a3f2b
41 changed files with 2341 additions and 1525 deletions

View File

@@ -1,8 +1,9 @@
import { app } from 'electron';
import * as path from 'path';
import { InMemorySessionRepository } from '@/packages/infrastructure/repositories/InMemorySessionRepository';
import { MockBrowserAutomationAdapter, PlaywrightAutomationAdapter, AutomationAdapterMode } from '@/packages/infrastructure/adapters/automation';
import { MockBrowserAutomationAdapter, PlaywrightAutomationAdapter, AutomationAdapterMode, FixtureServer } from '@/packages/infrastructure/adapters/automation';
import { MockAutomationEngineAdapter } from '@/packages/infrastructure/adapters/automation/engine/MockAutomationEngineAdapter';
import { AutomationEngineAdapter } from '@/packages/infrastructure/adapters/automation/engine/AutomationEngineAdapter';
import { StartAutomationSessionUseCase } from '@/packages/application/use-cases/StartAutomationSessionUseCase';
import { CheckAuthenticationUseCase } from '@/packages/application/use-cases/CheckAuthenticationUseCase';
import { InitiateLoginUseCase } from '@/packages/application/use-cases/InitiateLoginUseCase';
@@ -105,6 +106,10 @@ function getAdapterMode(envMode: AutomationMode): AutomationAdapterMode {
return envMode === 'test' ? 'mock' : 'real';
}
function isFixtureHostedMode(): boolean {
return process.env.NODE_ENV === 'test' && process.env.COMPANION_FIXTURE_HOSTED === '1';
}
/**
* Create screen automation adapter based on configuration mode.
*
@@ -120,7 +125,8 @@ function getAdapterMode(envMode: AutomationMode): AutomationAdapterMode {
function createBrowserAutomationAdapter(
mode: AutomationMode,
logger: ILogger,
browserModeConfigLoader: BrowserModeConfigLoader
browserModeConfigLoader: BrowserModeConfigLoader,
options?: { fixtureBaseUrl?: string; forcePlaywrightReal?: boolean }
): PlaywrightAutomationAdapter | MockBrowserAutomationAdapter {
const config = loadAutomationConfig();
@@ -160,6 +166,7 @@ function createBrowserAutomationAdapter(
headless: browserModeConfig.mode === 'headless',
mode: adapterMode,
userDataDir: sessionDataPath,
baseUrl: options?.fixtureBaseUrl ?? '',
},
logger.child({ adapter: 'Playwright', mode: adapterMode }),
browserModeConfigLoader
@@ -167,6 +174,19 @@ function createBrowserAutomationAdapter(
case 'test':
default:
if (options?.forcePlaywrightReal) {
return new PlaywrightAutomationAdapter(
{
headless: browserModeConfig.mode === 'headless',
timeout: config.defaultTimeout ?? 10_000,
baseUrl: options.fixtureBaseUrl ?? '',
mode: 'real',
userDataDir: sessionDataPath,
},
logger.child({ adapter: 'Playwright', mode: 'real' }),
browserModeConfigLoader
);
}
return new MockBrowserAutomationAdapter();
}
}
@@ -178,6 +198,7 @@ export class DIContainer {
private sessionRepository!: ISessionRepository;
private browserAutomation!: PlaywrightAutomationAdapter | MockBrowserAutomationAdapter;
private automationEngine!: IAutomationEngine;
private fixtureServer: FixtureServer | null = null;
private startAutomationUseCase!: StartAutomationSessionUseCase;
private checkAuthenticationUseCase: CheckAuthenticationUseCase | null = null;
private initiateLoginUseCase: InitiateLoginUseCase | null = null;
@@ -218,23 +239,37 @@ export class DIContainer {
const config = loadAutomationConfig();
this.sessionRepository = new InMemorySessionRepository();
const fixtureMode = isFixtureHostedMode();
const fixtureBaseUrl = fixtureMode ? 'http://localhost:3456' : undefined;
this.browserAutomation = createBrowserAutomationAdapter(
config.mode,
this.logger,
this.browserModeConfigLoader
);
this.automationEngine = new MockAutomationEngineAdapter(
this.browserAutomation,
this.sessionRepository
this.browserModeConfigLoader,
{ fixtureBaseUrl, forcePlaywrightReal: fixtureMode }
);
if (fixtureMode) {
this.fixtureServer = new FixtureServer();
this.automationEngine = new AutomationEngineAdapter(
this.browserAutomation as IScreenAutomation,
this.sessionRepository
);
} else {
this.automationEngine = new MockAutomationEngineAdapter(
this.browserAutomation,
this.sessionRepository
);
}
this.startAutomationUseCase = new StartAutomationSessionUseCase(
this.automationEngine,
this.browserAutomation,
this.sessionRepository
);
// Create authentication use cases only for real mode (PlaywrightAutomationAdapter)
if (this.browserAutomation instanceof PlaywrightAutomationAdapter) {
if (this.browserAutomation instanceof PlaywrightAutomationAdapter && !fixtureMode) {
const authService = this.browserAutomation as IAuthenticationService;
this.checkAuthenticationUseCase = new CheckAuthenticationUseCase(authService);
this.initiateLoginUseCase = new InitiateLoginUseCase(authService);
@@ -347,10 +382,14 @@ export class DIContainer {
*/
public async initializeBrowserConnection(): Promise<BrowserConnectionResult> {
this.ensureInitialized();
this.logger.info('Initializing automation connection', { mode: this.automationMode });
const fixtureMode = isFixtureHostedMode();
this.logger.info('Initializing automation connection', { mode: this.automationMode, fixtureMode });
if (this.automationMode === 'production' || this.automationMode === 'development') {
if (this.automationMode === 'production' || this.automationMode === 'development' || fixtureMode) {
try {
if (fixtureMode && this.fixtureServer && !this.fixtureServer.isRunning()) {
await this.fixtureServer.start();
}
const playwrightAdapter = this.browserAutomation as PlaywrightAutomationAdapter;
const result = await playwrightAdapter.connect();
if (!result.success) {
@@ -415,6 +454,17 @@ export class DIContainer {
this.logger.error('Error disconnecting automation adapter', error instanceof Error ? error : new Error('Unknown error'));
}
}
if (this.fixtureServer && this.fixtureServer.isRunning()) {
try {
await this.fixtureServer.stop();
this.logger.info('FixtureServer stopped');
} catch (error) {
this.logger.error('Error stopping FixtureServer', error instanceof Error ? error : new Error('Unknown error'));
} finally {
this.fixtureServer = null;
}
}
this.logger.info('DIContainer shutdown complete');
}