testchimp-runner-core 0.0.33 → 0.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/execution-service.d.ts +1 -4
- package/dist/execution-service.d.ts.map +1 -1
- package/dist/execution-service.js +155 -468
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/llm-facade.d.ts.map +1 -1
- package/dist/llm-facade.js +7 -7
- package/dist/llm-facade.js.map +1 -1
- package/dist/llm-provider.d.ts +9 -0
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/model-constants.d.ts +16 -5
- package/dist/model-constants.d.ts.map +1 -1
- package/dist/model-constants.js +17 -6
- package/dist/model-constants.js.map +1 -1
- package/dist/orchestrator/decision-parser.d.ts +18 -0
- package/dist/orchestrator/decision-parser.d.ts.map +1 -0
- package/dist/orchestrator/decision-parser.js +127 -0
- package/dist/orchestrator/decision-parser.js.map +1 -0
- package/dist/orchestrator/index.d.ts +4 -2
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +15 -2
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/orchestrator-agent.d.ts +17 -22
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator-agent.js +708 -577
- package/dist/orchestrator/orchestrator-agent.js.map +1 -1
- package/dist/orchestrator/orchestrator-prompts.d.ts +32 -0
- package/dist/orchestrator/orchestrator-prompts.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-prompts.js +737 -0
- package/dist/orchestrator/orchestrator-prompts.js.map +1 -0
- package/dist/orchestrator/page-som-handler.d.ts +106 -0
- package/dist/orchestrator/page-som-handler.d.ts.map +1 -0
- package/dist/orchestrator/page-som-handler.js +1353 -0
- package/dist/orchestrator/page-som-handler.js.map +1 -0
- package/dist/orchestrator/som-types.d.ts +149 -0
- package/dist/orchestrator/som-types.d.ts.map +1 -0
- package/dist/orchestrator/som-types.js +87 -0
- package/dist/orchestrator/som-types.js.map +1 -0
- package/dist/orchestrator/tool-registry.d.ts +2 -0
- package/dist/orchestrator/tool-registry.d.ts.map +1 -1
- package/dist/orchestrator/tool-registry.js.map +1 -1
- package/dist/orchestrator/tools/index.d.ts +5 -1
- package/dist/orchestrator/tools/index.d.ts.map +1 -1
- package/dist/orchestrator/tools/index.js +9 -2
- package/dist/orchestrator/tools/index.js.map +1 -1
- package/dist/orchestrator/tools/refresh-som-markers.d.ts +12 -0
- package/dist/orchestrator/tools/refresh-som-markers.d.ts.map +1 -0
- package/dist/orchestrator/tools/refresh-som-markers.js +64 -0
- package/dist/orchestrator/tools/refresh-som-markers.js.map +1 -0
- package/dist/orchestrator/tools/verify-action-result.d.ts +17 -0
- package/dist/orchestrator/tools/verify-action-result.d.ts.map +1 -0
- package/dist/orchestrator/tools/verify-action-result.js +140 -0
- package/dist/orchestrator/tools/verify-action-result.js.map +1 -0
- package/dist/orchestrator/tools/view-previous-screenshot.d.ts +15 -0
- package/dist/orchestrator/tools/view-previous-screenshot.d.ts.map +1 -0
- package/dist/orchestrator/tools/view-previous-screenshot.js +92 -0
- package/dist/orchestrator/tools/view-previous-screenshot.js.map +1 -0
- package/dist/orchestrator/types.d.ts +49 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/orchestrator/types.js +11 -1
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +40 -34
- package/dist/prompts.js.map +1 -1
- package/dist/scenario-service.d.ts +5 -0
- package/dist/scenario-service.d.ts.map +1 -1
- package/dist/scenario-service.js +17 -0
- package/dist/scenario-service.js.map +1 -1
- package/dist/scenario-worker-class.d.ts +4 -0
- package/dist/scenario-worker-class.d.ts.map +1 -1
- package/dist/scenario-worker-class.js +21 -3
- package/dist/scenario-worker-class.js.map +1 -1
- package/dist/testing/agent-tester.d.ts +35 -0
- package/dist/testing/agent-tester.d.ts.map +1 -0
- package/dist/testing/agent-tester.js +84 -0
- package/dist/testing/agent-tester.js.map +1 -0
- package/dist/testing/ref-translator-tester.d.ts +44 -0
- package/dist/testing/ref-translator-tester.d.ts.map +1 -0
- package/dist/testing/ref-translator-tester.js +104 -0
- package/dist/testing/ref-translator-tester.js.map +1 -0
- package/dist/utils/coordinate-converter.d.ts +32 -0
- package/dist/utils/coordinate-converter.d.ts.map +1 -0
- package/dist/utils/coordinate-converter.js +130 -0
- package/dist/utils/coordinate-converter.js.map +1 -0
- package/dist/utils/hierarchical-selector.d.ts +47 -0
- package/dist/utils/hierarchical-selector.d.ts.map +1 -0
- package/dist/utils/hierarchical-selector.js +212 -0
- package/dist/utils/hierarchical-selector.js.map +1 -0
- package/dist/utils/page-info-retry.d.ts +14 -0
- package/dist/utils/page-info-retry.d.ts.map +1 -0
- package/dist/utils/page-info-retry.js +60 -0
- package/dist/utils/page-info-retry.js.map +1 -0
- package/dist/utils/page-info-utils.d.ts +1 -0
- package/dist/utils/page-info-utils.d.ts.map +1 -1
- package/dist/utils/page-info-utils.js +46 -18
- package/dist/utils/page-info-utils.js.map +1 -1
- package/dist/utils/ref-attacher.d.ts +21 -0
- package/dist/utils/ref-attacher.d.ts.map +1 -0
- package/dist/utils/ref-attacher.js +149 -0
- package/dist/utils/ref-attacher.js.map +1 -0
- package/dist/utils/ref-translator.d.ts +49 -0
- package/dist/utils/ref-translator.d.ts.map +1 -0
- package/dist/utils/ref-translator.js +276 -0
- package/dist/utils/ref-translator.js.map +1 -0
- package/package.json +1 -1
- package/plandocs/BEFORE_AFTER_VERIFICATION.md +148 -0
- package/plandocs/COORDINATE_MODE_DIAGNOSIS.md +144 -0
- package/plandocs/IMPLEMENTATION_STATUS.md +108 -0
- package/plandocs/PHASE_1_COMPLETE.md +165 -0
- package/plandocs/PHASE_1_SUMMARY.md +184 -0
- package/plandocs/PROMPT_OPTIMIZATION_ANALYSIS.md +120 -0
- package/plandocs/PROMPT_SANITY_CHECK.md +120 -0
- package/plandocs/SESSION_SUMMARY_v0.0.33.md +151 -0
- package/plandocs/TROUBLESHOOTING_SESSION.md +72 -0
- package/plandocs/VISUAL_AGENT_EVOLUTION_PLAN.md +396 -0
- package/plandocs/WHATS_NEW_v0.0.33.md +183 -0
- package/plandocs/exploratory-mode-support-v2.plan.md +953 -0
- package/plandocs/exploratory-mode-support.plan.md +928 -0
- package/plandocs/journey-id-tracking-addendum.md +227 -0
- package/src/execution-service.ts +179 -596
- package/src/index.ts +10 -0
- package/src/llm-facade.ts +8 -8
- package/src/llm-provider.ts +11 -1
- package/src/model-constants.ts +17 -5
- package/src/orchestrator/decision-parser.ts +139 -0
- package/src/orchestrator/index.ts +27 -2
- package/src/orchestrator/orchestrator-agent.ts +868 -623
- package/src/orchestrator/orchestrator-prompts.ts +786 -0
- package/src/orchestrator/page-som-handler.ts +1565 -0
- package/src/orchestrator/som-types.ts +188 -0
- package/src/orchestrator/tool-registry.ts +2 -0
- package/src/orchestrator/tools/index.ts +5 -1
- package/src/orchestrator/tools/refresh-som-markers.ts +69 -0
- package/src/orchestrator/tools/verify-action-result.ts +159 -0
- package/src/orchestrator/tools/view-previous-screenshot.ts +103 -0
- package/src/orchestrator/types.ts +95 -4
- package/src/prompts.ts +40 -34
- package/src/scenario-service.ts +20 -0
- package/src/scenario-worker-class.ts +30 -4
- package/src/utils/coordinate-converter.ts +162 -0
- package/src/utils/page-info-retry.ts +65 -0
- package/src/utils/page-info-utils.ts +53 -18
- package/testchimp-runner-core-0.0.35.tgz +0 -0
- /package/{CREDIT_CALLBACK_ARCHITECTURE.md → plandocs/CREDIT_CALLBACK_ARCHITECTURE.md} +0 -0
- /package/{INTEGRATION_COMPLETE.md → plandocs/INTEGRATION_COMPLETE.md} +0 -0
- /package/{VISION_DIAGNOSTICS_IMPROVEMENTS.md → plandocs/VISION_DIAGNOSTICS_IMPROVEMENTS.md} +0 -0
- /package/{RELEASE_0.0.26.md → releasenotes/RELEASE_0.0.26.md} +0 -0
- /package/{RELEASE_0.0.27.md → releasenotes/RELEASE_0.0.27.md} +0 -0
- /package/{RELEASE_0.0.28.md → releasenotes/RELEASE_0.0.28.md} +0 -0
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ExecutionService = void 0;
|
|
4
4
|
const playwright_mcp_service_1 = require("./playwright-mcp-service");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
|
-
const page_info_utils_1 = require("./utils/page-info-utils");
|
|
7
6
|
const browser_utils_1 = require("./utils/browser-utils");
|
|
8
7
|
const llm_facade_1 = require("./llm-facade");
|
|
9
8
|
const script_utils_1 = require("./script-utils");
|
|
10
9
|
const credit_usage_service_1 = require("./credit-usage-service");
|
|
11
10
|
const model_constants_1 = require("./model-constants");
|
|
12
11
|
const backend_proxy_llm_provider_1 = require("./providers/backend-proxy-llm-provider");
|
|
12
|
+
const orchestrator_1 = require("./orchestrator");
|
|
13
13
|
/**
|
|
14
14
|
* Service for orchestrating Playwright script execution
|
|
15
15
|
*/
|
|
@@ -23,6 +23,13 @@ class ExecutionService {
|
|
|
23
23
|
this.progressReporter = progressReporter;
|
|
24
24
|
this.creditUsageService = new credit_usage_service_1.CreditUsageService(authConfig, backendUrl);
|
|
25
25
|
this.maxConcurrentExecutions = maxConcurrentExecutions;
|
|
26
|
+
// Initialize orchestrator for repair mode (reuses all SoM infrastructure)
|
|
27
|
+
const toolRegistry = new orchestrator_1.ToolRegistry();
|
|
28
|
+
const repairConfig = {
|
|
29
|
+
useSoM: true,
|
|
30
|
+
somRestrictCoordinates: true // Prefer SoM markers for repairs
|
|
31
|
+
};
|
|
32
|
+
this.orchestratorAgent = new orchestrator_1.OrchestratorAgent(this.llmFacade, toolRegistry, repairConfig, progressReporter, (msg, level) => this.log(msg));
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* Set a logger callback for capturing execution logs
|
|
@@ -201,7 +208,7 @@ class ExecutionService {
|
|
|
201
208
|
await this.progressReporter.beforeStartTest(page, browser, context);
|
|
202
209
|
}
|
|
203
210
|
// Execute the script as-is
|
|
204
|
-
await this.
|
|
211
|
+
await this.executeStepCode(request.script, page);
|
|
205
212
|
// LIFECYCLE: Call afterEndTest on success
|
|
206
213
|
if (this.progressReporter?.afterEndTest) {
|
|
207
214
|
await this.progressReporter.afterEndTest('passed', undefined, page);
|
|
@@ -243,7 +250,7 @@ class ExecutionService {
|
|
|
243
250
|
await this.progressReporter.beforeStartTest(page, browser, context);
|
|
244
251
|
}
|
|
245
252
|
// Execute the script as-is
|
|
246
|
-
await this.
|
|
253
|
+
await this.executeStepCode(request.script, page);
|
|
247
254
|
// LIFECYCLE: Call afterEndTest on success
|
|
248
255
|
if (this.progressReporter?.afterEndTest) {
|
|
249
256
|
await this.progressReporter.afterEndTest('passed', undefined, page);
|
|
@@ -315,8 +322,11 @@ class ExecutionService {
|
|
|
315
322
|
}
|
|
316
323
|
// Start AI repair process
|
|
317
324
|
this.log('Starting AI repair process...');
|
|
325
|
+
let repairBrowser = null;
|
|
326
|
+
let repairContext = null;
|
|
327
|
+
let repairPage = null;
|
|
318
328
|
try {
|
|
319
|
-
let
|
|
329
|
+
let steps, updatedSteps;
|
|
320
330
|
if (useExistingBrowser) {
|
|
321
331
|
// Use existing browser
|
|
322
332
|
this.log('Using existing browser for AI repair...');
|
|
@@ -376,8 +386,8 @@ class ExecutionService {
|
|
|
376
386
|
this.log('Starting AI repair with parsed steps...');
|
|
377
387
|
updatedSteps = await this.repairStepsWithAI(steps, repairPage, repairFlexibility, model, request.jobId);
|
|
378
388
|
}
|
|
379
|
-
// Always generate the updated script
|
|
380
|
-
const updatedScript = this.generateUpdatedScript(updatedSteps);
|
|
389
|
+
// Always generate the updated script (preserve original test name)
|
|
390
|
+
const updatedScript = this.generateUpdatedScript(updatedSteps, undefined, request.script);
|
|
381
391
|
// Check if repair was successful by seeing if we completed all steps
|
|
382
392
|
const allStepsSuccessful = updatedSteps.length > 0 && updatedSteps.every(step => step.success);
|
|
383
393
|
// Check if we have any successful repairs (partial success)
|
|
@@ -392,10 +402,29 @@ class ExecutionService {
|
|
|
392
402
|
});
|
|
393
403
|
// Update file if we have any successful repairs (partial or complete)
|
|
394
404
|
if (hasSuccessfulRepairs) {
|
|
405
|
+
// IMPORTANT: Use the orchestrator-generated script directly (already has proper Playwright commands)
|
|
406
|
+
// Don't regenerate via LLM as it loses the actual repairs
|
|
407
|
+
this.log('Using orchestrator-generated script (skipping LLM regeneration to preserve repairs)');
|
|
408
|
+
// For repair advice, compare original vs repaired
|
|
395
409
|
const confidenceResponse = await this.llmFacade.assessRepairConfidence(request.script, updatedScript, model);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
410
|
+
// Add TestChimp comment with repair advice
|
|
411
|
+
const scriptWithAdvice = (0, script_utils_1.addTestChimpComment)(updatedScript, confidenceResponse.advice);
|
|
412
|
+
// Polish the script with minor LLM cleanup (removes redundancies, fixes formatting)
|
|
413
|
+
this.log('Applying final LLM polish to repaired script (minor cleanup only)...');
|
|
414
|
+
const cleanupResult = await this.llmFacade.cleanupScript(scriptWithAdvice, model);
|
|
415
|
+
if (cleanupResult.changes.length > 0) {
|
|
416
|
+
this.log(`Script cleanup made ${cleanupResult.changes.length} minor improvements:`);
|
|
417
|
+
cleanupResult.changes.forEach((change, i) => {
|
|
418
|
+
this.log(` ${i + 1}. ${change}`);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
else if (cleanupResult.skipped) {
|
|
422
|
+
this.log(`Script cleanup skipped: ${cleanupResult.skipped}`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
this.log('Script cleanup: no changes needed');
|
|
426
|
+
}
|
|
427
|
+
const scriptWithRepairAdvice = cleanupResult.script;
|
|
399
428
|
// Report credit usage for successful AI repair
|
|
400
429
|
this.creditUsageService.reportAIRepairCredit().catch(error => {
|
|
401
430
|
this.log(`Failed to report credit usage for AI repair: ${error}`, 'warn');
|
|
@@ -409,10 +438,6 @@ class ExecutionService {
|
|
|
409
438
|
this.log(`afterEndTest callback failed: ${callbackError}`, 'warn');
|
|
410
439
|
}
|
|
411
440
|
}
|
|
412
|
-
// Only close browser if we created it (not provided by caller)
|
|
413
|
-
if (!useExistingBrowser) {
|
|
414
|
-
await repairBrowser.close();
|
|
415
|
-
}
|
|
416
441
|
return {
|
|
417
442
|
runStatus: 'failed', // Original script failed
|
|
418
443
|
repairStatus: allStepsSuccessful ? 'success' : 'partial', // Complete or partial repair success
|
|
@@ -434,10 +459,6 @@ class ExecutionService {
|
|
|
434
459
|
this.log(`afterEndTest callback failed: ${callbackError}`, 'warn');
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
|
-
// Only close browser if we created it (not provided by caller)
|
|
438
|
-
if (!useExistingBrowser) {
|
|
439
|
-
await repairBrowser.close();
|
|
440
|
-
}
|
|
441
462
|
return {
|
|
442
463
|
runStatus: 'failed', // Original script failed
|
|
443
464
|
repairStatus: 'failed',
|
|
@@ -459,6 +480,18 @@ class ExecutionService {
|
|
|
459
480
|
error: error instanceof Error ? error.message : 'Script execution failed'
|
|
460
481
|
};
|
|
461
482
|
}
|
|
483
|
+
finally {
|
|
484
|
+
// Clean up browser resources if we created them (not provided by caller)
|
|
485
|
+
if (!useExistingBrowser && repairBrowser) {
|
|
486
|
+
try {
|
|
487
|
+
await repairBrowser.close();
|
|
488
|
+
this.log('AI repair browser closed');
|
|
489
|
+
}
|
|
490
|
+
catch (closeError) {
|
|
491
|
+
this.log(`Error closing AI repair browser: ${closeError}`, 'warn');
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
462
495
|
}
|
|
463
496
|
async parseScriptIntoSteps(script, model) {
|
|
464
497
|
// First try LLM-based parsing
|
|
@@ -520,6 +553,8 @@ class ExecutionService {
|
|
|
520
553
|
let updatedSteps = [...steps];
|
|
521
554
|
const maxTries = 3;
|
|
522
555
|
const recentRepairs = [];
|
|
556
|
+
// Track actual executed steps (including agent repairs) for proper history
|
|
557
|
+
const executedStepDescriptions = [];
|
|
523
558
|
// Create a shared execution context that accumulates all executed code for variable tracking
|
|
524
559
|
let executionContext = '';
|
|
525
560
|
const contextVariables = new Map();
|
|
@@ -544,6 +579,8 @@ class ExecutionService {
|
|
|
544
579
|
step.success = true;
|
|
545
580
|
this.log(`Step ${i + 1} executed successfully: ${step.description}`);
|
|
546
581
|
this.log(`Step ${i + 1} success status set to: ${step.success}`);
|
|
582
|
+
// Track executed step description for agent context
|
|
583
|
+
executedStepDescriptions.push(step.description);
|
|
547
584
|
// Report successful step execution
|
|
548
585
|
this.log(`DEBUG: About to check callback - progressReporter=${!!this.progressReporter}, onStepProgress=${!!this.progressReporter?.onStepProgress}, jobId=${jobId}`);
|
|
549
586
|
if (this.progressReporter?.onStepProgress && jobId) {
|
|
@@ -575,234 +612,98 @@ class ExecutionService {
|
|
|
575
612
|
}
|
|
576
613
|
step.success = false;
|
|
577
614
|
step.error = this.safeSerializeError(error);
|
|
578
|
-
//
|
|
579
|
-
|
|
615
|
+
// Use orchestrator for repair (reuses all SoM infrastructure)
|
|
616
|
+
this.log(`Calling orchestrator in REPAIR mode for step ${i + 1}`);
|
|
617
|
+
// Prepare repair context - use executedStepDescriptions (includes agent repairs)
|
|
618
|
+
const priorSteps = executedStepDescriptions; // What was ACTUALLY executed (scripted + agent)
|
|
619
|
+
const nextSteps = updatedSteps.slice(i + 1).map(s => s.description);
|
|
620
|
+
this.log(` Prior steps executed: ${priorSteps.length}, Next steps: ${nextSteps.length}`);
|
|
621
|
+
this.log(` Prior steps context:\n ${priorSteps.map((s, idx) => `${idx + 1}. ${s}`).join('\n ')}`);
|
|
622
|
+
// Create minimal memory for repair
|
|
623
|
+
const memory = {
|
|
624
|
+
experiences: [],
|
|
625
|
+
extractedData: {},
|
|
626
|
+
history: [],
|
|
627
|
+
latestNote: undefined
|
|
628
|
+
};
|
|
580
629
|
let repairSuccess = false;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
//
|
|
589
|
-
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
// Two-step supervisor pattern for vision-based repair:
|
|
601
|
-
// 1. Supervisor analyzes screenshot and provides diagnostic insights
|
|
602
|
-
// 2. Get repair suggestion with enhanced context from vision analysis
|
|
603
|
-
this.log(` 📸 Taking screenshot for supervisor analysis...`);
|
|
604
|
-
// Capture optimized screenshot using utility method
|
|
605
|
-
const imageDataUrl = await (0, browser_utils_1.captureOptimizedScreenshot)(page, { timeout: 10000 }, // Uses default quality 60
|
|
606
|
-
(msg) => this.log(msg));
|
|
607
|
-
this.log(` 👔 STEP 1: Supervisor analyzing screenshot (${model_constants_1.VISION_MODEL})...`);
|
|
608
|
-
const supervisorDiagnostics = await this.llmFacade.getVisionDiagnostics(step.description, pageInfo, [], // No previous steps context for repair
|
|
609
|
-
step.error, imageDataUrl, model_constants_1.VISION_MODEL);
|
|
610
|
-
// DEBUG: Log vision diagnostics
|
|
611
|
-
this.log(` 📸 Visual insights: ${supervisorDiagnostics.visualAnalysis}`);
|
|
612
|
-
this.log(` 🔍 Root cause: ${supervisorDiagnostics.rootCause}`);
|
|
613
|
-
this.log(` 💡 Recommended approach: ${supervisorDiagnostics.recommendedApproach}`);
|
|
614
|
-
if (supervisorDiagnostics.elementsFound.length > 0) {
|
|
615
|
-
this.log(` ✅ Elements found: ${supervisorDiagnostics.elementsFound.join(', ')}`);
|
|
616
|
-
}
|
|
617
|
-
if (supervisorDiagnostics.elementsNotFound.length > 0) {
|
|
618
|
-
this.log(` ❌ Elements not found: ${supervisorDiagnostics.elementsNotFound.join(', ')}`);
|
|
619
|
-
}
|
|
620
|
-
// Get repair suggestion with vision-enhanced context
|
|
621
|
-
this.log(` 🔨 STEP 2: Getting repair suggestion with vision insights...`);
|
|
622
|
-
const visionEnhancedFailureHistory = `${failureHistory}
|
|
623
|
-
|
|
624
|
-
VISION-BASED DIAGNOSTIC INSIGHTS:
|
|
625
|
-
Visual Analysis: ${supervisorDiagnostics.visualAnalysis}
|
|
626
|
-
Root Cause: ${supervisorDiagnostics.rootCause}
|
|
627
|
-
Recommended Approach: ${supervisorDiagnostics.recommendedApproach}
|
|
628
|
-
Elements Found: ${supervisorDiagnostics.elementsFound.join(', ') || 'None'}
|
|
629
|
-
Elements Not Found: ${supervisorDiagnostics.elementsNotFound.join(', ') || 'None'}
|
|
630
|
-
|
|
631
|
-
Use these vision insights to inform your repair strategy.`;
|
|
632
|
-
repairSuggestion = await this.llmFacade.getRepairSuggestion(step.description, step.code, step.error || 'Unknown error', pageInfo, visionEnhancedFailureHistory, recentRepairsContext, model);
|
|
633
|
-
usedVisionMode = true;
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
// Regular repair without vision
|
|
637
|
-
if (screenshotNeed.alternativeApproach) {
|
|
638
|
-
this.log(` 💡 Alternative approach: ${screenshotNeed.alternativeApproach}`);
|
|
639
|
-
}
|
|
640
|
-
repairSuggestion = await this.llmFacade.getRepairSuggestion(step.description, step.code, step.error || 'Unknown error', pageInfo, failureHistory, recentRepairsContext, model);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
// Regular repair attempt (first 2 attempts or already used vision)
|
|
645
|
-
repairSuggestion = await this.llmFacade.getRepairSuggestion(step.description, step.code, step.error || 'Unknown error', pageInfo, failureHistory, recentRepairsContext, model);
|
|
646
|
-
}
|
|
647
|
-
if (!repairSuggestion.shouldContinue) {
|
|
648
|
-
this.log(`AI decided to stop repair at attempt ${attempt}: ${repairSuggestion.reason}`);
|
|
649
|
-
break;
|
|
650
|
-
}
|
|
651
|
-
// Apply the repair action
|
|
652
|
-
try {
|
|
653
|
-
// Set the step index and insertAfterIndex on the client side based on current step being processed
|
|
654
|
-
const repairAction = {
|
|
655
|
-
...repairSuggestion.action,
|
|
656
|
-
stepIndex: i, // Client-side step index management
|
|
657
|
-
insertAfterIndex: repairSuggestion.action.operation === types_1.StepOperation.INSERT ? i - 1 : undefined // For INSERT, insert before current step
|
|
630
|
+
try {
|
|
631
|
+
// Call orchestrator with repair context (page object persisted)
|
|
632
|
+
const repairResult = await this.orchestratorAgent.executeStep(page, // Same page object (persisted state)
|
|
633
|
+
step.description, // Goal with testdata embedded
|
|
634
|
+
i + 1, // Current step number
|
|
635
|
+
updatedSteps.length, // Total steps
|
|
636
|
+
updatedSteps.map(s => s.description), // All step descriptions
|
|
637
|
+
memory, // Memory (empty for repair)
|
|
638
|
+
jobId || 'repair', priorSteps, // NEW: What was already completed
|
|
639
|
+
nextSteps // NEW: What comes after this
|
|
640
|
+
);
|
|
641
|
+
if (repairResult.success && repairResult.commands.length > 0) {
|
|
642
|
+
// MODIFY: Orchestrator fixed the step - replace with new code
|
|
643
|
+
const repairedCode = repairResult.commands.join('\n');
|
|
644
|
+
updatedSteps[i] = {
|
|
645
|
+
...step,
|
|
646
|
+
code: repairedCode,
|
|
647
|
+
success: true,
|
|
648
|
+
error: undefined
|
|
658
649
|
};
|
|
659
|
-
this.log(
|
|
660
|
-
this.log(
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
step.
|
|
670
|
-
updatedSteps[i].success = true;
|
|
671
|
-
updatedSteps[i].error = undefined;
|
|
672
|
-
this.log(`Step ${i + 1} marked as successful after MODIFY repair`);
|
|
673
|
-
// Report repaired step
|
|
674
|
-
if (this.progressReporter?.onStepProgress && jobId) {
|
|
675
|
-
this.log(`DEBUG: Reporting repaired step ${i + 1}:`);
|
|
676
|
-
this.log(` description: ${updatedSteps[i].description}`);
|
|
677
|
-
this.log(` code: ${updatedSteps[i].code}`);
|
|
678
|
-
await this.progressReporter.onStepProgress({
|
|
679
|
-
jobId,
|
|
680
|
-
stepId: updatedSteps[i].id, // Preserve original step ID if provided
|
|
681
|
-
stepNumber: i + 1,
|
|
682
|
-
description: updatedSteps[i].description,
|
|
683
|
-
code: updatedSteps[i].code,
|
|
684
|
-
status: 'SUCCESS_STEP_EXECUTION',
|
|
685
|
-
wasRepaired: true
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
else if (repairAction.operation === types_1.StepOperation.INSERT) {
|
|
690
|
-
// For INSERT: mark the newly inserted step as successful
|
|
691
|
-
const insertIndex = repairAction.insertAfterIndex !== undefined ? repairAction.insertAfterIndex + 1 : i + 1;
|
|
692
|
-
if (updatedSteps[insertIndex]) {
|
|
693
|
-
updatedSteps[insertIndex].success = true;
|
|
694
|
-
updatedSteps[insertIndex].error = undefined;
|
|
695
|
-
// Report inserted step
|
|
696
|
-
if (this.progressReporter?.onStepProgress && jobId) {
|
|
697
|
-
await this.progressReporter.onStepProgress({
|
|
698
|
-
jobId,
|
|
699
|
-
stepId: updatedSteps[insertIndex].id, // Preserve original step ID if provided
|
|
700
|
-
stepNumber: insertIndex + 1,
|
|
701
|
-
description: updatedSteps[insertIndex].description,
|
|
702
|
-
code: updatedSteps[insertIndex].code,
|
|
703
|
-
status: 'SUCCESS_STEP_EXECUTION',
|
|
704
|
-
wasRepaired: true
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
else if (repairAction.operation === types_1.StepOperation.REMOVE) {
|
|
710
|
-
// For REMOVE: no step to mark as successful since we removed it
|
|
711
|
-
// The step is already removed from the array
|
|
712
|
-
}
|
|
713
|
-
const commandInfo = repairAction.operation === types_1.StepOperation.MODIFY ?
|
|
714
|
-
`MODIFY: "${repairAction.newStep?.code || 'N/A'}"` :
|
|
715
|
-
repairAction.operation === types_1.StepOperation.INSERT ?
|
|
716
|
-
`INSERT: "${repairAction.newStep?.code || 'N/A'}"` :
|
|
717
|
-
repairAction.operation === types_1.StepOperation.REMOVE ?
|
|
718
|
-
`REMOVE: step at index ${repairAction.stepIndex}` :
|
|
719
|
-
repairAction.operation;
|
|
720
|
-
this.log(`Step ${i + 1} repair action ${commandInfo} executed successfully on attempt ${attempt}${usedVisionMode ? ' (vision-aided)' : ''}`);
|
|
721
|
-
// Update execution context based on the repair action
|
|
722
|
-
if (repairAction.operation === types_1.StepOperation.MODIFY && repairAction.newStep) {
|
|
723
|
-
// Update the step in the execution context for variable tracking
|
|
724
|
-
executionContext = executionContext.replace(originalCode, repairAction.newStep.code);
|
|
725
|
-
}
|
|
726
|
-
else if (repairAction.operation === types_1.StepOperation.INSERT && repairAction.newStep) {
|
|
727
|
-
// Insert the new step code into execution context for variable tracking
|
|
728
|
-
executionContext += repairAction.newStep.code + '\n';
|
|
729
|
-
}
|
|
730
|
-
else if (repairAction.operation === types_1.StepOperation.REMOVE) {
|
|
731
|
-
// Remove the step code from execution context for variable tracking
|
|
732
|
-
executionContext = executionContext.replace(originalCode, '');
|
|
733
|
-
}
|
|
734
|
-
// Record this successful repair
|
|
735
|
-
recentRepairs.push({
|
|
650
|
+
this.log(`✓ Step ${i + 1} MODIFIED by orchestrator (repair successful)`);
|
|
651
|
+
this.log(` Original code: ${step.code}`);
|
|
652
|
+
this.log(` New code (${repairResult.commands.length} commands):\n ${repairResult.commands.join('\n ')}`);
|
|
653
|
+
// Track what agent actually did in history (for future repair context)
|
|
654
|
+
const agentActionSummary = `${step.description} [AI-repaired: ${repairResult.commands.length} commands]`;
|
|
655
|
+
executedStepDescriptions.push(agentActionSummary);
|
|
656
|
+
// Report repaired step
|
|
657
|
+
if (this.progressReporter?.onStepProgress && jobId) {
|
|
658
|
+
await this.progressReporter.onStepProgress({
|
|
659
|
+
jobId,
|
|
660
|
+
stepId: step.id,
|
|
736
661
|
stepNumber: i + 1,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
newCode: repairAction.newStep?.code
|
|
662
|
+
description: updatedSteps[i].description,
|
|
663
|
+
code: updatedSteps[i].code,
|
|
664
|
+
status: 'SUCCESS_STEP_EXECUTION',
|
|
665
|
+
wasRepaired: true
|
|
742
666
|
});
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
// If inserting before current position, current step moved down by 1
|
|
755
|
-
this.log(`INSERT before current position: incrementing i from ${i} to ${i + 1}`);
|
|
756
|
-
i++; // Move to the original step that was pushed to the next position
|
|
757
|
-
}
|
|
758
|
-
else {
|
|
759
|
-
// If inserting at or after current position, stay at current step
|
|
760
|
-
this.log(`INSERT at/after current position: keeping i at ${i}`);
|
|
761
|
-
}
|
|
762
|
-
this.log(`INSERT: Steps array length after: ${updatedSteps.length}`);
|
|
763
|
-
this.log(`INSERT: Steps after operation: ${updatedSteps.map((s, idx) => `${idx}: "${s.description}" (success: ${s.success})`).join(', ')}`);
|
|
764
|
-
}
|
|
765
|
-
else if (repairAction.operation === types_1.StepOperation.REMOVE) {
|
|
766
|
-
// For REMOVE: stay at same index since the next step moved to current position
|
|
767
|
-
// Don't increment i because the array shifted left
|
|
667
|
+
}
|
|
668
|
+
// Ensure page is stable after agent repairs before returning control to script
|
|
669
|
+
this.log(`Waiting for page stability after agent repair...`);
|
|
670
|
+
try {
|
|
671
|
+
await page.waitForLoadState('networkidle', { timeout: 5000 });
|
|
672
|
+
this.log(`Page stabilized (networkidle) after agent repair`);
|
|
673
|
+
}
|
|
674
|
+
catch (stabilityError) {
|
|
675
|
+
try {
|
|
676
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 3000 });
|
|
677
|
+
this.log(`Page loaded (domcontentloaded) after agent repair`);
|
|
768
678
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
i++; // Move to next step for MODIFY
|
|
679
|
+
catch (fallbackError) {
|
|
680
|
+
this.log(`Page stability wait timed out (continuing anyway)`, 'warn');
|
|
772
681
|
}
|
|
773
|
-
// Add the repaired step's code to execution context for variable tracking
|
|
774
|
-
executionContext += step.code + '\n';
|
|
775
|
-
break;
|
|
776
|
-
}
|
|
777
|
-
else {
|
|
778
|
-
throw new Error(result.error || 'Repair action failed');
|
|
779
682
|
}
|
|
683
|
+
repairSuccess = true;
|
|
684
|
+
i++; // Continue to NEXT step (hand control back to script)
|
|
780
685
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
if (repairError instanceof Error && repairError.stack) {
|
|
792
|
-
this.log(` Repair stack trace: ${repairError.stack}`);
|
|
793
|
-
}
|
|
794
|
-
// Record this attempt in history
|
|
795
|
-
repairHistory.push({
|
|
796
|
-
attempt,
|
|
797
|
-
action: repairSuggestion.action,
|
|
798
|
-
error: repairErrorMessage,
|
|
799
|
-
pageInfo
|
|
800
|
-
});
|
|
801
|
-
step.error = repairErrorMessage;
|
|
686
|
+
else if (repairResult.success && repairResult.commands.length === 0) {
|
|
687
|
+
// DELETE: Step goal already achieved or no longer needed (e.g., modal already dismissed)
|
|
688
|
+
this.log(`✓ Step ${i + 1} DELETED by orchestrator (goal already achieved, step obsolete)`);
|
|
689
|
+
this.log(` Reason: Orchestrator completed with 0 commands - step no longer needed`);
|
|
690
|
+
// Track deletion in history (helps agent understand what was skipped)
|
|
691
|
+
executedStepDescriptions.push(`${step.description} [AI-deleted: step obsolete/already done]`);
|
|
692
|
+
// Remove the step from array
|
|
693
|
+
updatedSteps.splice(i, 1);
|
|
694
|
+
repairSuccess = true;
|
|
695
|
+
// Don't increment i - next step moved to current position
|
|
802
696
|
}
|
|
697
|
+
else {
|
|
698
|
+
this.log(`✗ Step ${i + 1} could not be repaired by orchestrator (reason: ${repairResult.terminationReason})`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
catch (repairError) {
|
|
702
|
+
this.log(`✗ Orchestrator repair failed: ${repairError.message}`);
|
|
803
703
|
}
|
|
704
|
+
// Legacy repair code removed - now using orchestrator
|
|
804
705
|
if (!repairSuccess) {
|
|
805
|
-
this.log(`Step ${i + 1}
|
|
706
|
+
this.log(`Step ${i + 1} could not be repaired - stopping execution`);
|
|
806
707
|
break;
|
|
807
708
|
}
|
|
808
709
|
}
|
|
@@ -845,244 +746,30 @@ Use these vision insights to inform your repair strategy.`;
|
|
|
845
746
|
}
|
|
846
747
|
return code; // Return the original code without removing comments
|
|
847
748
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
code.includes('waitForNavigation');
|
|
854
|
-
// Use appropriate timeout based on operation type
|
|
855
|
-
const timeout = needsLongerTimeout ? 30000 : 5000;
|
|
856
|
-
page.setDefaultTimeout(timeout);
|
|
857
|
-
try {
|
|
858
|
-
// Execute only the current step code, but make context variables available
|
|
859
|
-
const fullCode = code;
|
|
860
|
-
// Dynamically import expect
|
|
861
|
-
const { expect } = require('@playwright/test');
|
|
862
|
-
// Create a function that has access to page, expect, and the context variables
|
|
863
|
-
const executeCode = new Function('page', 'expect', 'contextVariables', `return (async () => {
|
|
864
|
-
// Make context variables available in the execution scope
|
|
865
|
-
for (const [key, value] of contextVariables) {
|
|
866
|
-
globalThis[key] = value;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
${fullCode}
|
|
870
|
-
|
|
871
|
-
// Capture any new variables that might have been created
|
|
872
|
-
const newVars = {};
|
|
873
|
-
for (const key in globalThis) {
|
|
874
|
-
if (!contextVariables.has(key) && typeof globalThis[key] !== 'function' && key !== 'page' && key !== 'expect') {
|
|
875
|
-
newVars[key] = globalThis[key];
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return newVars;
|
|
879
|
-
})()`);
|
|
880
|
-
const newVars = await executeCode(page, expect, contextVariables);
|
|
881
|
-
// Update the context variables with any new variables created
|
|
882
|
-
for (const [key, value] of Object.entries(newVars)) {
|
|
883
|
-
contextVariables.set(key, value);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
finally {
|
|
887
|
-
// Reset to default timeout for element operations
|
|
888
|
-
page.setDefaultTimeout(5000);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
async executeScriptContent(script, page) {
|
|
892
|
-
// Extract the test function content
|
|
893
|
-
const testMatch = script.match(/test\([^,]+,\s*async\s*\(\s*\{\s*page[^}]*\}\s*\)\s*=>\s*\{([\s\S]*)\}\s*\);/);
|
|
894
|
-
if (!testMatch) {
|
|
895
|
-
throw new Error('Could not extract test function from script');
|
|
896
|
-
}
|
|
897
|
-
const testBody = testMatch[1];
|
|
898
|
-
// Dynamically import expect
|
|
899
|
-
const { expect } = require('@playwright/test');
|
|
900
|
-
// Execute the entire test body as one async function
|
|
901
|
-
const executeTest = new Function('page', 'expect', `return (async () => { ${testBody} })()`);
|
|
902
|
-
await executeTest(page, expect);
|
|
749
|
+
// Legacy repair helper methods (now unused but kept for compilation)
|
|
750
|
+
buildFailureHistory() { return ''; }
|
|
751
|
+
buildRecentRepairsContext() { return ''; }
|
|
752
|
+
async applyRepairActionInContext() {
|
|
753
|
+
return { success: false };
|
|
903
754
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
ariaSnapshot: null,
|
|
913
|
-
interactiveElements: [],
|
|
914
|
-
formattedElements: 'Unable to extract'
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
buildFailureHistory(repairHistory, originalStep, originalError) {
|
|
919
|
-
if (repairHistory.length === 0) {
|
|
920
|
-
return `Original failure: ${this.safeSerializeError(originalError)}`;
|
|
921
|
-
}
|
|
922
|
-
let history = `Original failure: ${this.safeSerializeError(originalError)}\n\n`;
|
|
923
|
-
history += `Previous repair attempts:\n`;
|
|
924
|
-
repairHistory.forEach((attempt, index) => {
|
|
925
|
-
history += `Attempt ${attempt.attempt}:\n`;
|
|
926
|
-
history += ` Operation: ${attempt.action.operation}\n`;
|
|
927
|
-
if (attempt.action.newStep) {
|
|
928
|
-
history += ` Description: ${attempt.action.newStep.description}\n`;
|
|
929
|
-
history += ` Code: ${attempt.action.newStep.code}\n`;
|
|
755
|
+
generateUpdatedScript(steps, repairAdvice, originalScript) {
|
|
756
|
+
// Extract test name and hashtags from original script if provided
|
|
757
|
+
let testName = 'repairedTest';
|
|
758
|
+
let hashtags = [];
|
|
759
|
+
if (originalScript) {
|
|
760
|
+
const testNameMatch = originalScript.match(/test\(['"]([^'"]+)['"]/);
|
|
761
|
+
if (testNameMatch) {
|
|
762
|
+
testName = testNameMatch[1];
|
|
930
763
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
764
|
+
// Extract hashtags from TestChimp comment
|
|
765
|
+
const hashtagMatch = originalScript.match(/#\w+(?:\s+#\w+)*/);
|
|
766
|
+
if (hashtagMatch) {
|
|
767
|
+
hashtags = hashtagMatch[0].split(/\s+/).filter(tag => tag.startsWith('#'));
|
|
934
768
|
}
|
|
935
|
-
});
|
|
936
|
-
return history;
|
|
937
|
-
}
|
|
938
|
-
buildRecentRepairsContext(recentRepairs) {
|
|
939
|
-
if (recentRepairs.length === 0) {
|
|
940
|
-
return 'No recent repairs to consider.';
|
|
941
769
|
}
|
|
942
|
-
let context = 'Recent successful repairs that may affect this step:\n\n';
|
|
943
|
-
recentRepairs.forEach((repair, index) => {
|
|
944
|
-
context += `Step ${repair.stepNumber} was successfully repaired:\n`;
|
|
945
|
-
context += ` Operation: ${repair.operation}\n`;
|
|
946
|
-
if (repair.operation === 'REMOVE') {
|
|
947
|
-
context += ` Removed: "${repair.originalDescription}"\n`;
|
|
948
|
-
context += ` Code removed:\n ${repair.originalCode?.replace(/\n/g, '\n ')}\n`;
|
|
949
|
-
}
|
|
950
|
-
else if (repair.operation === 'INSERT') {
|
|
951
|
-
context += ` Inserted: "${repair.newDescription}"\n`;
|
|
952
|
-
context += ` Code inserted:\n ${repair.newCode?.replace(/\n/g, '\n ')}\n`;
|
|
953
|
-
}
|
|
954
|
-
else {
|
|
955
|
-
context += ` Original: "${repair.originalDescription}"\n`;
|
|
956
|
-
context += ` Repaired: "${repair.newDescription}"\n`;
|
|
957
|
-
context += ` Code changed from:\n ${repair.originalCode?.replace(/\n/g, '\n ')}\n`;
|
|
958
|
-
context += ` To:\n ${repair.newCode?.replace(/\n/g, '\n ')}\n`;
|
|
959
|
-
}
|
|
960
|
-
if (index < recentRepairs.length - 1) {
|
|
961
|
-
context += `\n`;
|
|
962
|
-
}
|
|
963
|
-
});
|
|
964
|
-
context += '\nConsider how these changes might affect the current step and adjust accordingly.';
|
|
965
|
-
return context;
|
|
966
|
-
}
|
|
967
|
-
async applyRepairActionInContext(action, steps, currentIndex, page, executionContext, contextVariables) {
|
|
968
|
-
try {
|
|
969
|
-
switch (action.operation) {
|
|
970
|
-
case types_1.StepOperation.MODIFY:
|
|
971
|
-
if (action.newStep && action.stepIndex !== undefined) {
|
|
972
|
-
// Modify existing step
|
|
973
|
-
steps[action.stepIndex] = {
|
|
974
|
-
...action.newStep,
|
|
975
|
-
success: false,
|
|
976
|
-
error: undefined
|
|
977
|
-
};
|
|
978
|
-
// Test the modified step with current page state and variables
|
|
979
|
-
await this.executeStepCode(action.newStep.code, page);
|
|
980
|
-
return { success: true, updatedContext: executionContext + action.newStep.code };
|
|
981
|
-
}
|
|
982
|
-
break;
|
|
983
|
-
case types_1.StepOperation.INSERT:
|
|
984
|
-
if (action.newStep && action.insertAfterIndex !== undefined) {
|
|
985
|
-
// Insert new step after specified index
|
|
986
|
-
const insertIndex = action.insertAfterIndex + 1;
|
|
987
|
-
const newStep = {
|
|
988
|
-
...action.newStep,
|
|
989
|
-
success: false,
|
|
990
|
-
error: undefined
|
|
991
|
-
};
|
|
992
|
-
this.log(`INSERT: Inserting step at index ${insertIndex} with description "${newStep.description}"`);
|
|
993
|
-
this.log(`INSERT: Steps before insertion: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
|
|
994
|
-
// Preserve success status of existing steps before insertion
|
|
995
|
-
const successStatusMap = new Map(steps.map((step, index) => [index, { success: step.success, error: step.error }]));
|
|
996
|
-
steps.splice(insertIndex, 0, newStep);
|
|
997
|
-
// Restore success status for steps that were shifted by the insertion
|
|
998
|
-
// Steps at insertIndex and before keep their original status
|
|
999
|
-
// Steps after insertIndex need to be shifted to their new positions
|
|
1000
|
-
for (let i = insertIndex + 1; i < steps.length; i++) {
|
|
1001
|
-
const originalIndex = i - 1; // The step that was originally at this position
|
|
1002
|
-
if (successStatusMap.has(originalIndex)) {
|
|
1003
|
-
const status = successStatusMap.get(originalIndex);
|
|
1004
|
-
steps[i].success = status.success;
|
|
1005
|
-
steps[i].error = status.error;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
// CRITICAL FIX: Ensure the inserted step doesn't overwrite existing step data
|
|
1009
|
-
// The new step should only have its own description, not inherit from existing steps
|
|
1010
|
-
this.log(`INSERT: Final step array after restoration: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
|
|
1011
|
-
this.log(`INSERT: Steps after insertion: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
|
|
1012
|
-
// Test the new step with current page state
|
|
1013
|
-
await this.executeStepCode(action.newStep.code, page);
|
|
1014
|
-
return { success: true, updatedContext: executionContext + action.newStep.code };
|
|
1015
|
-
}
|
|
1016
|
-
break;
|
|
1017
|
-
case types_1.StepOperation.REMOVE:
|
|
1018
|
-
if (action.stepIndex !== undefined) {
|
|
1019
|
-
// Remove step
|
|
1020
|
-
steps.splice(action.stepIndex, 1);
|
|
1021
|
-
return { success: true, updatedContext: executionContext };
|
|
1022
|
-
}
|
|
1023
|
-
break;
|
|
1024
|
-
}
|
|
1025
|
-
return { success: false, error: 'Invalid repair action' };
|
|
1026
|
-
}
|
|
1027
|
-
catch (error) {
|
|
1028
|
-
return {
|
|
1029
|
-
success: false,
|
|
1030
|
-
error: error instanceof Error ? error.message : 'Unknown error during repair action'
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
async applyRepairAction(action, steps, currentIndex, page) {
|
|
1035
|
-
try {
|
|
1036
|
-
switch (action.operation) {
|
|
1037
|
-
case types_1.StepOperation.MODIFY:
|
|
1038
|
-
if (action.newStep && action.stepIndex !== undefined) {
|
|
1039
|
-
// Modify existing step
|
|
1040
|
-
steps[action.stepIndex] = {
|
|
1041
|
-
...action.newStep,
|
|
1042
|
-
success: false,
|
|
1043
|
-
error: undefined
|
|
1044
|
-
};
|
|
1045
|
-
// Test the modified step
|
|
1046
|
-
await this.executeStepCode(action.newStep.code, page);
|
|
1047
|
-
return { success: true };
|
|
1048
|
-
}
|
|
1049
|
-
break;
|
|
1050
|
-
case types_1.StepOperation.INSERT:
|
|
1051
|
-
if (action.newStep && action.insertAfterIndex !== undefined) {
|
|
1052
|
-
// Insert new step after specified index
|
|
1053
|
-
const insertIndex = action.insertAfterIndex + 1;
|
|
1054
|
-
const newStep = {
|
|
1055
|
-
...action.newStep,
|
|
1056
|
-
success: false,
|
|
1057
|
-
error: undefined
|
|
1058
|
-
};
|
|
1059
|
-
steps.splice(insertIndex, 0, newStep);
|
|
1060
|
-
// Test the inserted step
|
|
1061
|
-
await this.executeStepCode(action.newStep.code, page);
|
|
1062
|
-
return { success: true };
|
|
1063
|
-
}
|
|
1064
|
-
break;
|
|
1065
|
-
case types_1.StepOperation.REMOVE:
|
|
1066
|
-
if (action.stepIndex !== undefined) {
|
|
1067
|
-
// Remove the step
|
|
1068
|
-
steps.splice(action.stepIndex, 1);
|
|
1069
|
-
return { success: true };
|
|
1070
|
-
}
|
|
1071
|
-
break;
|
|
1072
|
-
}
|
|
1073
|
-
return { success: false, error: 'Invalid repair action' };
|
|
1074
|
-
}
|
|
1075
|
-
catch (error) {
|
|
1076
|
-
return {
|
|
1077
|
-
success: false,
|
|
1078
|
-
error: error instanceof Error ? error.message : 'Repair action execution failed'
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
generateUpdatedScript(steps, repairAdvice) {
|
|
1083
770
|
const scriptLines = [
|
|
1084
771
|
"import { test, expect } from '@playwright/test';",
|
|
1085
|
-
`test('
|
|
772
|
+
`test('${testName}', async ({ page, browser, context }) => {`
|
|
1086
773
|
];
|
|
1087
774
|
steps.forEach((step, index) => {
|
|
1088
775
|
// Only add step if it has code to execute
|
|
@@ -1096,8 +783,8 @@ Use these vision insights to inform your repair strategy.`;
|
|
|
1096
783
|
});
|
|
1097
784
|
scriptLines.push('});');
|
|
1098
785
|
const script = scriptLines.join('\n');
|
|
1099
|
-
// Add TestChimp comment
|
|
1100
|
-
return (0, script_utils_1.addTestChimpComment)(script, repairAdvice);
|
|
786
|
+
// Add TestChimp comment with hashtags and repair advice
|
|
787
|
+
return (0, script_utils_1.addTestChimpComment)(script, repairAdvice, hashtags);
|
|
1101
788
|
}
|
|
1102
789
|
/**
|
|
1103
790
|
* Initialize browser with configuration (delegates to utility function)
|