feat(logging): add professional logging with pino and headless E2E tests - Add ILogger port interface in application layer - Implement PinoLogAdapter with Electron-compatible structured logging - Add NoOpLogAdapter for testing - Wire logging into DI container and all adapters - Create 32 E2E tests for automation workflow (headless-only) - Add vitest.e2e.config.ts for E2E test configuration - All tests enforce HEADLESS mode (no headed browser allowed)
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
ModalResult,
|
||||
} from '../../../packages/application/ports/AutomationResults';
|
||||
import { StepId } from '../../../packages/domain/value-objects/StepId';
|
||||
import type { ILogger } from '../../../application/ports/ILogger';
|
||||
import { NoOpLogAdapter } from '../logging/NoOpLogAdapter';
|
||||
|
||||
export interface NutJsConfig {
|
||||
mouseSpeed?: number;
|
||||
@@ -20,27 +22,43 @@ export interface NutJsConfig {
|
||||
export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
private config: Required<NutJsConfig>;
|
||||
private connected: boolean = false;
|
||||
private logger: ILogger;
|
||||
|
||||
constructor(config: NutJsConfig = {}) {
|
||||
constructor(config: NutJsConfig = {}, logger?: ILogger) {
|
||||
this.config = {
|
||||
mouseSpeed: config.mouseSpeed ?? 1000,
|
||||
keyboardDelay: config.keyboardDelay ?? 50,
|
||||
screenResolution: config.screenResolution ?? { width: 1920, height: 1080 },
|
||||
defaultTimeout: config.defaultTimeout ?? 30000,
|
||||
};
|
||||
this.logger = logger ?? new NoOpLogAdapter();
|
||||
|
||||
mouse.config.mouseSpeed = this.config.mouseSpeed;
|
||||
keyboard.config.autoDelayMs = this.config.keyboardDelay;
|
||||
}
|
||||
|
||||
async connect(): Promise<AutomationResult> {
|
||||
const startTime = Date.now();
|
||||
this.logger.info('Initializing nut.js OS-level automation');
|
||||
|
||||
try {
|
||||
await screen.width();
|
||||
await screen.height();
|
||||
const width = await screen.width();
|
||||
const height = await screen.height();
|
||||
this.connected = true;
|
||||
|
||||
const durationMs = Date.now() - startTime;
|
||||
this.logger.info('nut.js automation connected', {
|
||||
durationMs,
|
||||
screenWidth: width,
|
||||
screenHeight: height,
|
||||
mouseSpeed: this.config.mouseSpeed,
|
||||
keyboardDelay: this.config.keyboardDelay
|
||||
});
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return { success: false, error: `Screen access failed: ${error}` };
|
||||
const errorMsg = `Screen access failed: ${error}`;
|
||||
this.logger.error('Failed to initialize nut.js', error instanceof Error ? error : new Error(errorMsg));
|
||||
return { success: false, error: errorMsg };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +154,9 @@ export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
this.logger.info('Disconnecting nut.js automation');
|
||||
this.connected = false;
|
||||
this.logger.debug('nut.js disconnected');
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
@@ -145,10 +165,14 @@ export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
|
||||
async executeStep(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult> {
|
||||
const stepNumber = stepId.value;
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.info('Executing step via OS-level automation', { stepId: stepNumber });
|
||||
|
||||
try {
|
||||
switch (stepNumber) {
|
||||
case 1:
|
||||
this.logger.debug('Skipping login step - user pre-authenticated', { stepId: stepNumber });
|
||||
return {
|
||||
success: true,
|
||||
metadata: {
|
||||
@@ -159,6 +183,7 @@ export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
};
|
||||
|
||||
case 18:
|
||||
this.logger.info('Safety stop at final step', { stepId: stepNumber });
|
||||
return {
|
||||
success: true,
|
||||
metadata: {
|
||||
@@ -168,7 +193,9 @@ export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
default: {
|
||||
const durationMs = Date.now() - startTime;
|
||||
this.logger.info('Step executed successfully', { stepId: stepNumber, durationMs });
|
||||
return {
|
||||
success: true,
|
||||
metadata: {
|
||||
@@ -177,8 +204,14 @@ export class NutJsAutomationAdapter implements IBrowserAutomation {
|
||||
config,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const durationMs = Date.now() - startTime;
|
||||
this.logger.error('Step execution failed', error instanceof Error ? error : new Error(String(error)), {
|
||||
stepId: stepNumber,
|
||||
durationMs
|
||||
});
|
||||
return {
|
||||
success: false,
|
||||
error: String(error),
|
||||
|
||||
Reference in New Issue
Block a user