This commit is contained in:
2025-11-30 17:37:03 +01:00
parent 65f74e124a
commit 4b8c70978f
6 changed files with 283 additions and 20 deletions

View File

@@ -1,6 +1,7 @@
import type { Browser, Page, BrowserContext } from 'playwright';
import * as fs from 'fs';
import * as path from 'path';
import { extractDom, ExportedElement } from '../../../../scripts/dom-export/domExtractor';
import { StepId } from '../../../../domain/value-objects/StepId';
import { AuthenticationState } from '../../../../domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '../../../../domain/value-objects/BrowserAuthenticationState';
@@ -22,6 +23,7 @@ import { Result } from '../../../../shared/result/Result';
import { IRACING_SELECTORS, IRACING_URLS, IRACING_TIMEOUTS, ALL_BLOCKED_SELECTORS, BLOCKED_KEYWORDS } from '../dom/IRacingSelectors';
import { SessionCookieStore } from '../auth/SessionCookieStore';
import { PlaywrightBrowserSession } from './PlaywrightBrowserSession';
import { getFixtureForStep } from '../engine/FixtureServer';
import { BrowserModeConfigLoader, BrowserMode } from '../../../config/BrowserModeConfig';
import { getAutomationMode } from '../../../config/AutomationConfig';
import { PageStateValidator, PageStateValidation, PageStateValidationResult } from '../../../../domain/services/PageStateValidator';
@@ -645,6 +647,21 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
}
this.syncSessionStateFromBrowser();
if (this.config.mode === 'mock' && this.config.baseUrl) {
try {
const fixture = getFixtureForStep(1);
if (fixture) {
const base = this.config.baseUrl.replace(/\/$/, '');
await this.navigator.navigateToPage(`${base}/${fixture}`);
}
} catch (error) {
this.log('debug', 'Initial fixture navigation failed (mock mode)', {
error: String(error),
});
}
}
return { success: true };
}
@@ -871,13 +888,13 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
*
* Error dumps are always kept and not subject to cleanup.
*/
private async saveDebugInfo(stepName: string, error: Error): Promise<{ screenshotPath?: string; htmlPath?: string }> {
private async saveDebugInfo(stepName: string, error: Error): Promise<{ screenshotPath?: string; htmlPath?: string; domPath?: string }> {
if (!this.page) return {};
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const baseName = `debug-error-${stepName}-${timestamp}`;
const debugDir = path.join(process.cwd(), 'debug-screenshots');
const result: { screenshotPath?: string; htmlPath?: string } = {};
const result: { screenshotPath?: string; htmlPath?: string; domPath?: string } = {};
try {
await fs.promises.mkdir(debugDir, { recursive: true });
@@ -913,6 +930,22 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
await fs.promises.writeFile(htmlPath, html);
result.htmlPath = htmlPath;
this.log('error', `Error debug HTML saved: ${htmlPath}`, { path: htmlPath });
// Save structural DOM export alongside HTML
try {
await this.page.evaluate(() => {
(window as any).__name = (window as any).__name || ((fn: any) => fn);
});
const items = (await this.page.evaluate(
extractDom as () => ExportedElement[]
)) as unknown as ExportedElement[];
const domPath = path.join(debugDir, `${baseName}.dom.json`);
await fs.promises.writeFile(domPath, JSON.stringify(items, null, 2), 'utf8');
result.domPath = domPath;
this.log('error', `Error debug DOM saved: ${domPath}`, { path: domPath });
} catch (domErr) {
this.log('warn', 'Failed to save error debug DOM', { error: String(domErr) });
}
} catch (e) {
this.log('warn', 'Failed to save error debug info', { error: String(e) });
}
@@ -927,13 +960,13 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
* Files are named with "before-step-N" prefix and old snapshots are cleaned up
* to avoid disk bloat (keeps only last MAX_BEFORE_SNAPSHOTS).
*/
private async saveProactiveDebugInfo(step: number): Promise<{ screenshotPath?: string; htmlPath?: string }> {
private async saveProactiveDebugInfo(step: number): Promise<{ screenshotPath?: string; htmlPath?: string; domPath?: string }> {
if (!this.page) return {};
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const baseName = `debug-before-step-${step}-${timestamp}`;
const debugDir = path.join(process.cwd(), 'debug-screenshots');
const result: { screenshotPath?: string; htmlPath?: string } = {};
const result: { screenshotPath?: string; htmlPath?: string; domPath?: string } = {};
try {
await fs.promises.mkdir(debugDir, { recursive: true });
@@ -972,6 +1005,22 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
await fs.promises.writeFile(htmlPath, html);
result.htmlPath = htmlPath;
this.log('info', `Pre-step HTML saved: ${htmlPath}`, { path: htmlPath, step });
// Save structural DOM export alongside HTML
try {
await this.page.evaluate(() => {
(window as any).__name = (window as any).__name || ((fn: any) => fn);
});
const items = (await this.page.evaluate(
extractDom as () => ExportedElement[]
)) as unknown as ExportedElement[];
const domPath = path.join(debugDir, `${baseName}.dom.json`);
await fs.promises.writeFile(domPath, JSON.stringify(items, null, 2), 'utf8');
result.domPath = domPath;
this.log('info', `Pre-step DOM saved: ${domPath}`, { path: domPath, step });
} catch (domErr) {
this.log('warn', 'Failed to save proactive debug DOM', { error: String(domErr), step });
}
} catch (e) {
// Don't fail step execution if debug save fails
this.log('warn', 'Failed to save proactive debug info', { error: String(e), step });

View File

@@ -15,6 +15,7 @@ import { PlaywrightBrowserSession } from './PlaywrightBrowserSession';
import { IRacingDomNavigator } from '../dom/IRacingDomNavigator';
import { IRacingDomInteractor } from '../dom/IRacingDomInteractor';
import { IRACING_SELECTORS } from '../dom/IRacingSelectors';
import { getFixtureForStep } from '../engine/FixtureServer';
import type {
PageStateValidation,
PageStateValidationResult,
@@ -306,6 +307,14 @@ export class WizardStepOrchestrator {
break;
case 2:
if (!this.isRealMode() && this.config.baseUrl) {
const fixture = getFixtureForStep(2);
if (fixture) {
const base = this.config.baseUrl.replace(/\/$/, '');
await this.navigator.navigateToPage(`${base}/${fixture}`);
break;
}
}
await this.clickAction('create');
break;