feat(automation): implement dual-mode browser automation with DevTools adapter - Add explicit result types for all automation operations (NavigationResult, FormFillResult, ClickResult, WaitResult, ModalResult) - Implement BrowserDevToolsAdapter for real browser automation via Chrome DevTools Protocol - Create IRacingSelectorMap with CSS selectors for all 18 workflow steps - Add AutomationConfig for environment-based adapter selection (mock/dev/production) - Update DI container to support mode switching via AUTOMATION_MODE env var - Add 54 new tests (34 integration + 20 unit), total now 212 passing - Add npm scripts: companion:mock, companion:devtools, chrome:debug
This commit is contained in:
212
tests/unit/infrastructure/AutomationConfig.test.ts
Normal file
212
tests/unit/infrastructure/AutomationConfig.test.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { loadAutomationConfig, AutomationMode } from '../../../src/infrastructure/config/AutomationConfig';
|
||||
|
||||
describe('AutomationConfig', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset environment before each test
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original environment
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('loadAutomationConfig', () => {
|
||||
describe('default configuration', () => {
|
||||
it('should return mock mode when AUTOMATION_MODE is not set', () => {
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.mode).toBe('mock');
|
||||
});
|
||||
|
||||
it('should return default devTools configuration', () => {
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.devTools?.debuggingPort).toBe(9222);
|
||||
expect(config.devTools?.browserWSEndpoint).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return default nutJs configuration', () => {
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.nutJs?.windowTitle).toBe('iRacing');
|
||||
expect(config.nutJs?.templatePath).toBe('./resources/templates');
|
||||
expect(config.nutJs?.confidence).toBe(0.9);
|
||||
});
|
||||
|
||||
it('should return default shared settings', () => {
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.defaultTimeout).toBe(30000);
|
||||
expect(config.retryAttempts).toBe(3);
|
||||
expect(config.screenshotOnError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dev mode configuration', () => {
|
||||
it('should return dev mode when AUTOMATION_MODE=dev', () => {
|
||||
process.env.AUTOMATION_MODE = 'dev';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.mode).toBe('dev');
|
||||
});
|
||||
|
||||
it('should parse CHROME_DEBUG_PORT', () => {
|
||||
process.env.CHROME_DEBUG_PORT = '9333';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.devTools?.debuggingPort).toBe(9333);
|
||||
});
|
||||
|
||||
it('should read CHROME_WS_ENDPOINT', () => {
|
||||
process.env.CHROME_WS_ENDPOINT = 'ws://127.0.0.1:9222/devtools/browser/abc123';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.devTools?.browserWSEndpoint).toBe('ws://127.0.0.1:9222/devtools/browser/abc123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('production mode configuration', () => {
|
||||
it('should return production mode when AUTOMATION_MODE=production', () => {
|
||||
process.env.AUTOMATION_MODE = 'production';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.mode).toBe('production');
|
||||
});
|
||||
|
||||
it('should parse IRACING_WINDOW_TITLE', () => {
|
||||
process.env.IRACING_WINDOW_TITLE = 'iRacing Simulator';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.nutJs?.windowTitle).toBe('iRacing Simulator');
|
||||
});
|
||||
|
||||
it('should parse TEMPLATE_PATH', () => {
|
||||
process.env.TEMPLATE_PATH = '/custom/templates';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.nutJs?.templatePath).toBe('/custom/templates');
|
||||
});
|
||||
|
||||
it('should parse OCR_CONFIDENCE', () => {
|
||||
process.env.OCR_CONFIDENCE = '0.85';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.nutJs?.confidence).toBe(0.85);
|
||||
});
|
||||
});
|
||||
|
||||
describe('environment variable parsing', () => {
|
||||
it('should parse AUTOMATION_TIMEOUT', () => {
|
||||
process.env.AUTOMATION_TIMEOUT = '60000';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.defaultTimeout).toBe(60000);
|
||||
});
|
||||
|
||||
it('should parse RETRY_ATTEMPTS', () => {
|
||||
process.env.RETRY_ATTEMPTS = '5';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.retryAttempts).toBe(5);
|
||||
});
|
||||
|
||||
it('should parse SCREENSHOT_ON_ERROR=false', () => {
|
||||
process.env.SCREENSHOT_ON_ERROR = 'false';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.screenshotOnError).toBe(false);
|
||||
});
|
||||
|
||||
it('should parse SCREENSHOT_ON_ERROR=true', () => {
|
||||
process.env.SCREENSHOT_ON_ERROR = 'true';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.screenshotOnError).toBe(true);
|
||||
});
|
||||
|
||||
it('should fallback to defaults for invalid integer values', () => {
|
||||
process.env.CHROME_DEBUG_PORT = 'invalid';
|
||||
process.env.AUTOMATION_TIMEOUT = 'not-a-number';
|
||||
process.env.RETRY_ATTEMPTS = '';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.devTools?.debuggingPort).toBe(9222);
|
||||
expect(config.defaultTimeout).toBe(30000);
|
||||
expect(config.retryAttempts).toBe(3);
|
||||
});
|
||||
|
||||
it('should fallback to defaults for invalid float values', () => {
|
||||
process.env.OCR_CONFIDENCE = 'invalid';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.nutJs?.confidence).toBe(0.9);
|
||||
});
|
||||
|
||||
it('should fallback to mock mode for invalid AUTOMATION_MODE', () => {
|
||||
process.env.AUTOMATION_MODE = 'invalid-mode';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.mode).toBe('mock');
|
||||
});
|
||||
});
|
||||
|
||||
describe('full configuration scenario', () => {
|
||||
it('should load complete dev environment configuration', () => {
|
||||
process.env.AUTOMATION_MODE = 'dev';
|
||||
process.env.CHROME_DEBUG_PORT = '9222';
|
||||
process.env.CHROME_WS_ENDPOINT = 'ws://localhost:9222/devtools/browser/test';
|
||||
process.env.AUTOMATION_TIMEOUT = '45000';
|
||||
process.env.RETRY_ATTEMPTS = '2';
|
||||
process.env.SCREENSHOT_ON_ERROR = 'true';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config).toEqual({
|
||||
mode: 'dev',
|
||||
devTools: {
|
||||
debuggingPort: 9222,
|
||||
browserWSEndpoint: 'ws://localhost:9222/devtools/browser/test',
|
||||
},
|
||||
nutJs: {
|
||||
windowTitle: 'iRacing',
|
||||
templatePath: './resources/templates',
|
||||
confidence: 0.9,
|
||||
},
|
||||
defaultTimeout: 45000,
|
||||
retryAttempts: 2,
|
||||
screenshotOnError: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should load complete mock environment configuration', () => {
|
||||
process.env.AUTOMATION_MODE = 'mock';
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
expect(config.mode).toBe('mock');
|
||||
expect(config.devTools).toBeDefined();
|
||||
expect(config.nutJs).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user