testchimp-runner-core 0.0.26 → 0.0.29
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/CREDIT_CALLBACK_ARCHITECTURE.md +253 -0
- package/INTEGRATION_COMPLETE.md +322 -0
- package/RELEASE_0.0.26.md +165 -0
- package/RELEASE_0.0.27.md +236 -0
- package/RELEASE_0.0.28.md +286 -0
- package/dist/credit-usage-service.d.ts +28 -2
- package/dist/credit-usage-service.d.ts.map +1 -1
- package/dist/credit-usage-service.js +60 -24
- package/dist/credit-usage-service.js.map +1 -1
- package/dist/execution-service.d.ts.map +1 -1
- package/dist/execution-service.js +134 -10
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/progress-reporter.d.ts +30 -0
- package/dist/progress-reporter.d.ts.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +5 -4
- package/dist/prompts.js.map +1 -1
- package/dist/scenario-service.d.ts +1 -1
- package/dist/scenario-service.d.ts.map +1 -1
- package/dist/scenario-service.js +7 -4
- package/dist/scenario-service.js.map +1 -1
- package/dist/scenario-worker-class.d.ts +2 -10
- package/dist/scenario-worker-class.d.ts.map +1 -1
- package/dist/scenario-worker-class.js +88 -26
- package/dist/scenario-worker-class.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/credit-usage-service.ts +81 -26
- package/src/execution-service.ts +158 -11
- package/src/index.ts +42 -10
- package/src/progress-reporter.ts +35 -0
- package/src/prompts.ts +5 -4
- package/src/scenario-service.ts +16 -4
- package/src/scenario-worker-class.ts +102 -28
- package/src/types.ts +16 -0
- package/testchimp-runner-core-0.0.27.tgz +0 -0
- package/testchimp-runner-core-0.0.26.tgz +0 -0
package/src/index.ts
CHANGED
|
@@ -10,9 +10,9 @@ import { ScenarioWorker } from './scenario-worker-class';
|
|
|
10
10
|
import { PlaywrightMCPService } from './playwright-mcp-service';
|
|
11
11
|
import { LLMFacade } from './llm-facade';
|
|
12
12
|
import { AuthConfig } from './auth-config';
|
|
13
|
-
import { CreditUsageService } from './credit-usage-service';
|
|
13
|
+
import { CreditUsageService, CreditUsageCallback, CreditUsage, CreditUsageReason } from './credit-usage-service';
|
|
14
14
|
|
|
15
|
-
export { ExecutionService, ScenarioService, ScenarioWorker, PlaywrightMCPService, LLMFacade, CreditUsageService };
|
|
15
|
+
export { ExecutionService, ScenarioService, ScenarioWorker, PlaywrightMCPService, LLMFacade, CreditUsageService, CreditUsageCallback, CreditUsage, CreditUsageReason };
|
|
16
16
|
|
|
17
17
|
// File handlers
|
|
18
18
|
import { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler } from './file-handler';
|
|
@@ -20,12 +20,12 @@ export { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler };
|
|
|
20
20
|
|
|
21
21
|
// LLM Provider interfaces
|
|
22
22
|
import { LLMProvider, LLMRequest, LLMResponse } from './llm-provider';
|
|
23
|
-
import { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus } from './progress-reporter';
|
|
23
|
+
import { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus, StepInfo } from './progress-reporter';
|
|
24
24
|
import { BackendProxyLLMProvider } from './providers/backend-proxy-llm-provider';
|
|
25
25
|
import { LocalLLMProvider } from './providers/local-llm-provider';
|
|
26
26
|
|
|
27
27
|
export { LLMProvider, LLMRequest, LLMResponse };
|
|
28
|
-
export { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus };
|
|
28
|
+
export { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus, StepInfo };
|
|
29
29
|
export { BackendProxyLLMProvider, LocalLLMProvider };
|
|
30
30
|
|
|
31
31
|
// Orchestrator (tool-using agent)
|
|
@@ -81,6 +81,7 @@ export class TestChimpService {
|
|
|
81
81
|
private logger?: (message: string, level?: 'log' | 'error' | 'warn') => void;
|
|
82
82
|
private orchestratorOptions?: { useOrchestrator?: boolean; orchestratorConfig?: Partial<AgentConfig>; debugMode?: boolean };
|
|
83
83
|
private outputChannel?: any; // Store outputChannel to preserve it across service recreations
|
|
84
|
+
private creditUsageCallback?: CreditUsageCallback; // Store credit callback to preserve it across service recreations
|
|
84
85
|
|
|
85
86
|
constructor(
|
|
86
87
|
fileHandler?: FileHandler,
|
|
@@ -89,19 +90,22 @@ export class TestChimpService {
|
|
|
89
90
|
maxWorkers?: number,
|
|
90
91
|
llmProvider?: LLMProvider,
|
|
91
92
|
progressReporter?: ProgressReporter,
|
|
92
|
-
orchestratorOptions?: { useOrchestrator?: boolean; orchestratorConfig?: Partial<AgentConfig>; debugMode?: boolean }
|
|
93
|
+
orchestratorOptions?: { useOrchestrator?: boolean; orchestratorConfig?: Partial<AgentConfig>; debugMode?: boolean },
|
|
94
|
+
creditUsageCallback?: CreditUsageCallback
|
|
93
95
|
) {
|
|
94
96
|
this.fileHandler = fileHandler || new NoOpFileHandler();
|
|
95
97
|
this.authConfig = authConfig || null;
|
|
96
98
|
this.backendUrl = backendUrl || 'https://featureservice.testchimp.io'; // Default to production
|
|
97
99
|
this.progressReporter = progressReporter;
|
|
98
100
|
this.orchestratorOptions = orchestratorOptions;
|
|
101
|
+
this.creditUsageCallback = creditUsageCallback;
|
|
99
102
|
|
|
100
103
|
// Use provided LLM provider or default to backend proxy (backward compatible)
|
|
101
104
|
this.llmProvider = llmProvider || new BackendProxyLLMProvider(authConfig, backendUrl);
|
|
102
105
|
|
|
103
106
|
this.playwrightService = new PlaywrightMCPService();
|
|
104
|
-
|
|
107
|
+
// Pass credit callback to constructor - preserved across recreations via this.creditUsageCallback
|
|
108
|
+
this.creditUsageService = new CreditUsageService(this.authConfig || undefined, this.backendUrl, this.creditUsageCallback);
|
|
105
109
|
|
|
106
110
|
// Create services with providers
|
|
107
111
|
this.executionService = new ExecutionService(
|
|
@@ -189,8 +193,8 @@ export class TestChimpService {
|
|
|
189
193
|
this.llmProvider.setLogger?.(this.logger);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
// Recreate services with new provider
|
|
193
|
-
this.creditUsageService = new CreditUsageService(this.authConfig || undefined, this.backendUrl);
|
|
196
|
+
// Recreate services with new provider (preserve credit callback)
|
|
197
|
+
this.creditUsageService = new CreditUsageService(this.authConfig || undefined, this.backendUrl, this.creditUsageCallback);
|
|
194
198
|
this.executionService = new ExecutionService(
|
|
195
199
|
this.authConfig || undefined,
|
|
196
200
|
this.backendUrl,
|
|
@@ -243,6 +247,16 @@ export class TestChimpService {
|
|
|
243
247
|
}
|
|
244
248
|
}
|
|
245
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Set credit usage callback
|
|
252
|
+
* Server-side: Use callback to update DB directly (no axios calls)
|
|
253
|
+
* Client-side: Don't set callback, uses auth for axios calls to backend
|
|
254
|
+
*/
|
|
255
|
+
setCreditUsageCallback(callback: CreditUsageCallback): void {
|
|
256
|
+
this.creditUsageCallback = callback; // Store for future service recreations
|
|
257
|
+
this.creditUsageService.setCreditUsageCallback(callback);
|
|
258
|
+
}
|
|
259
|
+
|
|
246
260
|
/**
|
|
247
261
|
* Log a message using the configured logger
|
|
248
262
|
*/
|
|
@@ -271,8 +285,26 @@ export class TestChimpService {
|
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
// Scenario generation
|
|
274
|
-
async generateScript(
|
|
275
|
-
|
|
288
|
+
async generateScript(
|
|
289
|
+
scenario: string,
|
|
290
|
+
testName?: string,
|
|
291
|
+
config?: string,
|
|
292
|
+
model?: string,
|
|
293
|
+
scenarioFileName?: string,
|
|
294
|
+
existingBrowser?: any,
|
|
295
|
+
existingContext?: any,
|
|
296
|
+
existingPage?: any
|
|
297
|
+
): Promise<string> {
|
|
298
|
+
return this.scenarioService.processScenario(
|
|
299
|
+
scenario,
|
|
300
|
+
testName,
|
|
301
|
+
config,
|
|
302
|
+
model,
|
|
303
|
+
scenarioFileName,
|
|
304
|
+
existingBrowser,
|
|
305
|
+
existingContext,
|
|
306
|
+
existingPage
|
|
307
|
+
);
|
|
276
308
|
}
|
|
277
309
|
|
|
278
310
|
// Test execution
|
package/src/progress-reporter.ts
CHANGED
|
@@ -66,6 +66,16 @@ export interface TokenUsage {
|
|
|
66
66
|
timestamp: number;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Additional step info for lifecycle callbacks
|
|
71
|
+
*/
|
|
72
|
+
export interface StepInfo {
|
|
73
|
+
stepId?: string;
|
|
74
|
+
stepNumber: number;
|
|
75
|
+
description: string;
|
|
76
|
+
code?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
/**
|
|
70
80
|
* Progress reporter interface for external consumers
|
|
71
81
|
*/
|
|
@@ -105,5 +115,30 @@ export interface ProgressReporter {
|
|
|
105
115
|
* Generic logging (for environments that don't need structured progress)
|
|
106
116
|
*/
|
|
107
117
|
log?(message: string, level?: 'log' | 'error' | 'warn'): void;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* LIFECYCLE CALLBACKS (optional - used by scriptservice, ignored by local clients)
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Called before test execution starts
|
|
125
|
+
* - Script Service: Initialize browser context, set up DB records
|
|
126
|
+
* - VS Extension/GitHub: Not used (ignore)
|
|
127
|
+
*/
|
|
128
|
+
beforeStartTest?(page: any, browser: any, context: any): Promise<void>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Called before each step execution
|
|
132
|
+
* - Script Service: Update step status to IN_PROGRESS in DB
|
|
133
|
+
* - VS Extension/GitHub: Not used (ignore)
|
|
134
|
+
*/
|
|
135
|
+
beforeStepStart?(step: StepInfo, page: any): Promise<void>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Called after test execution completes (success or failure)
|
|
139
|
+
* - Script Service: Write final status to DB, cleanup resources
|
|
140
|
+
* - VS Extension/GitHub: Not used (return value is sufficient)
|
|
141
|
+
*/
|
|
142
|
+
afterEndTest?(status: 'passed' | 'failed', error?: string, page?: any): Promise<void>;
|
|
108
143
|
}
|
|
109
144
|
|
package/src/prompts.ts
CHANGED
|
@@ -757,12 +757,13 @@ ${script}
|
|
|
757
757
|
|
|
758
758
|
YOUR TASK (MINOR ADJUSTMENTS ONLY):
|
|
759
759
|
1. Remove duplicate/redundant expect() assertions (e.g., same assertion repeated twice)
|
|
760
|
-
2.
|
|
761
|
-
3.
|
|
762
|
-
4.
|
|
763
|
-
5. Remove any obviously redundant waits or checks
|
|
760
|
+
2. Fix obvious formatting issues (inconsistent spacing, etc.)
|
|
761
|
+
3. Consolidate multiple identical assertions into one
|
|
762
|
+
4. Remove any obviously redundant waits or checks
|
|
764
763
|
|
|
765
764
|
DO NOT:
|
|
765
|
+
- Remove the TestChimp header comment (/* This is a TestChimp Smart Test... */) - this must be preserved
|
|
766
|
+
- Remove step comments (e.g., "// Step 1: ..." or "// Navigate to...") - these are important for readability
|
|
766
767
|
- Change the test logic or flow
|
|
767
768
|
- Remove legitimate assertions
|
|
768
769
|
- Restructure the code
|
package/src/scenario-service.ts
CHANGED
|
@@ -111,16 +111,25 @@ export class ScenarioService extends EventEmitter {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
this.workers.push(worker);
|
|
114
|
-
|
|
114
|
+
// Internal initialization - no need to log worker details
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
async initialize(): Promise<void> {
|
|
118
118
|
// Wait for workers to be initialized
|
|
119
119
|
await this.initializeWorkers();
|
|
120
|
-
|
|
120
|
+
// Internal initialization - consumer doesn't need to see this
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
processScenario(
|
|
123
|
+
processScenario(
|
|
124
|
+
scenario: string,
|
|
125
|
+
testName?: string,
|
|
126
|
+
config?: PlaywrightConfig,
|
|
127
|
+
model?: string,
|
|
128
|
+
scenarioFileName?: string,
|
|
129
|
+
existingBrowser?: any,
|
|
130
|
+
existingContext?: any,
|
|
131
|
+
existingPage?: any
|
|
132
|
+
): string {
|
|
124
133
|
const jobId = `scenario_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
125
134
|
|
|
126
135
|
// Add job to queue
|
|
@@ -130,7 +139,10 @@ export class ScenarioService extends EventEmitter {
|
|
|
130
139
|
testName,
|
|
131
140
|
playwrightConfig: config,
|
|
132
141
|
model,
|
|
133
|
-
scenarioFileName
|
|
142
|
+
scenarioFileName,
|
|
143
|
+
existingBrowser,
|
|
144
|
+
existingContext,
|
|
145
|
+
existingPage
|
|
134
146
|
};
|
|
135
147
|
|
|
136
148
|
this.jobQueue.push(job);
|
|
@@ -97,8 +97,6 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
97
97
|
* Initialize orchestrator mode with tools
|
|
98
98
|
*/
|
|
99
99
|
private initializeOrchestrator(): void {
|
|
100
|
-
this.log('🤖 Initializing Orchestrator Mode');
|
|
101
|
-
|
|
102
100
|
// Create tool registry
|
|
103
101
|
this.toolRegistry = new ToolRegistry();
|
|
104
102
|
|
|
@@ -135,12 +133,12 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
135
133
|
this.debugMode // Pass debug mode
|
|
136
134
|
);
|
|
137
135
|
|
|
138
|
-
|
|
136
|
+
// Minimal initialization logging - internal details not needed by consumer
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
private log(message: string): void {
|
|
142
|
-
|
|
143
|
-
const formattedMessage = `[
|
|
140
|
+
// Let consumer add timestamps - just report the raw message
|
|
141
|
+
const formattedMessage = `[ScenarioWorker] ${message}`;
|
|
144
142
|
// Always log to console for debug visibility
|
|
145
143
|
console.log(formattedMessage);
|
|
146
144
|
// Also route to outputChannel if provided
|
|
@@ -150,8 +148,8 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
150
148
|
}
|
|
151
149
|
|
|
152
150
|
private logError(message: string): void {
|
|
153
|
-
|
|
154
|
-
const formattedMessage = `[
|
|
151
|
+
// Let consumer add timestamps - just report the raw message
|
|
152
|
+
const formattedMessage = `[ScenarioWorker] ERROR: ${message}`;
|
|
155
153
|
// Always log to console for debug visibility
|
|
156
154
|
console.error(formattedMessage);
|
|
157
155
|
// Also route to outputChannel if provided
|
|
@@ -246,21 +244,16 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
246
244
|
|
|
247
245
|
async initialize(): Promise<void> {
|
|
248
246
|
try {
|
|
249
|
-
const RUNNER_CORE_VERSION = "v1.5.0-vision-preserve-values";
|
|
250
|
-
this.log('═══════════════════════════════════════════════════════');
|
|
251
|
-
this.log(`🚀 RUNNER-CORE VERSION: ${RUNNER_CORE_VERSION}`);
|
|
252
|
-
this.log('═══════════════════════════════════════════════════════');
|
|
253
|
-
this.log('Initializing Scenario worker...');
|
|
254
247
|
this.sessionId = `scenario_worker_${Date.now()}`;
|
|
255
248
|
this.initialized = true;
|
|
256
|
-
|
|
249
|
+
// Minimal initialization - consumer doesn't need to see internal details
|
|
257
250
|
} catch (error) {
|
|
258
251
|
this.logError(`Scenario worker initialization error: ${error}`);
|
|
259
252
|
throw error;
|
|
260
253
|
}
|
|
261
254
|
}
|
|
262
255
|
|
|
263
|
-
async processScenarioJob(job:
|
|
256
|
+
async processScenarioJob(job: ScenarioRunJob): Promise<ScenarioResponse> {
|
|
264
257
|
if (!this.initialized) {
|
|
265
258
|
throw new Error('Scenario worker not initialized');
|
|
266
259
|
}
|
|
@@ -268,9 +261,9 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
268
261
|
// Set current job ID for progress reporting
|
|
269
262
|
this.currentJobId = job.id;
|
|
270
263
|
|
|
271
|
-
//
|
|
272
|
-
const
|
|
273
|
-
this.log(
|
|
264
|
+
// Log library version once (read from package.json)
|
|
265
|
+
const packageJson = require('../package.json');
|
|
266
|
+
this.log(`testchimp-runner-core v${packageJson.version}`);
|
|
274
267
|
this.log(`📋 Processing scenario: ${job.scenario}`);
|
|
275
268
|
|
|
276
269
|
const startTime = Date.now();
|
|
@@ -310,16 +303,30 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
310
303
|
}
|
|
311
304
|
this.emit('log', job.id, `\n## Execution Progress\n\n`);
|
|
312
305
|
|
|
313
|
-
// 2. Start a new browser session
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
306
|
+
// 2. Start a new browser session or use existing one
|
|
307
|
+
if (job.existingBrowser && job.existingContext && job.existingPage) {
|
|
308
|
+
// Use existing browser provided by caller (e.g., scriptservice)
|
|
309
|
+
this.log('Using existing browser/page provided by caller');
|
|
310
|
+
browser = job.existingBrowser;
|
|
311
|
+
context = job.existingContext;
|
|
312
|
+
page = job.existingPage;
|
|
313
|
+
} else {
|
|
314
|
+
// Create new browser (default behavior for local clients)
|
|
315
|
+
// Default to headed mode (headless: false) for better debugging
|
|
316
|
+
// Create logger function from outputChannel for browser initialization
|
|
317
|
+
const logger = this.outputChannel ? (message: string, level?: 'log' | 'error' | 'warn') => {
|
|
318
|
+
this.outputChannel!.appendLine(`[Browser] ${message}`);
|
|
319
|
+
} : undefined;
|
|
320
|
+
const browserInstance = await initializeBrowser(job.playwrightConfig, false, undefined, logger);
|
|
321
|
+
browser = browserInstance.browser;
|
|
322
|
+
context = browserInstance.context;
|
|
323
|
+
page = browserInstance.page;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// LIFECYCLE: Call beforeStartTest if provided
|
|
327
|
+
if (this.progressReporter?.beforeStartTest) {
|
|
328
|
+
await this.progressReporter.beforeStartTest(page, browser, context);
|
|
329
|
+
}
|
|
323
330
|
|
|
324
331
|
// Set reasonable timeout for most operations
|
|
325
332
|
// 5 seconds for element interactions (fast feedback on wrong selectors)
|
|
@@ -367,6 +374,17 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
367
374
|
step.stepNumber = i + 1;
|
|
368
375
|
|
|
369
376
|
try {
|
|
377
|
+
// LIFECYCLE: Call beforeStepStart if provided
|
|
378
|
+
if (this.progressReporter?.beforeStepStart) {
|
|
379
|
+
await this.progressReporter.beforeStepStart(
|
|
380
|
+
{
|
|
381
|
+
stepNumber: step.stepNumber,
|
|
382
|
+
description: step.description
|
|
383
|
+
},
|
|
384
|
+
page
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
370
388
|
// Use orchestrator to execute this step
|
|
371
389
|
const result = await this.orchestratorAgent.executeStep(
|
|
372
390
|
page,
|
|
@@ -405,6 +423,18 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
405
423
|
}
|
|
406
424
|
}
|
|
407
425
|
|
|
426
|
+
// REPORT FINAL STEP RESULT (after orchestrator completes all iterations)
|
|
427
|
+
// This gives the complete accumulated commands, not just one iteration
|
|
428
|
+
await this.reportStepProgress({
|
|
429
|
+
jobId: job.id,
|
|
430
|
+
stepNumber: step.stepNumber,
|
|
431
|
+
description: step.description,
|
|
432
|
+
code: step.playwrightCommands?.join('\n') || '', // All accumulated commands
|
|
433
|
+
status: step.success ? StepExecutionStatus.SUCCESS : StepExecutionStatus.FAILURE,
|
|
434
|
+
error: step.error,
|
|
435
|
+
agentIteration: result.iterations
|
|
436
|
+
});
|
|
437
|
+
|
|
408
438
|
} catch (error: any) {
|
|
409
439
|
this.logError(`Orchestrator execution failed for step ${step.stepNumber}: ${error.message}`);
|
|
410
440
|
step.success = false;
|
|
@@ -858,7 +888,17 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
858
888
|
}
|
|
859
889
|
|
|
860
890
|
// Generate clean script with TestChimp comment and code
|
|
891
|
+
this.log(`[ScenarioWorker] Generating script from ${steps.length} steps`);
|
|
892
|
+
steps.forEach((s, i) => {
|
|
893
|
+
this.log(`[ScenarioWorker] Step ${i+1}: ${s.description}`);
|
|
894
|
+
this.log(`[ScenarioWorker] Commands: ${s.playwrightCommands?.length || 0}`);
|
|
895
|
+
if (s.playwrightCommands && s.playwrightCommands.length > 0) {
|
|
896
|
+
this.log(`[ScenarioWorker] First command: ${s.playwrightCommands[0]}`);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
861
900
|
generatedScript = generateTestScript(testName, steps, undefined, hashtags);
|
|
901
|
+
this.log(`[ScenarioWorker] Generated script length: ${generatedScript.length}`);
|
|
862
902
|
|
|
863
903
|
// Perform final cleanup pass to remove redundancies and make minor adjustments
|
|
864
904
|
this.log(`[ScenarioWorker] Performing final script cleanup...`);
|
|
@@ -931,6 +971,15 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
931
971
|
|
|
932
972
|
const executionLog = logLines.join('\n');
|
|
933
973
|
|
|
974
|
+
// Report job completion
|
|
975
|
+
await this.reportJobProgress({
|
|
976
|
+
jobId: job.id,
|
|
977
|
+
status: overallSuccess ? 'completed' : 'failed',
|
|
978
|
+
testName,
|
|
979
|
+
script: generatedScript,
|
|
980
|
+
error: overallSuccess ? undefined : 'Some steps failed during execution'
|
|
981
|
+
});
|
|
982
|
+
|
|
934
983
|
return {
|
|
935
984
|
success: overallSuccess,
|
|
936
985
|
steps,
|
|
@@ -944,6 +993,16 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
944
993
|
} catch (error: any) {
|
|
945
994
|
overallSuccess = false;
|
|
946
995
|
this.logError(`Overall scenario processing error: ${error}`);
|
|
996
|
+
|
|
997
|
+
// Report job failure
|
|
998
|
+
await this.reportJobProgress({
|
|
999
|
+
jobId: job.id,
|
|
1000
|
+
status: 'failed',
|
|
1001
|
+
testName: job.testName || 'test',
|
|
1002
|
+
script: generatedScript,
|
|
1003
|
+
error: error instanceof Error ? error.message : 'Unknown error during scenario processing'
|
|
1004
|
+
});
|
|
1005
|
+
|
|
947
1006
|
return {
|
|
948
1007
|
success: false,
|
|
949
1008
|
steps,
|
|
@@ -955,7 +1014,22 @@ export class ScenarioWorker extends EventEmitter {
|
|
|
955
1014
|
error: error instanceof Error ? error.message : 'Unknown error during scenario processing'
|
|
956
1015
|
};
|
|
957
1016
|
} finally {
|
|
958
|
-
if
|
|
1017
|
+
// LIFECYCLE: Call afterEndTest if provided
|
|
1018
|
+
if (browser && this.progressReporter?.afterEndTest) {
|
|
1019
|
+
try {
|
|
1020
|
+
await this.progressReporter.afterEndTest(
|
|
1021
|
+
overallSuccess ? 'passed' : 'failed',
|
|
1022
|
+
overallSuccess ? undefined : 'Test execution had failures',
|
|
1023
|
+
page
|
|
1024
|
+
);
|
|
1025
|
+
} catch (callbackError) {
|
|
1026
|
+
this.log(`afterEndTest callback failed: ${callbackError}`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Only close browser if we created it (not provided by caller)
|
|
1031
|
+
const usingExternalBrowser = !!(job.existingBrowser && job.existingContext && job.existingPage);
|
|
1032
|
+
if (browser && !usingExternalBrowser) {
|
|
959
1033
|
await browser.close();
|
|
960
1034
|
}
|
|
961
1035
|
}
|
package/src/types.ts
CHANGED
|
@@ -83,6 +83,11 @@ export interface ScenarioRunJob {
|
|
|
83
83
|
playwrightConfig?: PlaywrightConfig;
|
|
84
84
|
model?: string;
|
|
85
85
|
scenarioFileName?: string;
|
|
86
|
+
|
|
87
|
+
// Optional: Provide existing browser/page/context (for server-side usage)
|
|
88
|
+
existingBrowser?: any;
|
|
89
|
+
existingContext?: any;
|
|
90
|
+
existingPage?: any;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/**
|
|
@@ -139,6 +144,11 @@ export interface ScenarioJob {
|
|
|
139
144
|
config?: PlaywrightConfig;
|
|
140
145
|
resolve: (result: ScenarioResponse) => void;
|
|
141
146
|
reject: (error: Error) => void;
|
|
147
|
+
|
|
148
|
+
// Optional: Provide existing browser/page/context (for server-side usage)
|
|
149
|
+
existingBrowser?: any;
|
|
150
|
+
existingContext?: any;
|
|
151
|
+
existingPage?: any;
|
|
142
152
|
}
|
|
143
153
|
|
|
144
154
|
// ============================================================================
|
|
@@ -166,6 +176,12 @@ export interface ScriptExecutionRequest {
|
|
|
166
176
|
model?: string;
|
|
167
177
|
headless?: boolean; // defaults to false (headed)
|
|
168
178
|
deflake_run_count?: number; // defaults to 1
|
|
179
|
+
|
|
180
|
+
// Optional: Provide existing browser/page/context (for server-side usage)
|
|
181
|
+
// If not provided, runner-core will create its own
|
|
182
|
+
existingBrowser?: any; // Browser instance
|
|
183
|
+
existingContext?: any; // BrowserContext instance
|
|
184
|
+
existingPage?: any; // Page instance
|
|
169
185
|
}
|
|
170
186
|
|
|
171
187
|
/**
|
|
Binary file
|
|
Binary file
|