testchimp-runner-core 0.0.1
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/auth-config.d.ts +33 -0
- package/dist/auth-config.d.ts.map +1 -0
- package/dist/auth-config.js +69 -0
- package/dist/auth-config.js.map +1 -0
- package/dist/env-loader.d.ts +20 -0
- package/dist/env-loader.d.ts.map +1 -0
- package/dist/env-loader.js +83 -0
- package/dist/env-loader.js.map +1 -0
- package/dist/execution-service.d.ts +61 -0
- package/dist/execution-service.d.ts.map +1 -0
- package/dist/execution-service.js +822 -0
- package/dist/execution-service.js.map +1 -0
- package/dist/file-handler.d.ts +59 -0
- package/dist/file-handler.d.ts.map +1 -0
- package/dist/file-handler.js +75 -0
- package/dist/file-handler.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-facade.d.ts +101 -0
- package/dist/llm-facade.d.ts.map +1 -0
- package/dist/llm-facade.js +289 -0
- package/dist/llm-facade.js.map +1 -0
- package/dist/playwright-mcp-service.d.ts +42 -0
- package/dist/playwright-mcp-service.d.ts.map +1 -0
- package/dist/playwright-mcp-service.js +167 -0
- package/dist/playwright-mcp-service.js.map +1 -0
- package/dist/prompts.d.ts +34 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +237 -0
- package/dist/prompts.js.map +1 -0
- package/dist/scenario-service.d.ts +25 -0
- package/dist/scenario-service.d.ts.map +1 -0
- package/dist/scenario-service.js +119 -0
- package/dist/scenario-service.js.map +1 -0
- package/dist/scenario-worker-class.d.ts +30 -0
- package/dist/scenario-worker-class.d.ts.map +1 -0
- package/dist/scenario-worker-class.js +263 -0
- package/dist/scenario-worker-class.js.map +1 -0
- package/dist/script-utils.d.ts +44 -0
- package/dist/script-utils.d.ts.map +1 -0
- package/dist/script-utils.js +100 -0
- package/dist/script-utils.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/browser-utils.d.ts +13 -0
- package/dist/utils/browser-utils.d.ts.map +1 -0
- package/dist/utils/browser-utils.js +269 -0
- package/dist/utils/browser-utils.js.map +1 -0
- package/dist/utils/page-info-utils.d.ts +16 -0
- package/dist/utils/page-info-utils.d.ts.map +1 -0
- package/dist/utils/page-info-utils.js +77 -0
- package/dist/utils/page-info-utils.js.map +1 -0
- package/env.prod +1 -0
- package/env.staging +1 -0
- package/package.json +38 -0
- package/src/auth-config.ts +84 -0
- package/src/env-loader.ts +91 -0
- package/src/execution-service.ts +999 -0
- package/src/file-handler.ts +104 -0
- package/src/index.ts +205 -0
- package/src/llm-facade.ts +413 -0
- package/src/playwright-mcp-service.ts +203 -0
- package/src/prompts.ts +247 -0
- package/src/scenario-service.ts +138 -0
- package/src/scenario-worker-class.ts +330 -0
- package/src/script-utils.ts +109 -0
- package/src/types.ts +202 -0
- package/src/utils/browser-utils.ts +272 -0
- package/src/utils/page-info-utils.ts +93 -0
- package/tsconfig.json +19 -0
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* All LLM prompts used throughout the application
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PROMPTS = void 0;
|
|
7
|
+
exports.PROMPTS = {
|
|
8
|
+
// Test name generation
|
|
9
|
+
TEST_NAME_GENERATION: {
|
|
10
|
+
SYSTEM: 'You are an AI assistant that generates meaningful test names for user journey tests. Carefully analyze the scenario description to look for any hints, indicators, or explicit mentions of what this test should be called. Pay attention to phrases like "test", "scenario", "check", "verify", "flow", or any descriptive terms that suggest the test purpose. If you find such indicators, use them as the basis for the test name. Otherwise, analyze the overall user journey and business purpose. Generate a concise test name (under 30 characters) in camelCase format. Respond with a JSON object in this format: {"testName": "userJourneyName"}',
|
|
11
|
+
USER: (scenario) => `Analyze this scenario description and generate a meaningful test name:\n\n"${scenario}"\n\nInstructions:\n1. Look for ANY hints or indicators in the text that suggest what this test should be called:\n - Explicit mentions: "Test: ...", "Scenario: ...", "Check: ...", "Verify: ..."\n - Descriptive phrases: "...flow", "...process", "...journey", "...workflow"\n - Action-focused terms: "login", "registration", "purchase", "messaging", "search"\n - Business context: "user onboarding", "checkout process", "team collaboration"\n2. If you find such indicators, use them as the basis for the test name\n3. If not found, analyze the user journey and business purpose\n4. Generate a concise name under 30 characters in camelCase\n\nExamples:\n- "Test: User login and messaging flow" -> "userLoginAndMessagingFlow"\n- "Checkout process with payment" -> "checkoutProcess"\n- "User registration and email verification" -> "userRegistration"\n- "Team messaging and collaboration" -> "teamMessaging"`
|
|
12
|
+
},
|
|
13
|
+
// Scenario breakdown
|
|
14
|
+
SCENARIO_BREAKDOWN: {
|
|
15
|
+
SYSTEM: `You are an expert test automation engineer that breaks down user scenarios into precise, actionable Playwright steps.
|
|
16
|
+
|
|
17
|
+
RULES:
|
|
18
|
+
- Each step should be a single, specific action
|
|
19
|
+
- Use clear, imperative language (Go to, Click, Type, Verify, etc.)
|
|
20
|
+
- Include specific details (URLs, text content, element descriptions)
|
|
21
|
+
- Order steps logically (navigation first, then interactions, then verifications)
|
|
22
|
+
- Be specific about what to verify/assert
|
|
23
|
+
|
|
24
|
+
COMMON STEP PATTERNS:
|
|
25
|
+
- "Go to [URL]" - for navigation
|
|
26
|
+
- "Click on [element description]" - for clicking
|
|
27
|
+
- "Type '[text]' into [field description]" - for text input
|
|
28
|
+
- "Verify that [condition]" - for assertions
|
|
29
|
+
- "Wait for [element/condition]" - for waiting
|
|
30
|
+
|
|
31
|
+
Respond with JSON: {"steps": ["step1", "step2", "step3"]}`,
|
|
32
|
+
USER: (scenario) => `Break down this scenario into specific, actionable steps for Playwright automation:\n\n"${scenario}"`
|
|
33
|
+
},
|
|
34
|
+
// Playwright command generation
|
|
35
|
+
PLAYWRIGHT_COMMAND: {
|
|
36
|
+
SYSTEM: 'You are an expert Playwright automation engineer. Generate clean, concise, and reliable commands. Use Playwright\'s built-in auto-waiting instead of explicit timeouts. Keep code readable and maintainable. Learn from previous failures and adapt your approach accordingly.',
|
|
37
|
+
USER: (stepDescription, pageInfo, previousCommands, attemptHistory, errorContext) => `You are an expert Playwright automation engineer. Generate a single, precise Playwright command for the given step.
|
|
38
|
+
|
|
39
|
+
CRITICAL RULES:
|
|
40
|
+
- Generate ONLY ONE command per step
|
|
41
|
+
- Use the most reliable selectors (prefer getByRole, getByText, getByLabel)
|
|
42
|
+
- Always wait for elements before interacting (use waitFor, waitForSelector)
|
|
43
|
+
- Use proper error handling and timeouts
|
|
44
|
+
- If previous attempts failed, try a COMPLETELY DIFFERENT approach
|
|
45
|
+
- Learn from failures and adapt your strategy
|
|
46
|
+
|
|
47
|
+
ELEMENT SELECTION PRIORITY:
|
|
48
|
+
1. getByRole() - Most reliable for interactive elements
|
|
49
|
+
2. getByText() - For text content
|
|
50
|
+
3. getByLabel() - For form inputs
|
|
51
|
+
4. getByPlaceholder() - For input placeholders
|
|
52
|
+
5. getByTestId() - For test-specific elements
|
|
53
|
+
6. locator() with CSS selectors - Last resort
|
|
54
|
+
|
|
55
|
+
COMMON PATTERNS:
|
|
56
|
+
- Navigation: await page.goto('url')
|
|
57
|
+
- Click: await page.getByRole('button', { name: 'text' }).click()
|
|
58
|
+
- Type: await page.getByRole('textbox', { name: 'label' }).fill('text')
|
|
59
|
+
- Wait: await page.waitForLoadState('networkidle')
|
|
60
|
+
- Verify: await expect(page).toHaveTitle(/expected/)
|
|
61
|
+
|
|
62
|
+
CODE STYLE GUIDELINES:
|
|
63
|
+
- Keep commands concise and clean
|
|
64
|
+
- Avoid explicit timeouts unless necessary
|
|
65
|
+
- Use Playwright's built-in auto-waiting
|
|
66
|
+
- Only add timeouts for specific slow operations
|
|
67
|
+
- Prefer single-line commands when possible
|
|
68
|
+
|
|
69
|
+
RETRY STRATEGIES:
|
|
70
|
+
- Timeout errors: Add waitFor() or increase timeout
|
|
71
|
+
- Not found errors: Try different selectors or wait for element
|
|
72
|
+
- Not visible errors: Scroll into view or wait for visibility
|
|
73
|
+
- Not enabled errors: Wait for element to be enabled
|
|
74
|
+
|
|
75
|
+
TIMEOUT GUIDELINES:
|
|
76
|
+
- Only add explicit timeouts for slow operations (file uploads, large data loads)
|
|
77
|
+
- Use page.waitForLoadState('networkidle') for page navigation
|
|
78
|
+
- Use element.waitFor() only when waiting for specific conditions
|
|
79
|
+
- Let Playwright's auto-waiting handle most interactions
|
|
80
|
+
|
|
81
|
+
Respond with JSON:
|
|
82
|
+
{
|
|
83
|
+
"command": "await page.goto('https://www.google.com');",
|
|
84
|
+
"reasoning": "Direct navigation to target URL",
|
|
85
|
+
"selectorStrategy": "direct_navigation"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Current State:
|
|
89
|
+
- URL: ${pageInfo.url}
|
|
90
|
+
- Title: ${pageInfo.title}
|
|
91
|
+
- Page Structure: ${pageInfo.pageStructure}
|
|
92
|
+
- Interactive Elements: ${pageInfo.interactiveElements}
|
|
93
|
+
- Form Fields: ${pageInfo.formFields}
|
|
94
|
+
- All Elements: ${pageInfo.elements}
|
|
95
|
+
|
|
96
|
+
Previous Commands:
|
|
97
|
+
\`\`\`javascript
|
|
98
|
+
${previousCommands}
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
${attemptHistory}
|
|
102
|
+
|
|
103
|
+
${errorContext}
|
|
104
|
+
|
|
105
|
+
Step to execute: "${stepDescription}"`
|
|
106
|
+
},
|
|
107
|
+
// Script parsing for AI repair
|
|
108
|
+
SCRIPT_PARSING: {
|
|
109
|
+
SYSTEM: 'You are an expert at parsing Playwright test scripts into logical steps. IGNORE doc comments at the top (/** ... */) as they are repair advice, not test steps. ALWAYS prioritize existing step comments over generating new ones. If the script has "// Step N:" comments, use those exactly as they are. Only generate new descriptions if no existing step comments are found. Be conservative and preserve exact code formatting.',
|
|
110
|
+
USER: (script) => `Parse this Playwright test script into logical steps. Be conservative and preserve the exact code.
|
|
111
|
+
|
|
112
|
+
Instructions:
|
|
113
|
+
1. IGNORE any doc comments at the top of the script (e.g., /** ... */ or /* ... */) - these are repair advice and should not be parsed as steps
|
|
114
|
+
2. FIRST, look for existing step comments (e.g., "// Step 1:", "// Step 2:", etc.) and use those as step boundaries
|
|
115
|
+
3. If existing step comments are found, use them exactly as they are - do not modify or regenerate descriptions
|
|
116
|
+
4. If no existing step comments, then group related commands that work together logically
|
|
117
|
+
5. Preserve ALL code exactly as written - do not modify, reformat, or change any code
|
|
118
|
+
6. Each step should contain commands that belong together (e.g., navigation + wait, form filling, verification)
|
|
119
|
+
7. Keep steps focused and not too granular
|
|
120
|
+
|
|
121
|
+
Script:
|
|
122
|
+
${script}
|
|
123
|
+
|
|
124
|
+
Return JSON object with steps array:
|
|
125
|
+
{
|
|
126
|
+
"steps": [
|
|
127
|
+
{
|
|
128
|
+
"description": "use existing comment if available, otherwise create meaningful description",
|
|
129
|
+
"code": "exact code from script - preserve all formatting and content"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}`
|
|
133
|
+
},
|
|
134
|
+
// Repair suggestion
|
|
135
|
+
REPAIR_SUGGESTION: {
|
|
136
|
+
SYSTEM: 'You are an expert test automation engineer specializing in fixing failing Playwright tests. Analyze the current DOM state, error message, and step description to suggest the best repair action. Consider the failure history to avoid repeating the same mistakes.',
|
|
137
|
+
USER: (stepDescription, stepCode, errorMessage, pageInfo, failureHistory, recentRepairs) => `Analyze this failing Playwright test step and suggest a repair action.
|
|
138
|
+
|
|
139
|
+
Current Step:
|
|
140
|
+
Description: ${stepDescription}
|
|
141
|
+
Code: ${stepCode}
|
|
142
|
+
Error: ${errorMessage}
|
|
143
|
+
|
|
144
|
+
Current Page State:
|
|
145
|
+
- URL: ${pageInfo.url}
|
|
146
|
+
- Title: ${pageInfo.title}
|
|
147
|
+
- Interactive Elements: ${pageInfo.interactiveElements}
|
|
148
|
+
- Form Fields: ${pageInfo.formFields}
|
|
149
|
+
|
|
150
|
+
${failureHistory}
|
|
151
|
+
|
|
152
|
+
${recentRepairs}
|
|
153
|
+
|
|
154
|
+
Choose the best repair action:
|
|
155
|
+
1. MODIFY - Fix the current step with better selectors, waits, or logic
|
|
156
|
+
2. INSERT - Add a new step before the current one (e.g., wait for element, scroll into view)
|
|
157
|
+
3. REMOVE - Skip this step entirely if it's not essential
|
|
158
|
+
|
|
159
|
+
Respond with JSON:
|
|
160
|
+
{
|
|
161
|
+
"shouldContinue": true/false,
|
|
162
|
+
"reason": "explanation of decision",
|
|
163
|
+
"action": {
|
|
164
|
+
"operation": "MODIFY|INSERT|REMOVE",
|
|
165
|
+
"newStep": {
|
|
166
|
+
"description": "step description",
|
|
167
|
+
"code": "await page.getByRole('button', { name: 'Submit' }).click();"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}`
|
|
171
|
+
},
|
|
172
|
+
// Repair confidence assessment
|
|
173
|
+
REPAIR_CONFIDENCE: {
|
|
174
|
+
SYSTEM: 'You are an expert test automation engineer who writes concise repair advice to build a running understanding of this test behavior and repairs done.',
|
|
175
|
+
USER: (originalScript, updatedScript) => `You are an expert test automation engineer. Generate a short repair advice that will be used to build a running understanding of this test.
|
|
176
|
+
|
|
177
|
+
Original Script:
|
|
178
|
+
${originalScript}
|
|
179
|
+
|
|
180
|
+
Repaired Script:
|
|
181
|
+
${updatedScript}
|
|
182
|
+
|
|
183
|
+
Instructions:
|
|
184
|
+
1. Compare the original and repaired scripts to identify what was fixed
|
|
185
|
+
2. Determine confidence level (0-5) where:
|
|
186
|
+
- 0 = Low confidence, repairs may be unreliable
|
|
187
|
+
- 5 = High confidence, repairs are solid and maintainable
|
|
188
|
+
3. Write SHORT advice (few short sentences max) that:
|
|
189
|
+
- States what specific fix was made
|
|
190
|
+
- Builds on any previous repair advice found in the original script
|
|
191
|
+
- Captures patterns (e.g., "usually fails on selector issues", "often needs deflaking")
|
|
192
|
+
- Will help future repairs understand this test's quirks
|
|
193
|
+
|
|
194
|
+
IMPORTANT:
|
|
195
|
+
- Step comments are EXPECTED and GOOD - do not mention them as issues
|
|
196
|
+
- Be concise and factual
|
|
197
|
+
- Focus on the actual fix made, not general recommendations
|
|
198
|
+
- Build a running understanding of this test's behavior relating to the repairs done
|
|
199
|
+
- If the original script contains previous repair advice, build upon it to create a cumulative understanding
|
|
200
|
+
|
|
201
|
+
Respond with JSON:
|
|
202
|
+
{
|
|
203
|
+
"confidence": 0-5,
|
|
204
|
+
"advice": "short factual statement about the fix and test patterns"
|
|
205
|
+
}`
|
|
206
|
+
},
|
|
207
|
+
// Final script generation
|
|
208
|
+
FINAL_SCRIPT: {
|
|
209
|
+
SYSTEM: 'You are an expert at creating drop-in replacement scripts. Generate a complete, properly formatted script that preserves the original structure while incorporating repairs and new advice.',
|
|
210
|
+
USER: (originalScript, updatedScript, newRepairAdvice) => `You are an expert at generating drop-in replacement scripts. Create a final script that can be pasted directly into the original file.
|
|
211
|
+
|
|
212
|
+
Original Script (with existing repair advice):
|
|
213
|
+
${originalScript}
|
|
214
|
+
|
|
215
|
+
Updated Script (with repairs):
|
|
216
|
+
${updatedScript}
|
|
217
|
+
|
|
218
|
+
New Repair Advice:
|
|
219
|
+
${newRepairAdvice}
|
|
220
|
+
|
|
221
|
+
Instructions:
|
|
222
|
+
1. Create a drop-in replacement that preserves the original test name and structure
|
|
223
|
+
2. Update the TestChimp comment block at the top to include BOTH existing and new repair advice
|
|
224
|
+
3. If there was existing repair advice, combine it with the new advice to build a running understanding
|
|
225
|
+
4. Use the repaired code from the updated script
|
|
226
|
+
5. Preserve the original test name (don't use 'repairedTest')
|
|
227
|
+
6. Keep the same import statements and overall structure
|
|
228
|
+
7. Ensure the script is properly formatted and ready to use
|
|
229
|
+
8. The repair advice should accumulate knowledge about this test's behavior patterns
|
|
230
|
+
|
|
231
|
+
Return JSON object with the final script:
|
|
232
|
+
{
|
|
233
|
+
"script": "complete final script that can be pasted into the original file"
|
|
234
|
+
}`
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEU,QAAA,OAAO,GAAG;IACrB,uBAAuB;IACvB,oBAAoB,EAAE;QACpB,MAAM,EAAE,6nBAA6nB;QAEroB,IAAI,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,8EAA8E,QAAQ,i5BAAi5B;KACpgC;IAED,qBAAqB;IACrB,kBAAkB,EAAE;QAClB,MAAM,EAAE;;;;;;;;;;;;;;;;oEAgBwD;QAEhE,IAAI,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,2FAA2F,QAAQ,GAAG;KACnI;IAED,gCAAgC;IAChC,kBAAkB,EAAE;QAClB,MAAM,EAAE,gRAAgR;QAExR,IAAI,EAAE,CAAC,eAAuB,EAAE,QAAa,EAAE,gBAAwB,EAAE,cAAsB,EAAE,YAAoB,EAAE,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAoDjH,QAAQ,CAAC,GAAG;eACV,QAAQ,CAAC,KAAK;wBACL,QAAQ,CAAC,aAAa;8BAChB,QAAQ,CAAC,mBAAmB;qBACrC,QAAQ,CAAC,UAAU;sBAClB,QAAQ,CAAC,QAAQ;;;;MAIjC,gBAAgB;;;MAGhB,cAAc;;MAEd,YAAY;;wBAEM,eAAe,GAAG;KACvC;IAED,+BAA+B;IAC/B,cAAc,EAAE;QACd,MAAM,EAAE,uaAAua;QAE/a,IAAI,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC;;;;;;;;;;;;cAYhB,MAAM;;;;;;;;;;cAUN;KACX;IAED,oBAAoB;IACpB,iBAAiB,EAAE;QACjB,MAAM,EAAE,sQAAsQ;QAE9Q,IAAI,EAAE,CAAC,eAAuB,EAAE,QAAgB,EAAE,YAAoB,EAAE,QAAa,EAAE,cAAsB,EAAE,aAAqB,EAAE,EAAE,CAAC;;;mBAG1H,eAAe;YACtB,QAAQ;aACP,YAAY;;;aAGZ,QAAQ,CAAC,GAAG;eACV,QAAQ,CAAC,KAAK;8BACC,QAAQ,CAAC,mBAAmB;qBACrC,QAAQ,CAAC,UAAU;;MAElC,cAAc;;MAEd,aAAa;;;;;;;;;;;;;;;;;;MAkBb;KACH;IAED,+BAA+B;IAC/B,iBAAiB,EAAE;QACjB,MAAM,EAAE,sJAAsJ;QAE9J,IAAI,EAAE,CAAC,cAAsB,EAAE,aAAqB,EAAE,EAAE,CAAC;;;cAG/C,cAAc;;;cAGd,aAAa;;;;;;;;;;;;;;;;;;;;;;;;cAwBb;KACX;IAED,0BAA0B;IAC1B,YAAY,EAAE;QACZ,MAAM,EAAE,6LAA6L;QAErM,IAAI,EAAE,CAAC,cAAsB,EAAE,aAAqB,EAAE,eAAuB,EAAE,EAAE,CAAC;;;cAGxE,cAAc;;;cAGd,aAAa;;;cAGb,eAAe;;;;;;;;;;;;;;;cAef;KACX;CACF,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { PlaywrightConfig } from './types';
|
|
3
|
+
import { FileHandler } from './file-handler';
|
|
4
|
+
import { AuthConfig } from './auth-config';
|
|
5
|
+
/**
|
|
6
|
+
* Service for processing scenarios using LLM + Playwright
|
|
7
|
+
*/
|
|
8
|
+
export declare class ScenarioService extends EventEmitter {
|
|
9
|
+
private workers;
|
|
10
|
+
private jobQueue;
|
|
11
|
+
private busyWorkers;
|
|
12
|
+
private maxWorkers;
|
|
13
|
+
private fileHandler;
|
|
14
|
+
private authConfig;
|
|
15
|
+
constructor(maxWorkers?: number, fileHandler?: FileHandler, authConfig?: AuthConfig);
|
|
16
|
+
private initializeWorkers;
|
|
17
|
+
private createWorker;
|
|
18
|
+
initialize(): Promise<void>;
|
|
19
|
+
processScenario(scenario: string, testName?: string, config?: PlaywrightConfig, model?: string, scenarioFileName?: string): string;
|
|
20
|
+
private processNextJob;
|
|
21
|
+
private handleJobResult;
|
|
22
|
+
private handleJobError;
|
|
23
|
+
shutdown(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=scenario-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-service.d.ts","sourceRoot":"","sources":["../src/scenario-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAiD,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE1F,OAAO,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAK3C;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAoB;gBAE1B,UAAU,GAAE,MAAU,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,UAAU;YAOxE,iBAAiB;YAMjB,YAAY;IAOpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;YAmBpH,cAAc;IA0C5B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,cAAc;IAShB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAWhC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ScenarioService = void 0;
|
|
7
|
+
const events_1 = require("events");
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
const scenario_worker_class_1 = require("./scenario-worker-class");
|
|
10
|
+
const file_handler_1 = require("./file-handler");
|
|
11
|
+
// Load environment variables
|
|
12
|
+
dotenv_1.default.config();
|
|
13
|
+
/**
|
|
14
|
+
* Service for processing scenarios using LLM + Playwright
|
|
15
|
+
*/
|
|
16
|
+
class ScenarioService extends events_1.EventEmitter {
|
|
17
|
+
constructor(maxWorkers = 2, fileHandler, authConfig) {
|
|
18
|
+
super();
|
|
19
|
+
this.workers = [];
|
|
20
|
+
this.jobQueue = [];
|
|
21
|
+
this.busyWorkers = new Set();
|
|
22
|
+
this.maxWorkers = maxWorkers;
|
|
23
|
+
this.fileHandler = fileHandler || new file_handler_1.NoOpFileHandler();
|
|
24
|
+
this.authConfig = authConfig || null;
|
|
25
|
+
}
|
|
26
|
+
async initializeWorkers() {
|
|
27
|
+
for (let i = 0; i < this.maxWorkers; i++) {
|
|
28
|
+
await this.createWorker();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async createWorker() {
|
|
32
|
+
const worker = new scenario_worker_class_1.ScenarioWorker(this.fileHandler, this.authConfig || undefined);
|
|
33
|
+
await worker.initialize();
|
|
34
|
+
this.workers.push(worker);
|
|
35
|
+
console.log(`Scenario worker initialized with session: ${worker['sessionId']}`);
|
|
36
|
+
}
|
|
37
|
+
async initialize() {
|
|
38
|
+
// Wait for workers to be initialized
|
|
39
|
+
await this.initializeWorkers();
|
|
40
|
+
console.log('Scenario service initialized');
|
|
41
|
+
}
|
|
42
|
+
processScenario(scenario, testName, config, model, scenarioFileName) {
|
|
43
|
+
const jobId = `scenario_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
44
|
+
// Add job to queue
|
|
45
|
+
const job = {
|
|
46
|
+
id: jobId,
|
|
47
|
+
scenario,
|
|
48
|
+
testName,
|
|
49
|
+
playwrightConfig: config,
|
|
50
|
+
model,
|
|
51
|
+
scenarioFileName
|
|
52
|
+
};
|
|
53
|
+
this.jobQueue.push(job);
|
|
54
|
+
this.processNextJob();
|
|
55
|
+
return jobId; // Return job ID for tracking
|
|
56
|
+
}
|
|
57
|
+
async processNextJob() {
|
|
58
|
+
if (this.jobQueue.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(`[ScenarioService] Processing next job. Queue length: ${this.jobQueue.length}, Workers: ${this.workers.length}, Busy workers: ${this.busyWorkers.size}`);
|
|
62
|
+
// Find available worker (proper load balancing)
|
|
63
|
+
const availableWorker = this.workers.find(worker => !this.busyWorkers.has(worker));
|
|
64
|
+
if (!availableWorker) {
|
|
65
|
+
console.log('[ScenarioService] No available workers, waiting...');
|
|
66
|
+
return; // All workers busy, wait for one to become available
|
|
67
|
+
}
|
|
68
|
+
const job = this.jobQueue.shift();
|
|
69
|
+
if (!job) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
console.log(`[ScenarioService] Processing job ${job.id} with worker`);
|
|
73
|
+
// Mark worker as busy
|
|
74
|
+
this.busyWorkers.add(availableWorker);
|
|
75
|
+
try {
|
|
76
|
+
// Process job directly with worker
|
|
77
|
+
const result = await availableWorker.processScenarioJob(job);
|
|
78
|
+
console.log(`[ScenarioService] Job ${job.id} completed with result:`, result);
|
|
79
|
+
this.handleJobResult(job.id, result);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error('Error processing job with worker:', error);
|
|
83
|
+
this.emit('jobError', job.id, error);
|
|
84
|
+
// Put job back in queue if it failed
|
|
85
|
+
this.jobQueue.unshift(job);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
// Mark worker as available again
|
|
89
|
+
this.busyWorkers.delete(availableWorker);
|
|
90
|
+
// Process next job
|
|
91
|
+
this.processNextJob();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
handleJobResult(jobId, result) {
|
|
95
|
+
// Emit result event
|
|
96
|
+
this.emit('jobComplete', jobId, result);
|
|
97
|
+
// Mark worker as available and process next job
|
|
98
|
+
this.busyWorkers.clear(); // Simple approach - clear all busy workers
|
|
99
|
+
this.processNextJob();
|
|
100
|
+
}
|
|
101
|
+
handleJobError(jobId, error) {
|
|
102
|
+
// Emit error event
|
|
103
|
+
this.emit('jobError', jobId, new Error(error));
|
|
104
|
+
// Mark worker as available and process next job
|
|
105
|
+
this.busyWorkers.clear(); // Simple approach - clear all busy workers
|
|
106
|
+
this.processNextJob();
|
|
107
|
+
}
|
|
108
|
+
async shutdown() {
|
|
109
|
+
console.log('Shutting down scenario service...');
|
|
110
|
+
// Cleanup all workers
|
|
111
|
+
const cleanupPromises = this.workers.map(worker => worker.cleanup());
|
|
112
|
+
await Promise.all(cleanupPromises);
|
|
113
|
+
this.workers = [];
|
|
114
|
+
this.busyWorkers.clear();
|
|
115
|
+
console.log('Scenario service shutdown complete');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ScenarioService = ScenarioService;
|
|
119
|
+
//# sourceMappingURL=scenario-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-service.js","sourceRoot":"","sources":["../src/scenario-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ScenarioResponse } from './types';
|
|
2
|
+
import { FileHandler } from './file-handler';
|
|
3
|
+
import { AuthConfig } from './auth-config';
|
|
4
|
+
interface OutputChannel {
|
|
5
|
+
appendLine: (text: string) => void;
|
|
6
|
+
}
|
|
7
|
+
interface ScenarioJob {
|
|
8
|
+
id: string;
|
|
9
|
+
scenario: string;
|
|
10
|
+
testName?: string;
|
|
11
|
+
playwrightConfig?: string;
|
|
12
|
+
model?: string;
|
|
13
|
+
scenarioFileName?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class ScenarioWorker {
|
|
16
|
+
private initialized;
|
|
17
|
+
private sessionId;
|
|
18
|
+
private llmFacade;
|
|
19
|
+
private fileHandler?;
|
|
20
|
+
private outputChannel?;
|
|
21
|
+
constructor(fileHandler?: FileHandler, authConfig?: AuthConfig, outputChannel?: OutputChannel);
|
|
22
|
+
private log;
|
|
23
|
+
private logError;
|
|
24
|
+
initialize(): Promise<void>;
|
|
25
|
+
processScenarioJob(job: ScenarioJob): Promise<ScenarioResponse>;
|
|
26
|
+
private executePlaywrightCommand;
|
|
27
|
+
cleanup(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=scenario-worker-class.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-worker-class.d.ts","sourceRoot":"","sources":["../src/scenario-worker-class.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,gBAAgB,EAAgB,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,UAAU,aAAa;IACrB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAGD,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAKD,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAE1B,WAAW,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,aAAa;IAM7F,OAAO,CAAC,GAAG;IAOX,OAAO,CAAC,QAAQ;IAOV,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,kBAAkB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAiOvD,wBAAwB;IAgChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B"}
|