wip
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
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';
|
||||
@@ -775,6 +774,30 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, IAuthent
|
||||
}
|
||||
|
||||
async executeStep(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult> {
|
||||
const stepNumber = stepId.value;
|
||||
|
||||
if (!this.isRealMode() && this.config.baseUrl) {
|
||||
if (stepNumber >= 2 && stepNumber <= this.totalSteps) {
|
||||
try {
|
||||
const fixture = getFixtureForStep(stepNumber);
|
||||
if (fixture) {
|
||||
const base = this.config.baseUrl.replace(/\/$/, '');
|
||||
const url = `${base}/${fixture}`;
|
||||
this.log('debug', 'Mock mode: navigating to fixture for step', {
|
||||
step: stepNumber,
|
||||
url,
|
||||
});
|
||||
await this.navigator.navigateToPage(url);
|
||||
}
|
||||
} catch (error) {
|
||||
this.log('debug', 'Mock mode fixture navigation failed (non-fatal)', {
|
||||
step: stepNumber,
|
||||
error: String(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.stepOrchestrator.executeStep(stepId, config);
|
||||
}
|
||||
|
||||
@@ -888,36 +911,34 @@ 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; domPath?: string }> {
|
||||
private async saveDebugInfo(stepName: string, error: Error): Promise<{ screenshotPath?: string; htmlPath?: 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; domPath?: string } = {};
|
||||
const result: { screenshotPath?: string; htmlPath?: string } = {};
|
||||
|
||||
try {
|
||||
await fs.promises.mkdir(debugDir, { recursive: true });
|
||||
|
||||
// Save screenshot
|
||||
const screenshotPath = path.join(debugDir, `${baseName}.png`);
|
||||
await this.page.screenshot({ path: screenshotPath, fullPage: true });
|
||||
result.screenshotPath = screenshotPath;
|
||||
this.log('error', `Error debug screenshot saved: ${screenshotPath}`, { path: screenshotPath, error: error.message });
|
||||
this.log('error', `Error debug screenshot saved: ${screenshotPath}`, {
|
||||
path: screenshotPath,
|
||||
error: error.message,
|
||||
});
|
||||
|
||||
// Save HTML (cleaned to remove noise)
|
||||
const htmlPath = path.join(debugDir, `${baseName}.html`);
|
||||
const html = await this.page.evaluate(() => {
|
||||
// Clone the document
|
||||
const root = document.documentElement.cloneNode(true) as HTMLElement;
|
||||
|
||||
// Remove noise elements
|
||||
['script', 'noscript', 'meta', 'base', 'style', 'link', 'iframe',
|
||||
'picture', 'source', 'svg', 'path', 'img', 'canvas', 'video', 'audio']
|
||||
.forEach(sel => root.querySelectorAll(sel).forEach(n => n.remove()));
|
||||
.forEach((sel) => root.querySelectorAll(sel).forEach((n) => n.remove()));
|
||||
|
||||
// Remove empty non-interactive elements
|
||||
root.querySelectorAll('*').forEach(n => {
|
||||
root.querySelectorAll('*').forEach((n) => {
|
||||
const text = (n.textContent || '').trim();
|
||||
const interactive = n.matches('a,button,input,select,textarea,option,label');
|
||||
if (!interactive && text === '' && n.children.length === 0) {
|
||||
@@ -930,22 +951,6 @@ 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) });
|
||||
}
|
||||
@@ -960,39 +965,36 @@ 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; domPath?: string }> {
|
||||
private async saveProactiveDebugInfo(step: number): Promise<{ screenshotPath?: string; htmlPath?: 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; domPath?: string } = {};
|
||||
const result: { screenshotPath?: string; htmlPath?: string } = {};
|
||||
|
||||
try {
|
||||
await fs.promises.mkdir(debugDir, { recursive: true });
|
||||
|
||||
// Clean up old "before" snapshots first
|
||||
await this.cleanupOldBeforeSnapshots(debugDir);
|
||||
|
||||
// Save screenshot
|
||||
const screenshotPath = path.join(debugDir, `${baseName}.png`);
|
||||
await this.page.screenshot({ path: screenshotPath, fullPage: true });
|
||||
result.screenshotPath = screenshotPath;
|
||||
this.log('info', `Pre-step screenshot saved: ${screenshotPath}`, { path: screenshotPath, step });
|
||||
this.log('info', `Pre-step screenshot saved: ${screenshotPath}`, {
|
||||
path: screenshotPath,
|
||||
step,
|
||||
});
|
||||
|
||||
// Save HTML (cleaned to remove noise)
|
||||
const htmlPath = path.join(debugDir, `${baseName}.html`);
|
||||
const html = await this.page.evaluate(() => {
|
||||
// Clone the document
|
||||
const root = document.documentElement.cloneNode(true) as HTMLElement;
|
||||
|
||||
// Remove noise elements
|
||||
['script', 'noscript', 'meta', 'base', 'style', 'link', 'iframe',
|
||||
'picture', 'source', 'svg', 'path', 'img', 'canvas', 'video', 'audio']
|
||||
.forEach(sel => root.querySelectorAll(sel).forEach(n => n.remove()));
|
||||
.forEach((sel) => root.querySelectorAll(sel).forEach((n) => n.remove()));
|
||||
|
||||
// Remove empty non-interactive elements
|
||||
root.querySelectorAll('*').forEach(n => {
|
||||
root.querySelectorAll('*').forEach((n) => {
|
||||
const text = (n.textContent || '').trim();
|
||||
const interactive = n.matches('a,button,input,select,textarea,option,label');
|
||||
if (!interactive && text === '' && n.children.length === 0) {
|
||||
@@ -1005,24 +1007,7 @@ 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 });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user