move automation out of core

This commit is contained in:
2025-12-16 14:31:43 +01:00
parent 29dc11deb9
commit 29410708c8
145 changed files with 378 additions and 1532 deletions

View File

@@ -1,6 +1,6 @@
import { AuthenticationState } from '../../domain/value-objects/AuthenticationState'; import { AuthenticationState } from '../../domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '../../domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from '../../domain/value-objects/BrowserAuthenticationState';
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
/** /**
* Port for authentication services implementing zero-knowledge login. * Port for authentication services implementing zero-knowledge login.

View File

@@ -2,7 +2,7 @@ export type AutomationEvent = {
actionId?: string actionId?: string
type: 'panel-attached'|'modal-opened'|'action-started'|'action-complete'|'action-failed'|'panel-missing' type: 'panel-attached'|'modal-opened'|'action-started'|'action-complete'|'action-failed'|'panel-missing'
timestamp: number timestamp: number
payload?: any payload?: unknown
} }
export interface AutomationEventPublisherPort { export interface AutomationEventPublisherPort {

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { CheckoutConfirmation } from '../../domain/value-objects/CheckoutConfirmation'; import { CheckoutConfirmation } from '../../domain/value-objects/CheckoutConfirmation';
import type { CheckoutConfirmationRequestDTO } from '../dto/CheckoutConfirmationRequestDTO'; import type { CheckoutConfirmationRequestDTO } from '../dto/CheckoutConfirmationRequestDTO';

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { CheckoutInfoDTO } from '../dto/CheckoutInfoDTO'; import type { CheckoutInfoDTO } from '../dto/CheckoutInfoDTO';
export interface CheckoutServicePort { export interface CheckoutServicePort {

View File

@@ -2,7 +2,7 @@ export type AutomationEvent = {
actionId?: string actionId?: string
type: 'panel-attached'|'modal-opened'|'action-started'|'action-complete'|'action-failed'|'panel-missing' type: 'panel-attached'|'modal-opened'|'action-started'|'action-complete'|'action-failed'|'panel-missing'
timestamp: number timestamp: number
payload?: any payload?: unknown
} }
export interface IAutomationEventPublisher { export interface IAutomationEventPublisher {

View File

@@ -1,3 +1,4 @@
import { SessionStateValue } from '@/automation/domain/value-objects/SessionState';
import { AutomationSession } from '../../domain/entities/AutomationSession'; import { AutomationSession } from '../../domain/entities/AutomationSession';

View File

@@ -1,4 +1,4 @@
import type { Result } from '../../../shared/result/Result'; import type { Result } from '@gridpilot/shared/result/Result';
export interface SessionValidatorPort { export interface SessionValidatorPort {
validateSession(): Promise<Result<boolean>>; validateSession(): Promise<Result<boolean>>;

View File

@@ -1,7 +1,7 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { OverlayAction } from '@core/automation/application/ports/IOverlaySyncPort' import { OverlayAction } from 'apps/companion/main/automation/application/ports/IOverlaySyncPort'
import { IAutomationLifecycleEmitter, LifecycleCallback } from '@core/automation/infrastructure//IAutomationLifecycleEmitter' import { IAutomationLifecycleEmitter, LifecycleCallback } from '@core/automation/infrastructure//IAutomationLifecycleEmitter'
import { OverlaySyncService } from '@core/automation/application/services/OverlaySyncService' import { OverlaySyncService } from 'apps/companion/main/automation/application/services/OverlaySyncService'
class MockLifecycleEmitter implements IAutomationLifecycleEmitter { class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
private callbacks: Set<LifecycleCallback> = new Set() private callbacks: Set<LifecycleCallback> = new Set()

View File

@@ -3,6 +3,8 @@ import { AutomationEventPublisherPort, AutomationEvent } from '../ports/Automati
import { AutomationLifecycleEmitterPort, LifecycleCallback } from '../ports/AutomationLifecycleEmitterPort'; import { AutomationLifecycleEmitterPort, LifecycleCallback } from '../ports/AutomationLifecycleEmitterPort';
import { LoggerPort } from '../ports/LoggerPort'; import { LoggerPort } from '../ports/LoggerPort';
import type { IAsyncApplicationService } from '@core/shared/application'; import type { IAsyncApplicationService } from '@core/shared/application';
import { OverlayAction, OverlaySyncPort } from '../ports/OverlaySyncPort';
import { ActionAck } from '../ports/IOverlaySyncPort';
type ConstructorArgs = { type ConstructorArgs = {
lifecycleEmitter: AutomationLifecycleEmitterPort lifecycleEmitter: AutomationLifecycleEmitterPort

View File

@@ -1,9 +1,9 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { CheckAuthenticationUseCase } from '@core/automation/application/use-cases/CheckAuthenticationUseCase'; import { CheckAuthenticationUseCase } from 'apps/companion/main/automation/application/use-cases/CheckAuthenticationUseCase';
import { AuthenticationState } from '@core/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from 'apps/companion/main/automation/domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '@core/automation/domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from 'apps/companion/main/automation/domain/value-objects/BrowserAuthenticationState';
import { Result } from '@core/shared/result/Result'; import { Result } from '@core/shared/result/Result';
import type { AuthenticationServicePort } from '@core/automation/application/ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from 'apps/companion/main/automation/application/ports/AuthenticationServicePort';
interface ISessionValidator { interface ISessionValidator {
validateSession(): Promise<Result<boolean>>; validateSession(): Promise<Result<boolean>>;

View File

@@ -1,6 +1,6 @@
import { AuthenticationState } from '../../domain/value-objects/AuthenticationState'; import { AuthenticationState } from '../../domain/value-objects/AuthenticationState';
import type { Logger } from '@core/shared/application'; import type { Logger } from '@core/shared/application';
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort';
import { SessionLifetime } from '../../domain/value-objects/SessionLifetime'; import { SessionLifetime } from '../../domain/value-objects/SessionLifetime';
import type { SessionValidatorPort } from '../ports/SessionValidatorPort'; import type { SessionValidatorPort } from '../ports/SessionValidatorPort';
@@ -38,7 +38,7 @@ export class CheckAuthenticationUseCase {
this.logger.debug('Performing file-based authentication check.'); this.logger.debug('Performing file-based authentication check.');
const fileResult = await this.authService.checkSession(); const fileResult = await this.authService.checkSession();
if (fileResult.isErr()) { if (fileResult.isErr()) {
this.logger.error('File-based authentication check failed.', { error: fileResult.unwrapErr() }); this.logger.error('File-based authentication check failed.', fileResult.unwrapErr());
return fileResult; return fileResult;
} }
this.logger.info('File-based authentication check succeeded.'); this.logger.info('File-based authentication check succeeded.');
@@ -66,7 +66,7 @@ export class CheckAuthenticationUseCase {
} }
this.logger.debug('Session is not expired.'); this.logger.debug('Session is not expired.');
} catch (error) { } catch (error) {
this.logger.error('Invalid expiry date encountered, treating session as expired.', { expiry, error }); this.logger.error('Invalid expiry date encountered, treating session as expired.', error as Error, { expiry });
// Invalid expiry date, treat as expired for safety // Invalid expiry date, treat as expired for safety
return Result.ok(AuthenticationState.EXPIRED); return Result.ok(AuthenticationState.EXPIRED);
} }
@@ -112,7 +112,7 @@ export class CheckAuthenticationUseCase {
this.logger.info(`CheckAuthenticationUseCase completed successfully with state: ${fileState}`); this.logger.info(`CheckAuthenticationUseCase completed successfully with state: ${fileState}`);
return Result.ok(fileState); return Result.ok(fileState);
} catch (error) { } catch (error) {
this.logger.error('An unexpected error occurred during authentication check.', { error }); this.logger.error('An unexpected error occurred during authentication check.', error as Error);
throw error; throw error;
} }
} }

View File

@@ -0,0 +1,106 @@
import { vi, Mock } from 'vitest';
import { ClearSessionUseCase } from './ClearSessionUseCase';
import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort';
import type { Logger } from '@core/shared/application';
import { Result } from '@gridpilot/shared/result/Result';
describe('ClearSessionUseCase', () => {
let useCase: ClearSessionUseCase;
let authService: AuthenticationServicePort;
let logger: Logger;
beforeEach(() => {
const mockAuthService = {
clearSession: vi.fn(),
checkSession: vi.fn(),
initiateLogin: vi.fn(),
getState: vi.fn(),
validateServerSide: vi.fn(),
refreshSession: vi.fn(),
getSessionExpiry: vi.fn(),
verifyPageAuthentication: vi.fn(),
};
const mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
authService = mockAuthService as unknown as AuthenticationServicePort;
logger = mockLogger as Logger;
useCase = new ClearSessionUseCase(authService, logger);
});
describe('execute', () => {
it('should clear session successfully and return ok result', async () => {
const successResult = Result.ok<void>(undefined);
(authService.clearSession as Mock).mockResolvedValue(successResult);
const result = await useCase.execute();
expect(authService.clearSession).toHaveBeenCalledTimes(1);
expect(logger.debug).toHaveBeenCalledWith('Attempting to clear user session.', {
useCase: 'ClearSessionUseCase'
});
expect(logger.info).toHaveBeenCalledWith('User session cleared successfully.', {
useCase: 'ClearSessionUseCase'
});
expect(result.isOk()).toBe(true);
});
it('should handle clearSession failure and return err result', async () => {
const error = new Error('Clear session failed');
const failureResult = Result.err<void>(error);
(authService.clearSession as Mock).mockResolvedValue(failureResult);
const result = await useCase.execute();
expect(authService.clearSession).toHaveBeenCalledTimes(1);
expect(logger.debug).toHaveBeenCalledWith('Attempting to clear user session.', {
useCase: 'ClearSessionUseCase'
});
expect(logger.warn).toHaveBeenCalledWith('Failed to clear user session.', {
useCase: 'ClearSessionUseCase',
error: error,
});
expect(result.isErr()).toBe(true);
expect(result.error).toBe(error);
});
it('should handle unexpected errors and return err result with Error', async () => {
const thrownError = new Error('Unexpected error');
(authService.clearSession as Mock).mockRejectedValue(thrownError);
const result = await useCase.execute();
expect(authService.clearSession).toHaveBeenCalledTimes(1);
expect(logger.debug).toHaveBeenCalledWith('Attempting to clear user session.', {
useCase: 'ClearSessionUseCase'
});
expect(logger.error).toHaveBeenCalledWith('Error clearing user session.', thrownError, {
useCase: 'ClearSessionUseCase'
});
expect(result.isErr()).toBe(true);
expect(result.error).toBeInstanceOf(Error);
expect(result.error?.message).toBe('Unexpected error');
});
it('should handle non-Error thrown values and convert to Error', async () => {
const thrownValue = 'String error';
(authService.clearSession as Mock).mockRejectedValue(thrownValue);
const result = await useCase.execute();
expect(authService.clearSession).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith('Error clearing user session.', expect.any(Error), {
useCase: 'ClearSessionUseCase'
});
expect(result.isErr()).toBe(true);
expect(result.error).toBeInstanceOf(Error);
expect(result.error?.message).toBe('String error');
});
});
});

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort';
import type { Logger } from '@core/shared/application'; import type { Logger } from '@core/shared/application';
@@ -26,7 +26,7 @@ export class ClearSessionUseCase {
try { try {
const result = await this.authService.clearSession(); const result = await this.authService.clearSession();
if (result.isSuccess) { if (result.isOk()) {
this.logger.info('User session cleared successfully.', { this.logger.info('User session cleared successfully.', {
useCase: 'ClearSessionUseCase' useCase: 'ClearSessionUseCase'
}); });
@@ -38,10 +38,11 @@ export class ClearSessionUseCase {
} }
return result; return result;
} catch (error) { } catch (error) {
this.logger.error('Error clearing user session.', error, { const err = error instanceof Error ? error : new Error(String(error));
this.logger.error('Error clearing user session.', err, {
useCase: 'ClearSessionUseCase' useCase: 'ClearSessionUseCase'
}); });
return Result.fail(error.message); return Result.err(err);
} }
} }
} }

View File

@@ -1,10 +1,10 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CompleteRaceCreationUseCase } from '@core/automation/application/use-cases/CompleteRaceCreationUseCase'; import { CompleteRaceCreationUseCase } from 'apps/companion/main/automation/application/use-cases/CompleteRaceCreationUseCase';
import { Result } from '@core/shared/result/Result'; import { Result } from '@core/shared/result/Result';
import { RaceCreationResult } from '@core/automation/domain/value-objects/RaceCreationResult'; import { RaceCreationResult } from 'apps/companion/main/automation/domain/value-objects/RaceCreationResult';
import { CheckoutPrice } from '@core/automation/domain/value-objects/CheckoutPrice'; import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
import type { CheckoutServicePort } from '@core/automation/application/ports/CheckoutServicePort'; import type { CheckoutServicePort } from 'apps/companion/main/automation/application/ports/CheckoutServicePort';
import { CheckoutState } from '@core/automation/domain/value-objects/CheckoutState'; import { CheckoutState } from 'apps/companion/main/automation/domain/value-objects/CheckoutState';
describe('CompleteRaceCreationUseCase', () => { describe('CompleteRaceCreationUseCase', () => {
let mockCheckoutService: CheckoutServicePort; let mockCheckoutService: CheckoutServicePort;

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { RaceCreationResult } from '../../domain/value-objects/RaceCreationResult'; import { RaceCreationResult } from '../../domain/value-objects/RaceCreationResult';
import type { CheckoutServicePort } from '../ports/CheckoutServicePort'; import type { CheckoutServicePort } from '../ports/CheckoutServicePort';
import type { Logger } from '@core/shared/application'; import type { Logger } from '@core/shared/application';

View File

@@ -1,12 +1,12 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { Result } from '@core/shared/result/Result'; import { Result } from '@core/shared/result/Result';
import { ConfirmCheckoutUseCase } from '@core/automation/application/use-cases/ConfirmCheckoutUseCase'; import { ConfirmCheckoutUseCase } from 'apps/companion/main/automation/application/use-cases/ConfirmCheckoutUseCase';
import type { CheckoutServicePort } from '@core/automation/application/ports/CheckoutServicePort'; import type { CheckoutServicePort } from 'apps/companion/main/automation/application/ports/CheckoutServicePort';
import type { CheckoutConfirmationPort } from '@core/automation/application/ports/CheckoutConfirmationPort'; import type { CheckoutConfirmationPort } from 'apps/companion/main/automation/application/ports/CheckoutConfirmationPort';
import type { CheckoutInfoDTO } from '@core/automation/application/dto/CheckoutInfoDTO'; import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
import { CheckoutPrice } from '@core/automation/domain/value-objects/CheckoutPrice'; import { CheckoutState } from 'apps/companion/main/automation/domain/value-objects/CheckoutState';
import { CheckoutConfirmation } from 'apps/companion/main/automation/domain/value-objects/CheckoutConfirmation';
import { CheckoutConfirmation } from '@core/automation/domain/value-objects/CheckoutConfirmation'; import type { Logger } from '@core/shared/application';
/** /**
* ConfirmCheckoutUseCase - GREEN PHASE * ConfirmCheckoutUseCase - GREEN PHASE
@@ -23,6 +23,7 @@ describe('ConfirmCheckoutUseCase', () => {
let mockConfirmationPort: { let mockConfirmationPort: {
requestCheckoutConfirmation: Mock; requestCheckoutConfirmation: Mock;
}; };
let mockLogger: Logger;
let mockPrice: CheckoutPrice; let mockPrice: CheckoutPrice;
beforeEach(() => { beforeEach(() => {
@@ -30,11 +31,21 @@ describe('ConfirmCheckoutUseCase', () => {
extractCheckoutInfo: vi.fn(), extractCheckoutInfo: vi.fn(),
proceedWithCheckout: vi.fn(), proceedWithCheckout: vi.fn(),
}; };
mockConfirmationPort = { mockConfirmationPort = {
requestCheckoutConfirmation: vi.fn(), requestCheckoutConfirmation: vi.fn(),
}; };
mockLogger = {
debug: vi.fn(),
info: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
fatal: vi.fn(),
child: vi.fn(() => mockLogger),
flush: vi.fn(),
} as Logger;
mockPrice = { mockPrice = {
getAmount: vi.fn(() => 0.50), getAmount: vi.fn(() => 0.50),
toDisplayString: vi.fn(() => '$0.50'), toDisplayString: vi.fn(() => '$0.50'),
@@ -46,7 +57,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should extract price, get user confirmation, and proceed with checkout', async () => { it('should extract price, get user confirmation, and proceed with checkout', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -75,7 +87,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should include price in confirmation message', async () => { it('should include price in confirmation message', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -102,7 +115,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should abort checkout when user cancels confirmation', async () => { it('should abort checkout when user cancels confirmation', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -126,7 +140,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should not proceed with checkout after cancellation', async () => { it('should not proceed with checkout after cancellation', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -150,7 +165,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should return error when checkout state is INSUFFICIENT_FUNDS', async () => { it('should return error when checkout state is INSUFFICIENT_FUNDS', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -172,7 +188,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should not ask for confirmation when funds are insufficient', async () => { it('should not ask for confirmation when funds are insufficient', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -193,7 +210,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should return error when price cannot be extracted', async () => { it('should return error when price cannot be extracted', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -215,7 +233,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should return error when extraction service fails', async () => { it('should return error when extraction service fails', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -239,7 +258,8 @@ describe('ConfirmCheckoutUseCase', () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -272,7 +292,8 @@ describe('ConfirmCheckoutUseCase', () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -297,7 +318,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('should return error when proceedWithCheckout fails', async () => { it('should return error when proceedWithCheckout fails', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -325,7 +347,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('Given checkout price $0.50 and READY state, When user confirms, Then checkout proceeds', async () => { it('Given checkout price $0.50 and READY state, When user confirms, Then checkout proceeds', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -348,7 +371,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('Given checkout price $0.50, When user cancels, Then checkout is aborted', async () => { it('Given checkout price $0.50, When user cancels, Then checkout is aborted', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -371,7 +395,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('Given INSUFFICIENT_FUNDS state, When executing, Then error is returned', async () => { it('Given INSUFFICIENT_FUNDS state, When executing, Then error is returned', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(
@@ -390,7 +415,8 @@ describe('ConfirmCheckoutUseCase', () => {
it('Given price extraction failure, When executing, Then error is returned', async () => { it('Given price extraction failure, When executing, Then error is returned', async () => {
const useCase = new ConfirmCheckoutUseCase( const useCase = new ConfirmCheckoutUseCase(
mockCheckoutService as unknown as CheckoutServicePort, mockCheckoutService as unknown as CheckoutServicePort,
mockConfirmationPort as unknown as CheckoutConfirmationPort mockConfirmationPort as unknown as CheckoutConfirmationPort,
mockLogger
); );
mockCheckoutService.extractCheckoutInfo.mockResolvedValue( mockCheckoutService.extractCheckoutInfo.mockResolvedValue(

View File

@@ -1,7 +1,8 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { Logger } from '@core/shared/application'; import type { Logger } from '@core/shared/application';
import type { CheckoutServicePort } from '../ports/CheckoutServicePort'; import type { CheckoutServicePort } from '../ports/CheckoutServicePort';
import type { CheckoutConfirmationPort } from '../ports/CheckoutConfirmationPort'; import type { CheckoutConfirmationPort } from '../ports/CheckoutConfirmationPort';
import { CheckoutStateEnum } from '../../domain/value-objects/CheckoutState';
interface SessionMetadata { interface SessionMetadata {
@@ -25,7 +26,7 @@ export class ConfirmCheckoutUseCase {
const infoResult = await this.checkoutService.extractCheckoutInfo(); const infoResult = await this.checkoutService.extractCheckoutInfo();
if (infoResult.isErr()) { if (infoResult.isErr()) {
this.logger.error('Failed to extract checkout info', { error: infoResult.unwrapErr() }); this.logger.error('Failed to extract checkout info', infoResult.unwrapErr());
return Result.err(infoResult.unwrapErr()); return Result.err(infoResult.unwrapErr());
} }
@@ -58,7 +59,7 @@ export class ConfirmCheckoutUseCase {
}); });
if (confirmationResult.isErr()) { if (confirmationResult.isErr()) {
this.logger.error('Checkout confirmation failed', { error: confirmationResult.unwrapErr() }); this.logger.error('Checkout confirmation failed', confirmationResult.unwrapErr());
return Result.err(confirmationResult.unwrapErr()); return Result.err(confirmationResult.unwrapErr());
} }
@@ -81,7 +82,7 @@ export class ConfirmCheckoutUseCase {
if (checkoutResult.isOk()) { if (checkoutResult.isOk()) {
this.logger.info('Checkout process completed successfully.'); this.logger.info('Checkout process completed successfully.');
} else { } else {
this.logger.error('Checkout process failed', { error: checkoutResult.unwrapErr() }); this.logger.error('Checkout process failed', checkoutResult.unwrapErr());
} }
return checkoutResult; return checkoutResult;
} }

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort';
import type { Logger } from '@core/shared/application/Logger'; import type { Logger } from '@core/shared/application/Logger';
@@ -32,8 +32,9 @@ export class InitiateLoginUseCase {
} }
return result; return result;
} catch (error) { } catch (error) {
this.logger.error('Error initiating login flow.', error); const err = error instanceof Error ? error : new Error(String(error));
return Result.fail(error.message || 'Unknown error during login initiation.'); this.logger.error('Error initiating login flow.', err);
return Result.err(err);
} }
} }
} }

View File

@@ -1,9 +1,10 @@
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { StartAutomationSessionUseCase } from '@core/automation/application/use-cases/StartAutomationSessionUseCase'; import { StartAutomationSessionUseCase } from 'apps/companion/main/automation/application/use-cases/StartAutomationSessionUseCase';
import { AutomationEnginePort as IAutomationEngine } from '@core/automation/application/ports/AutomationEnginePort'; import { AutomationEnginePort as IAutomationEngine } from 'apps/companion/main/automation/application/ports/AutomationEnginePort';
import { IBrowserAutomation as IScreenAutomation } from '@core/automation/application/ports/ScreenAutomationPort'; import { IBrowserAutomation as IScreenAutomation } from 'apps/companion/main/automation/application/ports/ScreenAutomationPort';
import { SessionRepositoryPort as ISessionRepository } from '@core/automation/application/ports/SessionRepositoryPort'; import { SessionRepositoryPort as ISessionRepository } from 'apps/companion/main/automation/application/ports/SessionRepositoryPort';
import { AutomationSession } from '@core/automation/domain/entities/AutomationSession'; import type { Logger } from '@core/shared/application';
import { AutomationSession } from 'apps/companion/main/automation/domain/entities/AutomationSession';
describe('StartAutomationSessionUseCase', () => { describe('StartAutomationSessionUseCase', () => {
let mockAutomationEngine: { let mockAutomationEngine: {
@@ -23,6 +24,12 @@ describe('StartAutomationSessionUseCase', () => {
update: Mock; update: Mock;
delete: Mock; delete: Mock;
}; };
let mockLogger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
let useCase: StartAutomationSessionUseCase; let useCase: StartAutomationSessionUseCase;
beforeEach(() => { beforeEach(() => {
@@ -46,10 +53,18 @@ describe('StartAutomationSessionUseCase', () => {
delete: vi.fn(), delete: vi.fn(),
}; };
mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
useCase = new StartAutomationSessionUseCase( useCase = new StartAutomationSessionUseCase(
mockAutomationEngine as unknown as IAutomationEngine, mockAutomationEngine as unknown as IAutomationEngine,
mockBrowserAutomation as unknown as IScreenAutomation, mockBrowserAutomation as unknown as IScreenAutomation,
mockSessionRepository as unknown as ISessionRepository mockSessionRepository as unknown as ISessionRepository,
mockLogger as unknown as Logger
); );
}); });

View File

@@ -25,7 +25,6 @@ export class StartAutomationSessionUseCase
const validationResult = await this.automationEngine.validateConfiguration(config); const validationResult = await this.automationEngine.validateConfiguration(config);
if (!validationResult.isValid) { if (!validationResult.isValid) {
this.logger.warn('Automation session configuration validation failed', { config, error: validationResult.error }); this.logger.warn('Automation session configuration validation failed', { config, error: validationResult.error });
this.logger.error('Automation session configuration validation failed', { config, error: validationResult.error });
throw new Error(validationResult.error); throw new Error(validationResult.error);
} }
this.logger.debug('Automation session configuration validated successfully.'); this.logger.debug('Automation session configuration validated successfully.');

View File

@@ -1,9 +1,9 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest';
import { VerifyAuthenticatedPageUseCase } from '@core/automation/application/use-cases/VerifyAuthenticatedPageUseCase'; import { VerifyAuthenticatedPageUseCase } from 'apps/companion/main/automation/application/use-cases/VerifyAuthenticatedPageUseCase';
import { AuthenticationServicePort as IAuthenticationService } from '@core/automation/application/ports/AuthenticationServicePort'; import { AuthenticationServicePort as IAuthenticationService } from 'apps/companion/main/automation/application/ports/AuthenticationServicePort';
import { Result } from '@core/shared/result/Result'; import { Result } from '@core/shared/result/Result';
import { BrowserAuthenticationState } from '@core/automation/domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from 'apps/companion/main/automation/domain/value-objects/BrowserAuthenticationState';
import { AuthenticationState } from '@core/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from 'apps/companion/main/automation/domain/value-objects/AuthenticationState';
describe('VerifyAuthenticatedPageUseCase', () => { describe('VerifyAuthenticatedPageUseCase', () => {
let useCase: VerifyAuthenticatedPageUseCase; let useCase: VerifyAuthenticatedPageUseCase;

View File

@@ -1,5 +1,5 @@
import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from '../ports/AuthenticationServicePort';
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { BrowserAuthenticationState } from '../../domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from '../../domain/value-objects/BrowserAuthenticationState';
import type { Logger } from '@core/shared/application'; import type { Logger } from '@core/shared/application';
@@ -29,7 +29,7 @@ export class VerifyAuthenticatedPageUseCase {
return Result.ok<BrowserAuthenticationState>(browserState); return Result.ok<BrowserAuthenticationState>(browserState);
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
this.logger.error(`Page verification failed unexpectedly: ${message}`, error); this.logger.error(`Page verification failed unexpectedly: ${message}`, error instanceof Error ? error : undefined);
return Result.err<BrowserAuthenticationState>(new Error(`Page verification failed: ${message}`)); return Result.err<BrowserAuthenticationState>(new Error(`Page verification failed: ${message}`));
} }
} }

View File

@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { AutomationSession } from '@core/automation/domain/entities/AutomationSession'; import { AutomationSession } from 'apps/companion/main/automation/domain/entities/AutomationSession';
import { StepId } from '@core/automation/domain/value-objects/StepId'; import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId';
describe('AutomationSession Entity', () => { describe('AutomationSession Entity', () => {
describe('create', () => { describe('create', () => {

View File

@@ -4,6 +4,7 @@ import { StepId } from '../value-objects/StepId';
import type { HostedSessionConfig } from '../types/HostedSessionConfig'; import type { HostedSessionConfig } from '../types/HostedSessionConfig';
import { AutomationDomainError } from '../errors/AutomationDomainError'; import { AutomationDomainError } from '../errors/AutomationDomainError';
import { SessionState } from '../value-objects/SessionState';
export class AutomationSession implements IEntity<string> { export class AutomationSession implements IEntity<string> {
private readonly _id: string; private readonly _id: string;

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { PageStateValidator } from '@core/automation/domain/services/PageStateValidator'; import { PageStateValidator } from 'apps/companion/main/automation/domain/services/PageStateValidator';
describe('PageStateValidator', () => { describe('PageStateValidator', () => {
const validator = new PageStateValidator(); const validator = new PageStateValidator();
@@ -7,7 +7,7 @@ describe('PageStateValidator', () => {
describe('validateState', () => { describe('validateState', () => {
it('should return valid when all required selectors are present', () => { it('should return valid when all required selectors are present', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = (selector: string) => {
return ['#add-car-button', '#cars-list'].includes(selector); return ['#add-car-button', '#cars-list'].includes(selector);
}; };
@@ -27,7 +27,7 @@ describe('PageStateValidator', () => {
it('should return invalid when required selectors are missing', () => { it('should return invalid when required selectors are missing', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = (selector: string) => {
return selector === '#add-car-button'; // Only one of two selectors present return selector === '#add-car-button'; // Only one of two selectors present
}; };
@@ -48,7 +48,7 @@ describe('PageStateValidator', () => {
it('should return invalid when forbidden selectors are present', () => { it('should return invalid when forbidden selectors are present', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = (selector: string) => {
return ['#add-car-button', '#set-track'].includes(selector); return ['#add-car-button', '#set-track'].includes(selector);
}; };
@@ -70,7 +70,7 @@ describe('PageStateValidator', () => {
it('should handle empty forbidden selectors array', () => { it('should handle empty forbidden selectors array', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = (selector: string) => {
return selector === '#add-car-button'; return selector === '#add-car-button';
}; };
@@ -89,7 +89,7 @@ describe('PageStateValidator', () => {
it('should handle undefined forbidden selectors', () => { it('should handle undefined forbidden selectors', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = (selector: string) => {
return selector === '#add-car-button'; return selector === '#add-car-button';
}; };
@@ -108,7 +108,7 @@ describe('PageStateValidator', () => {
it('should return error result when actualState function throws', () => { it('should return error result when actualState function throws', () => {
// Arrange // Arrange
const actualState = (_selector: string) => { const actualState = () => {
throw new Error('Selector evaluation failed'); throw new Error('Selector evaluation failed');
}; };
@@ -144,7 +144,7 @@ describe('PageStateValidator', () => {
it('should validate complex state with both required and forbidden selectors', () => { it('should validate complex state with both required and forbidden selectors', () => {
// Arrange - Simulate being on Cars page but Track page elements leaked through // Arrange - Simulate being on Cars page but Track page elements leaked through
const actualState = (_selector: string) => { const actualState = (selector: string) => {
const presentSelectors = ['#add-car-button', '#cars-list', '#set-track']; const presentSelectors = ['#add-car-button', '#cars-list', '#set-track'];
return presentSelectors.includes(selector); return presentSelectors.includes(selector);
}; };

View File

@@ -1,5 +1,5 @@
import type { IDomainValidationService } from '@core/shared/domain'; import type { IDomainValidationService } from '@core/shared/domain';
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
/** /**
* Configuration for page state validation. * Configuration for page state validation.
@@ -183,7 +183,7 @@ export class PageStateValidator
} }
// Check required selectors are present (with fallbacks for real mode) // Check required selectors are present (with fallbacks for real mode)
const missingSelectors = requiredSelectors.filter(_selector => { const missingSelectors = requiredSelectors.filter(selector => {
if (realMode) { if (realMode) {
const relatedSelectors = selectorsToCheck.filter(s => const relatedSelectors = selectorsToCheck.filter(s =>
s.includes(expectedStep) || s.includes(expectedStep) ||

View File

@@ -1,7 +1,8 @@
import { StepId } from '../value-objects/StepId'; import { StepId } from '../value-objects/StepId';
import type { IDomainValidationService } from '@core/shared/domain'; import type { IDomainValidationService } from '@core/shared/domain';
import { Result } from '../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { SessionState } from '../value-objects/SessionState';
export interface ValidationResult { export interface ValidationResult {
isValid: boolean; isValid: boolean;
@@ -90,13 +91,11 @@ export class StepTransitionValidator
} }
static validateModalStepTransition( static validateModalStepTransition(
currentStep: StepId,
nextStep: StepId
): ValidationResult { ): ValidationResult {
return { isValid: true }; return { isValid: true };
} }
static shouldStopAtStep18(_nextStep: StepId): boolean { static shouldStopAtStep18(nextStep: StepId): boolean {
return nextStep.isFinalStep(); return nextStep.isFinalStep();
} }

View File

@@ -1,6 +1,6 @@
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { BrowserAuthenticationState } from '@core/automation/domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from 'apps/companion/main/automation/domain/value-objects/BrowserAuthenticationState';
import { AuthenticationState } from '@core/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from 'apps/companion/main/automation/domain/value-objects/AuthenticationState';
describe('BrowserAuthenticationState', () => { describe('BrowserAuthenticationState', () => {
describe('isFullyAuthenticated()', () => { describe('isFullyAuthenticated()', () => {

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { CheckoutConfirmation } from '@core/automation/domain/value-objects/CheckoutConfirmation'; import { CheckoutConfirmation } from 'apps/companion/main/automation/domain/value-objects/CheckoutConfirmation';
describe('CheckoutConfirmation Value Object', () => { describe('CheckoutConfirmation Value Object', () => {
describe('create', () => { describe('create', () => {
@@ -19,7 +19,8 @@ describe('CheckoutConfirmation Value Object', () => {
}); });
it('should throw error for invalid decision', () => { it('should throw error for invalid decision', () => {
expect(() => CheckoutConfirmation.create('invalid' as unknown)).toThrow( // @ts-expect-error Testing invalid input
expect(() => CheckoutConfirmation.create('invalid')).toThrow(
'Invalid checkout confirmation decision', 'Invalid checkout confirmation decision',
); );
}); });

View File

@@ -24,7 +24,7 @@ export class CheckoutConfirmation {
return CheckoutConfirmation.create('confirmed'); return CheckoutConfirmation.create('confirmed');
} }
static cancelled(__reason?: string): CheckoutConfirmation { static cancelled(): CheckoutConfirmation {
return CheckoutConfirmation.create('cancelled'); return CheckoutConfirmation.create('cancelled');
} }

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { CheckoutPrice } from '@core/automation/domain/value-objects/CheckoutPrice'; import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
/** /**
* CheckoutPrice Value Object - GREEN PHASE * CheckoutPrice Value Object - GREEN PHASE

View File

@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { CheckoutState, CheckoutStateEnum } from './CheckoutState';
/** /**

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { CookieConfiguration } from '@core/automation/domain/value-objects/CookieConfiguration'; import { CookieConfiguration } from 'apps/companion/main/automation/domain/value-objects/CookieConfiguration';
describe('CookieConfiguration', () => { describe('CookieConfiguration', () => {
const validTargetUrl = 'https://members-ng.iracing.com/jjwtauth/success'; const validTargetUrl = 'https://members-ng.iracing.com/jjwtauth/success';

View File

@@ -16,7 +16,7 @@ export class CookieConfiguration {
this.cookie = cookie; this.cookie = cookie;
try { try {
this.targetUrl = new URL(targetUrl); this.targetUrl = new URL(targetUrl);
} catch (error) { } catch {
throw new Error(`Invalid target URL: ${targetUrl}`); throw new Error(`Invalid target URL: ${targetUrl}`);
} }

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { RaceCreationResult } from '@core/automation/domain/value-objects/RaceCreationResult'; import { RaceCreationResult } from 'apps/companion/main/automation/domain/value-objects/RaceCreationResult';
describe('RaceCreationResult Value Object', () => { describe('RaceCreationResult Value Object', () => {
describe('create', () => { describe('create', () => {

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { SessionLifetime } from '@core/automation/domain/value-objects/SessionLifetime'; import { SessionLifetime } from 'apps/companion/main/automation/domain/value-objects/SessionLifetime';
describe('SessionLifetime Value Object', () => { describe('SessionLifetime Value Object', () => {
describe('Construction', () => { describe('Construction', () => {

View File

@@ -1,4 +1,6 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { SessionState } from './SessionState';
import type { SessionStateValue } from './SessionState';
describe('SessionState Value Object', () => { describe('SessionState Value Object', () => {
@@ -44,11 +46,11 @@ describe('SessionState Value Object', () => {
}); });
it('should throw error for invalid state', () => { it('should throw error for invalid state', () => {
expect(() => SessionState.create('INVALID' as unknown)).toThrow('Invalid session state'); expect(() => SessionState.create('INVALID' as SessionStateValue)).toThrow('Invalid session state');
}); });
it('should throw error for empty string', () => { it('should throw error for empty string', () => {
expect(() => SessionState.create('' as unknown)).toThrow('Invalid session state'); expect(() => SessionState.create('' as SessionStateValue)).toThrow('Invalid session state');
}); });
}); });

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { StepId } from '@core/automation/domain/value-objects/StepId'; import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId';
describe('StepId Value Object', () => { describe('StepId Value Object', () => {
describe('create', () => { describe('create', () => {

View File

@@ -10,8 +10,8 @@ vi.mock('electron', () => ({
})); }));
import { ElectronCheckoutConfirmationAdapter } from '@core/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter'; import { ElectronCheckoutConfirmationAdapter } from '@core/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
import { CheckoutPrice } from '@core/automation/domain/value-objects/CheckoutPrice'; import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from '@core/automation/domain/value-objects/CheckoutState'; import { CheckoutState } from 'apps/companion/main/automation/domain/value-objects/CheckoutState';
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
describe('ElectronCheckoutConfirmationAdapter', () => { describe('ElectronCheckoutConfirmationAdapter', () => {

View File

@@ -1,4 +1,4 @@
import { AutomationEvent } from '@core/automation/application/ports/AutomationEventPublisherPort'; import { AutomationEvent } from 'apps/companion/main/automation/application/ports/AutomationEventPublisherPort';
export type LifecycleCallback = (event: AutomationEvent) => Promise<void> | void; export type LifecycleCallback = (event: AutomationEvent) => Promise<void> | void;

View File

@@ -1,4 +1,4 @@
import { Result } from '../../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { CheckoutPrice } from '../../../domain/value-objects/CheckoutPrice'; import { CheckoutPrice } from '../../../domain/value-objects/CheckoutPrice';
import { CheckoutState } from '../../../domain/value-objects/CheckoutState'; import { CheckoutState } from '../../../domain/value-objects/CheckoutState';
import type { CheckoutInfoDTO } from '../../../application/dto/CheckoutInfoDTO'; import type { CheckoutInfoDTO } from '../../../application/dto/CheckoutInfoDTO';

View File

@@ -1,6 +1,6 @@
import { describe, test, expect, beforeEach, vi } from 'vitest'; import { describe, test, expect, beforeEach, vi } from 'vitest';
import type { Page } from 'playwright'; import type { Page } from 'playwright';
import { AuthenticationGuard } from '@core/automation/infrastructure//automation/auth/AuthenticationGuard'; import { AuthenticationGuard } from './AuthenticationGuard';
describe('AuthenticationGuard', () => { describe('AuthenticationGuard', () => {
let mockPage: Page; let mockPage: Page;

View File

@@ -1,5 +1,5 @@
import { Page } from 'playwright'; import { Page } from 'playwright';
import { LoggerPort } from '@core/automation/application/ports/LoggerPort'; import { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
export class AuthenticationGuard { export class AuthenticationGuard {
constructor( constructor(

View File

@@ -1,5 +1,5 @@
import type { Page } from 'playwright'; import type { Page } from 'playwright';
import type { LoggerPort } from '@core/automation/application/ports/LoggerPort'; import type { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { IPlaywrightAuthFlow } from './PlaywrightAuthFlow'; import type { IPlaywrightAuthFlow } from './PlaywrightAuthFlow';
import { IRACING_URLS, IRACING_SELECTORS, IRACING_TIMEOUTS } from '../dom/IRacingSelectors'; import { IRACING_URLS, IRACING_SELECTORS, IRACING_TIMEOUTS } from '../dom/IRacingSelectors';
import { AuthenticationGuard } from './AuthenticationGuard'; import { AuthenticationGuard } from './AuthenticationGuard';

View File

@@ -1,12 +1,11 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { Page, BrowserContext } from 'playwright'; import type { Page, BrowserContext } from 'playwright';
import { PlaywrightAuthSessionService } from '@core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService'; import type { LoggerPort as Logger } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { PlaywrightBrowserSession } from '@core/automation/infrastructure//automation/core/PlaywrightBrowserSession'; import { AuthenticationState } from 'apps/companion/main/automation/domain/value-objects/AuthenticationState';
import type { SessionCookieStore } from '@core/automation/infrastructure//automation/auth/SessionCookieStore'; import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession';
import type { IPlaywrightAuthFlow } from '@core/automation/infrastructure//automation/auth/PlaywrightAuthFlow'; import { IPlaywrightAuthFlow } from './PlaywrightAuthFlow';
import type { LoggerPort as Logger } from '@core/automation/application/ports/LoggerPort'; import { PlaywrightAuthSessionService } from './PlaywrightAuthSessionService';
import { AuthenticationState } from '@core/automation/domain/value-objects/AuthenticationState'; import { SessionCookieStore } from './SessionCookieStore';
import { Result } from '@core/shared/result/Result';
describe('PlaywrightAuthSessionService.initiateLogin browser mode behaviour', () => { describe('PlaywrightAuthSessionService.initiateLogin browser mode behaviour', () => {
const originalEnv = { ...process.env }; const originalEnv = { ...process.env };

View File

@@ -5,7 +5,7 @@ import type { AuthenticationServicePort } from '../../../../application/ports/Au
import type { LoggerPort } from '../../../../application/ports/LoggerPort'; import type { LoggerPort } from '../../../../application/ports/LoggerPort';
import { AuthenticationState } from '../../../../domain/value-objects/AuthenticationState'; import { AuthenticationState } from '../../../../domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '../../../../domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from '../../../../domain/value-objects/BrowserAuthenticationState';
import { Result } from '../../../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession'; import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession';
import { SessionCookieStore } from './SessionCookieStore'; import { SessionCookieStore } from './SessionCookieStore';
import type { IPlaywrightAuthFlow } from './PlaywrightAuthFlow'; import type { IPlaywrightAuthFlow } from './PlaywrightAuthFlow';

View File

@@ -1,13 +1,13 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import type { Page, Locator } from 'playwright'; import type { Page, Locator } from 'playwright';
import { PlaywrightAuthSessionService } from '@core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService'; import { AuthenticationState } from 'apps/companion/main/automation/domain/value-objects/AuthenticationState';
import { AuthenticationState } from '@core/automation/domain/value-objects/AuthenticationState'; import { BrowserAuthenticationState } from 'apps/companion/main/automation/domain/value-objects/BrowserAuthenticationState';
import { BrowserAuthenticationState } from '@core/automation/domain/value-objects/BrowserAuthenticationState'; import type { LoggerPort as Logger } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { LoggerPort as Logger } from '@core/automation/application/ports/LoggerPort';
import type { Result } from '@core/shared/result/Result'; import type { Result } from '@core/shared/result/Result';
import type { PlaywrightBrowserSession } from '@core/automation/infrastructure//automation/core/PlaywrightBrowserSession'; import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession';
import type { SessionCookieStore } from '@core/automation/infrastructure//automation/auth/SessionCookieStore'; import { IPlaywrightAuthFlow } from './PlaywrightAuthFlow';
import type { IPlaywrightAuthFlow } from '@core/automation/infrastructure//automation/auth/PlaywrightAuthFlow'; import { PlaywrightAuthSessionService } from './PlaywrightAuthSessionService';
import { SessionCookieStore } from './SessionCookieStore';
describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => { describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => {
function createService(deps: { function createService(deps: {

View File

@@ -1,6 +1,6 @@
import { describe, test, expect, beforeEach } from 'vitest'; import { describe, test, expect, beforeEach } from 'vitest';
import { SessionCookieStore } from '@core/automation/infrastructure//automation/auth/SessionCookieStore';
import type { Cookie } from 'playwright'; import type { Cookie } from 'playwright';
import { SessionCookieStore } from './SessionCookieStore';
const logger = console as unknown; const logger = console as unknown;

View File

@@ -2,7 +2,7 @@ import * as fs from 'fs/promises';
import * as path from 'path'; import * as path from 'path';
import { AuthenticationState } from '../../../../domain/value-objects/AuthenticationState'; import { AuthenticationState } from '../../../../domain/value-objects/AuthenticationState';
import { CookieConfiguration } from '../../../../domain/value-objects/CookieConfiguration'; import { CookieConfiguration } from '../../../../domain/value-objects/CookieConfiguration';
import { Result } from '../../../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import type { LoggerPort } from '../../../../application/ports/LoggerPort'; import type { LoggerPort } from '../../../../application/ports/LoggerPort';
interface Cookie { interface Cookie {

View File

@@ -16,14 +16,12 @@ import type { ModalResultDTO } from '../../../../application/dto/ModalResultDTO'
import type { AutomationResultDTO } from '../../../../application/dto/AutomationResultDTO'; import type { AutomationResultDTO } from '../../../../application/dto/AutomationResultDTO';
import type { AuthenticationServicePort } from '../../../../application/ports/AuthenticationServicePort'; import type { AuthenticationServicePort } from '../../../../application/ports/AuthenticationServicePort';
import type { LoggerPort } from '../../../../application/ports/LoggerPort'; import type { LoggerPort } from '../../../../application/ports/LoggerPort';
import { Result } from '../../../../../shared/result/Result'; import { Result } from '@gridpilot/shared/result/Result';
import { IRACING_SELECTORS, IRACING_URLS, IRACING_TIMEOUTS, ALL_BLOCKED_SELECTORS, BLOCKED_KEYWORDS } from '../dom/IRacingSelectors'; import { IRACING_SELECTORS, IRACING_URLS, IRACING_TIMEOUTS, BLOCKED_KEYWORDS } from '../dom/IRacingSelectors';
import { SessionCookieStore } from '../auth/SessionCookieStore'; import { SessionCookieStore } from '../auth/SessionCookieStore';
import { PlaywrightBrowserSession } from './PlaywrightBrowserSession'; import { PlaywrightBrowserSession } from './PlaywrightBrowserSession';
import { BrowserModeConfigLoader, BrowserMode } from '../../../config/BrowserModeConfig'; import { PageStateValidator, PageStateValidation, PageStateValidationResult } from 'apps/companion/main/automation/domain/services/PageStateValidator';
import { PageStateValidator, PageStateValidation, PageStateValidationResult } from '@core/automation/domain/services/PageStateValidator';
import { IRacingDomNavigator } from '../dom/IRacingDomNavigator'; import { IRacingDomNavigator } from '../dom/IRacingDomNavigator';
import { SafeClickService } from '../dom/SafeClickService'; import { SafeClickService } from '../dom/SafeClickService';
import { IRacingDomInteractor } from '../dom/IRacingDomInteractor'; import { IRacingDomInteractor } from '../dom/IRacingDomInteractor';

View File

@@ -4,8 +4,7 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import type { LoggerPort } from '@core/automation/application/ports/LoggerPort'; import type { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import { BrowserModeConfigLoader, BrowserMode } from '../../../config/BrowserModeConfig';
import type { PlaywrightConfig } from './PlaywrightAutomationAdapter'; import type { PlaywrightConfig } from './PlaywrightAutomationAdapter';
import { PlaywrightAutomationAdapter } from './PlaywrightAutomationAdapter'; import { PlaywrightAutomationAdapter } from './PlaywrightAutomationAdapter';

View File

@@ -18,7 +18,7 @@ import type {
PageStateValidation, PageStateValidation,
PageStateValidationResult, PageStateValidationResult,
} from '../../../../domain/services/PageStateValidator'; } from '../../../../domain/services/PageStateValidator';
import type { Result } from '../../../../../shared/result/Result'; import type { Result } from '@gridpilot/shared/result/Result';
interface WizardStepOrchestratorDeps { interface WizardStepOrchestratorDeps {
config: Required<PlaywrightConfig>; config: Required<PlaywrightConfig>;
@@ -167,7 +167,7 @@ export class WizardStepOrchestrator {
await this.navigator.waitForWizardStep(stepName); await this.navigator.waitForWizardStep(stepName);
} }
private async checkWizardDismissed(_currentStep: number): Promise<void> { private async checkWizardDismissed(currentStep: number): Promise<void> {
await this.navigator.checkWizardDismissed(currentStep); await this.navigator.checkWizardDismissed(currentStep);
} }

View File

@@ -1,5 +1,5 @@
import type { Page } from 'playwright'; import type { Page } from 'playwright';
import type { LoggerPort } from '@core/automation/application/ports/LoggerPort'; import type { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import { IRACING_SELECTORS, BLOCKED_KEYWORDS } from './IRacingSelectors'; import { IRACING_SELECTORS, BLOCKED_KEYWORDS } from './IRacingSelectors';
import type { PlaywrightConfig } from '../core/PlaywrightAutomationAdapter'; import type { PlaywrightConfig } from '../core/PlaywrightAutomationAdapter';
import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession'; import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession';

Some files were not shown because too many files have changed in this diff Show More