testchimp-runner-core 0.1.8 → 0.1.10
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.map +1 -1
- package/dist/execution-service.js +114 -239
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator-agent.js +12 -2
- package/dist/orchestrator/orchestrator-agent.js.map +1 -1
- package/dist/progress-reporter.d.ts +9 -1
- package/dist/progress-reporter.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-service.d.ts","sourceRoot":"","sources":["../src/execution-service.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAE3B,sBAAsB,EACtB,uBAAuB,EAKvB,wBAAwB,EACxB,yBAAyB,EAG1B,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAW3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"execution-service.d.ts","sourceRoot":"","sources":["../src/execution-service.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAE3B,sBAAsB,EACtB,uBAAuB,EAKvB,wBAAwB,EACxB,yBAAyB,EAG1B,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAW3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAmbvD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,MAAM,CAAC,CAA8D;IAC7E,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,cAAc,CAAkB;IAExC;;OAEG;IACH,aAAa;gBAKX,UAAU,CAAC,EAAE,UAAU,EACvB,UAAU,CAAC,EAAE,MAAM,EACnB,uBAAuB,GAAE,MAAW,EACpC,WAAW,CAAC,EAAE,WAAW,EACzB,gBAAgB,CAAC,EAAE,gBAAgB;IA8BrC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI;IAKpF;;OAEG;IACH,OAAO,CAAC,GAAG;IAOX,OAAO,CAAC,WAAW;IAanB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAQ3C;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAkBtF;;OAEG;YACW,qBAAqB;IAmBnC;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAgCjG;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAsC7B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;;;OAIG;YACW,+BAA+B;IA4lB7C;;;OAGG;YACW,iBAAiB;YAiGjB,UAAU;YAkLV,eAAe;YAoKf,oBAAoB;YAiBpB,eAAe;IAuC7B,OAAO,CAAC,qBAAqB;IAK7B;;OAEG;YACW,iBAAiB;IAI/B;;;;OAIG;IACG,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAorBxF;;;;;OAKG;YACW,WAAW;CAgI1B"}
|
|
@@ -56,13 +56,6 @@ const build_info_1 = require("./build-info");
|
|
|
56
56
|
const orchestrator_1 = require("./orchestrator");
|
|
57
57
|
const test_file_parser_1 = require("./utils/test-file-parser");
|
|
58
58
|
class ModuleResolutionManager {
|
|
59
|
-
/**
|
|
60
|
-
* Get the current module resolution context from AsyncLocalStorage
|
|
61
|
-
* Public method to allow requireWrapper to access the context
|
|
62
|
-
*/
|
|
63
|
-
static getContext() {
|
|
64
|
-
return this.asyncLocalStorage.getStore();
|
|
65
|
-
}
|
|
66
59
|
/**
|
|
67
60
|
* Install the global hook (only once, shared by all instances)
|
|
68
61
|
*/
|
|
@@ -84,7 +77,7 @@ class ModuleResolutionManager {
|
|
|
84
77
|
const nodeModulesDir = path.dirname(runnerCoreDir);
|
|
85
78
|
if (fs.existsSync(nodeModulesDir) && path.basename(nodeModulesDir) === 'node_modules') {
|
|
86
79
|
this.scriptserviceNodeModules = nodeModulesDir;
|
|
87
|
-
|
|
80
|
+
console.log(`[ModuleResolution] Cached scriptservice node_modules: ${this.scriptserviceNodeModules}`);
|
|
88
81
|
}
|
|
89
82
|
}
|
|
90
83
|
catch (e) {
|
|
@@ -94,14 +87,12 @@ class ModuleResolutionManager {
|
|
|
94
87
|
Module._resolveFilename = function (request, parent, isMain, options) {
|
|
95
88
|
// Get resolution context for current execution
|
|
96
89
|
const context = ModuleResolutionManager.asyncLocalStorage.getStore();
|
|
97
|
-
const log = context?.log || (() => { }); // No-op if no logger available
|
|
98
|
-
// Log all module resolution attempts (even without context, for debugging)
|
|
99
|
-
log(`[ModuleResolution] Hook intercepted: "${request}" (parent: ${parent?.filename || 'none'}, hasContext: ${!!context})`);
|
|
100
90
|
// If no context, use original resolution (for requires outside our execution context)
|
|
101
91
|
if (!context) {
|
|
102
|
-
log(`[ModuleResolution] No context available, using original resolution`);
|
|
103
92
|
return ModuleResolutionManager.originalResolveFilename.call(this, request, parent, isMain, options);
|
|
104
93
|
}
|
|
94
|
+
// Log all module resolution attempts when we have context
|
|
95
|
+
console.log(`[ModuleResolution] Hook intercepted: "${request}" (parent: ${parent?.filename || 'none'})`);
|
|
105
96
|
// Handle relative imports
|
|
106
97
|
if (request.startsWith('.')) {
|
|
107
98
|
// Use standard Node.js relative path resolution
|
|
@@ -109,27 +100,27 @@ class ModuleResolutionManager {
|
|
|
109
100
|
// Resolve relative to testFileDir (the directory where the test file is located)
|
|
110
101
|
const resolveDir = context.testFileDir || (parent?.filename ? path.dirname(parent.filename) : context.tempDir);
|
|
111
102
|
// Log module resolution attempt
|
|
112
|
-
log(`[ModuleResolution] Resolving relative import: "${request}"`);
|
|
113
|
-
log(`[ModuleResolution] resolveDir: ${resolveDir}`);
|
|
103
|
+
console.log(`[ModuleResolution] Resolving relative import: "${request}"`);
|
|
104
|
+
console.log(`[ModuleResolution] resolveDir: ${resolveDir}`);
|
|
114
105
|
// Resolve the relative path using standard Node.js resolution
|
|
115
106
|
// This will correctly resolve based on the actual folder structure in tempDir
|
|
116
107
|
const resolvedPath = path.resolve(resolveDir, request);
|
|
117
|
-
log(`[ModuleResolution] resolvedPath: ${resolvedPath}`);
|
|
108
|
+
console.log(`[ModuleResolution] resolvedPath: ${resolvedPath}`);
|
|
118
109
|
// Try extensions in order: .page.ts, .page.js (POM files)
|
|
119
110
|
const extensions = ['.page.ts', '.page.js'];
|
|
120
111
|
for (const ext of extensions) {
|
|
121
112
|
const testPath = resolvedPath + ext;
|
|
122
113
|
try {
|
|
123
114
|
if (fs.existsSync(testPath)) {
|
|
124
|
-
log(`[ModuleResolution] ✓ Found file: ${testPath}`);
|
|
115
|
+
console.log(`[ModuleResolution] ✓ Found file: ${testPath}`);
|
|
125
116
|
return testPath;
|
|
126
117
|
}
|
|
127
118
|
else {
|
|
128
|
-
log(`[ModuleResolution] ✗ File does not exist: ${testPath}`);
|
|
119
|
+
console.log(`[ModuleResolution] ✗ File does not exist: ${testPath}`);
|
|
129
120
|
}
|
|
130
121
|
}
|
|
131
122
|
catch (e) {
|
|
132
|
-
log(`[ModuleResolution] ✗ Error checking file: ${testPath} - ${e}
|
|
123
|
+
console.log(`[ModuleResolution] ✗ Error checking file: ${testPath} - ${e}`);
|
|
133
124
|
}
|
|
134
125
|
}
|
|
135
126
|
// If no extension worked, try original resolution
|
|
@@ -153,95 +144,45 @@ class ModuleResolutionManager {
|
|
|
153
144
|
}
|
|
154
145
|
}
|
|
155
146
|
// For non-relative imports (like 'ai-wright', '@playwright/test'), use standard Node.js resolution
|
|
156
|
-
// These should resolve from node_modules
|
|
157
|
-
log(`[ModuleResolution] Resolving non-relative import: "${request}"`);
|
|
158
|
-
log(`[ModuleResolution] tempDir: ${context.tempDir}, testFileDir: ${context.testFileDir || 'undefined'}`);
|
|
159
|
-
// Find workspace root by looking for node_modules or package.json going up from tempDir
|
|
160
|
-
// tempDir is the tests folder (e.g., /workspace/tests), so we need to find /workspace/node_modules
|
|
161
|
-
let workspaceRoot = null;
|
|
162
|
-
if (context.tempDir) {
|
|
163
|
-
let currentDir = context.tempDir;
|
|
164
|
-
const maxDepth = 10; // Prevent infinite loops
|
|
165
|
-
let depth = 0;
|
|
166
|
-
log(`[ModuleResolution] Searching for workspace root starting from: ${currentDir}`);
|
|
167
|
-
while (depth < maxDepth && currentDir !== path.dirname(currentDir)) {
|
|
168
|
-
const nodeModulesPath = path.join(currentDir, 'node_modules');
|
|
169
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
170
|
-
log(`[ModuleResolution] Checking: ${currentDir} (depth: ${depth})`);
|
|
171
|
-
if (fs.existsSync(nodeModulesPath) || fs.existsSync(packageJsonPath)) {
|
|
172
|
-
workspaceRoot = currentDir;
|
|
173
|
-
log(`[ModuleResolution] ✓ Found workspace root: ${workspaceRoot}`);
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
currentDir = path.dirname(currentDir);
|
|
177
|
-
depth++;
|
|
178
|
-
}
|
|
179
|
-
if (!workspaceRoot) {
|
|
180
|
-
log(`[ModuleResolution] ✗ Workspace root not found after ${depth} levels`, 'warn');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
log(`[ModuleResolution] ✗ No tempDir in context`, 'warn');
|
|
185
|
-
}
|
|
186
|
-
// Temporarily modify Module._nodeModulePaths to include workspace root's node_modules
|
|
187
|
-
// This ensures Node.js searches the workspace root for modules like @playwright/test and ai-wright
|
|
188
|
-
const originalNodeModulePaths = Module._nodeModulePaths;
|
|
189
|
-
let nodeModulePathsModified = false;
|
|
190
|
-
if (workspaceRoot) {
|
|
191
|
-
const workspaceNodeModules = path.join(workspaceRoot, 'node_modules');
|
|
192
|
-
log(`[ModuleResolution] Workspace node_modules: ${workspaceNodeModules}`);
|
|
193
|
-
Module._nodeModulePaths = function (from) {
|
|
194
|
-
const paths = originalNodeModulePaths.call(this, from);
|
|
195
|
-
// Prepend workspace root's node_modules to the search paths (highest priority)
|
|
196
|
-
if (!paths.includes(workspaceNodeModules)) {
|
|
197
|
-
paths.unshift(workspaceNodeModules);
|
|
198
|
-
log(`[ModuleResolution] Modified _nodeModulePaths: prepended ${workspaceNodeModules}`);
|
|
199
|
-
}
|
|
200
|
-
return paths;
|
|
201
|
-
};
|
|
202
|
-
nodeModulePathsModified = true;
|
|
203
|
-
log(`[ModuleResolution] Modified _nodeModulePaths to include workspace root: ${workspaceNodeModules}`);
|
|
204
|
-
}
|
|
147
|
+
// These should resolve from node_modules where runner-core is installed (scriptservice's node_modules)
|
|
148
|
+
console.log(`[ModuleResolution] Resolving non-relative import: "${request}"`);
|
|
205
149
|
try {
|
|
206
|
-
// Use original Node.js resolution (will
|
|
207
|
-
log(`[ModuleResolution] Attempting resolution with modified paths...`);
|
|
150
|
+
// Use original Node.js resolution (will find from node_modules)
|
|
208
151
|
const resolved = ModuleResolutionManager.originalResolveFilename.call(this, request, parent, isMain, options);
|
|
209
|
-
log(`[ModuleResolution] ✓ Resolved to: ${resolved}`);
|
|
210
|
-
// Restore original _nodeModulePaths
|
|
211
|
-
if (nodeModulePathsModified) {
|
|
212
|
-
Module._nodeModulePaths = originalNodeModulePaths;
|
|
213
|
-
}
|
|
152
|
+
console.log(`[ModuleResolution] ✓ Resolved to: ${resolved}`);
|
|
214
153
|
return resolved;
|
|
215
154
|
}
|
|
216
155
|
catch (err) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
156
|
+
console.log(`[ModuleResolution] ✗ Failed to resolve: ${err.message}`);
|
|
157
|
+
// Build enhanced paths that include scriptservice's node_modules
|
|
158
|
+
// Find node_modules by looking up from the execution-service.js location
|
|
159
|
+
const enhancedPaths = [...(options?.paths || [])];
|
|
160
|
+
// Add tempDir paths
|
|
161
|
+
if (context.tempDir) {
|
|
162
|
+
enhancedPaths.push(context.tempDir);
|
|
163
|
+
enhancedPaths.push(path.join(context.tempDir, 'node_modules'));
|
|
164
|
+
}
|
|
165
|
+
// Add scriptservice's node_modules (where runner-core is installed)
|
|
166
|
+
// Use the cached path from execution-service.js location
|
|
223
167
|
if (ModuleResolutionManager.scriptserviceNodeModules) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// Continue to throw original error
|
|
242
|
-
}
|
|
168
|
+
enhancedPaths.push(ModuleResolutionManager.scriptserviceNodeModules);
|
|
169
|
+
console.log(`[ModuleResolution] Added scriptservice node_modules to paths: ${ModuleResolutionManager.scriptserviceNodeModules}`);
|
|
170
|
+
}
|
|
171
|
+
// Try with enhanced paths
|
|
172
|
+
try {
|
|
173
|
+
const enhancedOptions = {
|
|
174
|
+
...options,
|
|
175
|
+
paths: enhancedPaths
|
|
176
|
+
};
|
|
177
|
+
const resolved = ModuleResolutionManager.originalResolveFilename.call(this, request, parent, isMain, enhancedOptions);
|
|
178
|
+
console.log(`[ModuleResolution] ✓ Resolved (with enhanced paths): ${resolved}`);
|
|
179
|
+
return resolved;
|
|
180
|
+
}
|
|
181
|
+
catch (resolveErr) {
|
|
182
|
+
console.log(`[ModuleResolution] ✗ Failed with enhanced paths: ${resolveErr}`);
|
|
183
|
+
// Fall back to original error
|
|
184
|
+
throw err;
|
|
243
185
|
}
|
|
244
|
-
throw err;
|
|
245
186
|
}
|
|
246
187
|
};
|
|
247
188
|
this.hookInstalled = true;
|
|
@@ -253,9 +194,6 @@ class ModuleResolutionManager {
|
|
|
253
194
|
return this.asyncLocalStorage.run({ tempDir }, fn);
|
|
254
195
|
}
|
|
255
196
|
static runWithContext(context, fn) {
|
|
256
|
-
// Log when context is set
|
|
257
|
-
const log = context.log || (() => { });
|
|
258
|
-
log(`[ModuleResolutionManager] Setting context: tempDir=${context.tempDir}, testFileDir=${context.testFileDir || 'undefined'}`);
|
|
259
197
|
return this.asyncLocalStorage.run(context, fn);
|
|
260
198
|
}
|
|
261
199
|
}
|
|
@@ -268,97 +206,13 @@ ModuleResolutionManager.scriptserviceNodeModules = null;
|
|
|
268
206
|
* Variables defined in earlier steps remain available in later steps without re-evaluation
|
|
269
207
|
*/
|
|
270
208
|
class PersistentExecutionContext {
|
|
271
|
-
constructor(page, expect, test, ai, browser, browserContext, tempDir
|
|
209
|
+
constructor(page, expect, test, ai, browser, browserContext, tempDir) {
|
|
272
210
|
this.tempDir = tempDir;
|
|
273
|
-
this.log = log;
|
|
274
|
-
// Find workspace root by looking for node_modules or package.json going up from tempDir
|
|
275
|
-
// tempDir is the tests folder (e.g., /workspace/tests), so we need to find /workspace/node_modules
|
|
276
|
-
const path = require('path');
|
|
277
|
-
const Module = require('module');
|
|
278
|
-
let workspaceRoot = null;
|
|
279
|
-
if (tempDir) {
|
|
280
|
-
const fs = require('fs');
|
|
281
|
-
let currentDir = tempDir;
|
|
282
|
-
const maxDepth = 10; // Prevent infinite loops
|
|
283
|
-
let depth = 0;
|
|
284
|
-
while (depth < maxDepth && currentDir !== path.dirname(currentDir)) {
|
|
285
|
-
const nodeModulesPath = path.join(currentDir, 'node_modules');
|
|
286
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
287
|
-
if (fs.existsSync(nodeModulesPath) || fs.existsSync(packageJsonPath)) {
|
|
288
|
-
workspaceRoot = currentDir;
|
|
289
|
-
this.log?.(`[PersistentExecutionContext] Found workspace root: ${workspaceRoot}`);
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
currentDir = path.dirname(currentDir);
|
|
293
|
-
depth++;
|
|
294
|
-
}
|
|
295
|
-
if (!workspaceRoot) {
|
|
296
|
-
this.log?.(`[PersistentExecutionContext] Workspace root not found, tempDir: ${tempDir}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
this.workspaceRoot = workspaceRoot || undefined;
|
|
300
211
|
// Install global hook if not already installed (only happens once)
|
|
301
212
|
if (tempDir) {
|
|
302
213
|
ModuleResolutionManager.installHook();
|
|
303
214
|
this.setupTypeScriptSupport(tempDir);
|
|
304
215
|
}
|
|
305
|
-
// Create a require wrapper that ensures the hook is triggered
|
|
306
|
-
// When require is passed directly to vm.createContext(), it may not use the hooked Module._resolveFilename
|
|
307
|
-
// We manually invoke Module._resolveFilename (our hook) when context is available
|
|
308
|
-
const parentRequire = require;
|
|
309
|
-
const requireWrapper = (function (parentReq, logFn) {
|
|
310
|
-
return function (id) {
|
|
311
|
-
// Log when wrapper is called
|
|
312
|
-
logFn?.(`[RequireWrapper] Wrapper called for: "${id}"`);
|
|
313
|
-
// Get the current AsyncLocalStorage context
|
|
314
|
-
const context = ModuleResolutionManager.getContext();
|
|
315
|
-
if (context) {
|
|
316
|
-
// We have context, so manually trigger our hook logic by calling Module._resolveFilename
|
|
317
|
-
logFn?.(`[RequireWrapper] Context available, manually invoking Module._resolveFilename for: "${id}"`);
|
|
318
|
-
try {
|
|
319
|
-
// Create a fake parent module object for resolution
|
|
320
|
-
// Use testFileDir or tempDir as the parent's filename to ensure relative paths resolve correctly
|
|
321
|
-
const parentFilename = context.testFileDir
|
|
322
|
-
? path.join(context.testFileDir, 'dummy.js')
|
|
323
|
-
: (context.tempDir ? path.join(context.tempDir, 'dummy.js') : __filename);
|
|
324
|
-
// Manually call the hooked Module._resolveFilename to get the resolved path
|
|
325
|
-
// This will trigger our custom resolution logic (e.g., .page.ts extensions)
|
|
326
|
-
const resolvedPath = Module._resolveFilename(id, { filename: parentFilename }, false);
|
|
327
|
-
logFn?.(`[RequireWrapper] Resolved path: ${resolvedPath}`);
|
|
328
|
-
// Now load the module using Module._load with the resolved path
|
|
329
|
-
const module = Module._load(resolvedPath, { filename: parentFilename }, false);
|
|
330
|
-
logFn?.(`[RequireWrapper] Successfully loaded: "${id}"`);
|
|
331
|
-
return module;
|
|
332
|
-
}
|
|
333
|
-
catch (err) {
|
|
334
|
-
logFn?.(`[RequireWrapper] Manual resolution failed for: "${id}" - ${err.message}`, 'error');
|
|
335
|
-
// Fall back to standard require
|
|
336
|
-
try {
|
|
337
|
-
const result = parentReq(id);
|
|
338
|
-
logFn?.(`[RequireWrapper] Fallback to standard require succeeded for: "${id}"`);
|
|
339
|
-
return result;
|
|
340
|
-
}
|
|
341
|
-
catch (fallbackErr) {
|
|
342
|
-
logFn?.(`[RequireWrapper] Fallback also failed for: "${id}" - ${fallbackErr.message}`, 'error');
|
|
343
|
-
throw err; // Throw original error from our hook
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
// No context, use standard require (for requires outside our execution context)
|
|
349
|
-
logFn?.(`[RequireWrapper] No context available, using standard require for: "${id}"`);
|
|
350
|
-
try {
|
|
351
|
-
const result = parentReq(id);
|
|
352
|
-
logFn?.(`[RequireWrapper] Successfully resolved: "${id}"`);
|
|
353
|
-
return result;
|
|
354
|
-
}
|
|
355
|
-
catch (err) {
|
|
356
|
-
logFn?.(`[RequireWrapper] Failed to resolve: "${id}" - ${err.message}`, 'error');
|
|
357
|
-
throw err;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
}(parentRequire, this.log));
|
|
362
216
|
// Create V8 context with Playwright globals
|
|
363
217
|
// ai-wright will handle timeout extension internally (test.setTimeout or page.setDefaultTimeout)
|
|
364
218
|
const contextGlobals = {
|
|
@@ -371,7 +225,7 @@ class PersistentExecutionContext {
|
|
|
371
225
|
context: browserContext,
|
|
372
226
|
// Node.js globals
|
|
373
227
|
console,
|
|
374
|
-
require
|
|
228
|
+
require, // Normal require - hook will intercept resolution
|
|
375
229
|
setTimeout,
|
|
376
230
|
setInterval,
|
|
377
231
|
clearTimeout,
|
|
@@ -470,13 +324,13 @@ class PersistentExecutionContext {
|
|
|
470
324
|
// If tempDir is set, run in isolated context with module resolution
|
|
471
325
|
if (this.tempDir) {
|
|
472
326
|
// Execute within AsyncLocalStorage context so module resolution hook can access tempDir
|
|
473
|
-
// Use the stored testFileDir if available, otherwise use tempDir
|
|
474
|
-
const testFileDir = this.testFileDir || this.tempDir;
|
|
327
|
+
// Use the stored testFileDir if available, otherwise use tempDir/tests as default
|
|
328
|
+
const testFileDir = this.testFileDir || (this.tempDir ? require('path').join(this.tempDir, 'tests') : undefined);
|
|
475
329
|
const context = {
|
|
476
330
|
tempDir: this.tempDir,
|
|
477
331
|
testFileDir: testFileDir
|
|
478
332
|
};
|
|
479
|
-
await ModuleResolutionManager.runWithContext(
|
|
333
|
+
await ModuleResolutionManager.runWithContext(context, async () => {
|
|
480
334
|
if (!this.context) {
|
|
481
335
|
throw new Error('Context has been disposed');
|
|
482
336
|
}
|
|
@@ -540,12 +394,11 @@ function resolveFilePaths(tempDir, files) {
|
|
|
540
394
|
* Overrides setInputFiles to resolve relative paths using the test file's directory
|
|
541
395
|
*/
|
|
542
396
|
function applyFileUploadOverrides(page, tempDir, testFolderPath, log) {
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
// Otherwise use tempDir directly (don't append '/tests' again since tempDir is already the tests folder)
|
|
397
|
+
// Use test folder path from DB (e.g., ["tests", "e2e"]) to construct test file directory
|
|
398
|
+
// This ensures paths like "../fixtures/file.pdf" resolve correctly regardless of test nesting
|
|
546
399
|
const testFileDir = testFolderPath && testFolderPath.length > 0
|
|
547
400
|
? path.join(tempDir, ...testFolderPath)
|
|
548
|
-
: tempDir; //
|
|
401
|
+
: path.join(tempDir, 'tests'); // Fallback if not provided
|
|
549
402
|
log(`Using test file directory for file path resolution: ${testFileDir}`);
|
|
550
403
|
const originalSetInputFiles = page.setInputFiles.bind(page);
|
|
551
404
|
const originalLocator = page.locator.bind(page);
|
|
@@ -771,7 +624,7 @@ class ExecutionService {
|
|
|
771
624
|
const { expect, test } = require('@playwright/test');
|
|
772
625
|
const { ai } = require('ai-wright');
|
|
773
626
|
// Use shared context if provided, otherwise create new context
|
|
774
|
-
const persistentContext = sharedContext || new PersistentExecutionContext(page, expect, test, ai, browser, browserContext, options.tempDir
|
|
627
|
+
const persistentContext = sharedContext || new PersistentExecutionContext(page, expect, test, ai, browser, browserContext, options.tempDir);
|
|
775
628
|
const isSharedContext = !!sharedContext;
|
|
776
629
|
// Extract and execute imports from original script if provided
|
|
777
630
|
this.log(`[executeStepsInPersistentContext] Checking imports: originalScript=${!!options.originalScript}, tempDir=${!!options.tempDir}`);
|
|
@@ -783,22 +636,62 @@ class ExecutionService {
|
|
|
783
636
|
this.log(`Executing ${importStatements.length} import statement(s) before steps`);
|
|
784
637
|
// Convert ES6 imports to CommonJS requires and execute
|
|
785
638
|
const requireStatements = import_utils_1.ImportUtils.convertImportsToRequires(importStatements, (msg) => this.log(msg));
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
let testFileDir;
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
639
|
+
// Execute imports with proper context - find the actual test file path
|
|
640
|
+
// All tests are within the tests folder
|
|
641
|
+
// We search for test files in tempDir/tests directory tree
|
|
642
|
+
let testFileDir = path.join(options.tempDir, 'tests'); // Default to tests directory
|
|
643
|
+
try {
|
|
644
|
+
const fs = require('fs');
|
|
645
|
+
const testsDir = path.join(options.tempDir, 'tests');
|
|
646
|
+
// Check if tests directory exists
|
|
647
|
+
if (!fs.existsSync(testsDir)) {
|
|
648
|
+
this.log(`Tests directory ${testsDir} does not exist, using default for module resolution`, 'warn');
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
// Recursive search with depth limit to prevent infinite loops
|
|
652
|
+
const findTestFile = (dir, depth = 0, maxDepth = 10) => {
|
|
653
|
+
if (depth > maxDepth) {
|
|
654
|
+
return null; // Prevent infinite recursion
|
|
655
|
+
}
|
|
656
|
+
try {
|
|
657
|
+
const files = fs.readdirSync(dir);
|
|
658
|
+
for (const file of files) {
|
|
659
|
+
const fullPath = path.join(dir, file);
|
|
660
|
+
try {
|
|
661
|
+
const stat = fs.statSync(fullPath);
|
|
662
|
+
if (stat.isDirectory()) {
|
|
663
|
+
const found = findTestFile(fullPath, depth + 1, maxDepth);
|
|
664
|
+
if (found)
|
|
665
|
+
return found;
|
|
666
|
+
}
|
|
667
|
+
else if (file.endsWith('.spec.ts') || file.endsWith('.spec.js') ||
|
|
668
|
+
file.endsWith('.test.ts') || file.endsWith('.test.js')) {
|
|
669
|
+
return fullPath; // Found a test file
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
catch (statError) {
|
|
673
|
+
// Skip files we can't stat
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
catch (e) {
|
|
679
|
+
// Continue searching in other directories
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
};
|
|
683
|
+
const testFilePath = findTestFile(testsDir);
|
|
684
|
+
if (testFilePath) {
|
|
685
|
+
testFileDir = path.dirname(testFilePath);
|
|
686
|
+
this.log(`Found test file at ${testFilePath}, using directory ${testFileDir} for module resolution`);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
this.log(`No test file found in ${testsDir}, using tests directory for module resolution`, 'warn');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
799
692
|
}
|
|
800
|
-
|
|
801
|
-
|
|
693
|
+
catch (searchError) {
|
|
694
|
+
this.log(`Could not search for test file: ${searchError.message}, using tests directory for module resolution`, 'warn');
|
|
802
695
|
}
|
|
803
696
|
// Store test file directory in context for module resolution
|
|
804
697
|
persistentContext.setTestFileDir(testFileDir);
|
|
@@ -874,8 +767,7 @@ class ExecutionService {
|
|
|
874
767
|
let importResults = {};
|
|
875
768
|
try {
|
|
876
769
|
if (options.tempDir) {
|
|
877
|
-
importResults = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir
|
|
878
|
-
this.log(`[executeStepsInPersistentContext] Executing import capture code`);
|
|
770
|
+
importResults = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir }, async () => {
|
|
879
771
|
const script = new vm.Script(captureCode);
|
|
880
772
|
const result = script.runInContext(context);
|
|
881
773
|
return await result;
|
|
@@ -908,36 +800,25 @@ class ExecutionService {
|
|
|
908
800
|
let moduleResult;
|
|
909
801
|
if (modulePath && options.tempDir) {
|
|
910
802
|
// Execute require for the specific module
|
|
911
|
-
|
|
912
|
-
moduleResult = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir, log: this.log.bind(this) }, async () => {
|
|
913
|
-
this.log(`[executeStepsInPersistentContext] Inside runWithContext, executing require for module: ${modulePath}`);
|
|
914
|
-
this.log(`[DEBUG] About to execute require('${modulePath}') in VM context`);
|
|
803
|
+
moduleResult = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir }, async () => {
|
|
915
804
|
const script = new vm.Script(`(() => require('${modulePath}'))()`);
|
|
916
|
-
|
|
917
|
-
this.log(`[DEBUG] require('${modulePath}') completed in VM context`);
|
|
918
|
-
return result;
|
|
805
|
+
return script.runInContext(context);
|
|
919
806
|
});
|
|
920
807
|
}
|
|
921
808
|
else {
|
|
922
809
|
// Execute the full require statement as-is
|
|
923
810
|
if (options.tempDir) {
|
|
924
|
-
|
|
925
|
-
moduleResult = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir, log: this.log.bind(this) }, async () => {
|
|
926
|
-
this.log(`[executeStepsInPersistentContext] Inside runWithContext, executing require statement: ${requireStmt}`);
|
|
927
|
-
this.log(`[DEBUG] About to execute require statement in VM context: ${requireStmt}`);
|
|
811
|
+
moduleResult = await ModuleResolutionManager.runWithContext({ tempDir: options.tempDir, testFileDir: context.__testFileDir }, async () => {
|
|
928
812
|
// Try to extract variable name, but execute the require
|
|
929
813
|
const varNameMatch = requireStmt.match(/const\s+(\w+)\s*=/);
|
|
930
814
|
if (varNameMatch) {
|
|
931
815
|
const script = new vm.Script(`(() => { ${requireStmt}; return ${varNameMatch[1]}; })()`);
|
|
932
|
-
|
|
933
|
-
this.log(`[DEBUG] require statement completed in VM context`);
|
|
934
|
-
return result;
|
|
816
|
+
return script.runInContext(context);
|
|
935
817
|
}
|
|
936
818
|
else {
|
|
937
819
|
// Side-effect import - just execute
|
|
938
820
|
const script = new vm.Script(`(() => { ${requireStmt}; return null; })()`);
|
|
939
821
|
script.runInContext(context);
|
|
940
|
-
this.log(`[DEBUG] Side-effect require statement completed in VM context`);
|
|
941
822
|
return null;
|
|
942
823
|
}
|
|
943
824
|
});
|
|
@@ -1371,8 +1252,7 @@ class ExecutionService {
|
|
|
1371
1252
|
mode: 'RUN_EXACTLY',
|
|
1372
1253
|
jobId: request.jobId,
|
|
1373
1254
|
tempDir: request.tempDir,
|
|
1374
|
-
originalScript: request.script
|
|
1375
|
-
scriptFilePath: request.scriptFilePath // Pass script file path for module resolution
|
|
1255
|
+
originalScript: request.script // AST will extract statements from this
|
|
1376
1256
|
});
|
|
1377
1257
|
// LIFECYCLE: afterEndTest
|
|
1378
1258
|
if (this.progressReporter?.afterEndTest) {
|
|
@@ -1425,8 +1305,7 @@ class ExecutionService {
|
|
|
1425
1305
|
mode: 'RUN_EXACTLY',
|
|
1426
1306
|
jobId: request.jobId,
|
|
1427
1307
|
tempDir: request.tempDir,
|
|
1428
|
-
originalScript: request.script
|
|
1429
|
-
scriptFilePath: request.scriptFilePath // Pass script file path for module resolution
|
|
1308
|
+
originalScript: request.script // AST will extract statements from this
|
|
1430
1309
|
});
|
|
1431
1310
|
// LIFECYCLE: afterEndTest
|
|
1432
1311
|
if (this.progressReporter?.afterEndTest) {
|
|
@@ -1542,8 +1421,7 @@ class ExecutionService {
|
|
|
1542
1421
|
jobId: request.jobId,
|
|
1543
1422
|
model,
|
|
1544
1423
|
tempDir: request.tempDir,
|
|
1545
|
-
originalScript: request.script
|
|
1546
|
-
scriptFilePath: request.scriptFilePath // Pass script file path for module resolution
|
|
1424
|
+
originalScript: request.script // Pass original script for import extraction
|
|
1547
1425
|
});
|
|
1548
1426
|
const updatedSteps = result.updatedSteps || steps;
|
|
1549
1427
|
const allStepsSuccessful = result.success;
|
|
@@ -2029,8 +1907,7 @@ class ExecutionService {
|
|
|
2029
1907
|
jobId: jobId,
|
|
2030
1908
|
tempDir: request.tempDir,
|
|
2031
1909
|
originalScript: testScript, // For module resolution (imports)
|
|
2032
|
-
model: request.model
|
|
2033
|
-
scriptFilePath: request.scriptFilePath // Pass script file path for module resolution
|
|
1910
|
+
model: request.model
|
|
2034
1911
|
}, suiteContext // Pass shared context
|
|
2035
1912
|
);
|
|
2036
1913
|
if (!result.success) {
|
|
@@ -2299,13 +2176,11 @@ class ExecutionService {
|
|
|
2299
2176
|
const hookContext = sharedContext || new PersistentExecutionContext(page, expect, test, ai, browser, context, tempDir);
|
|
2300
2177
|
try {
|
|
2301
2178
|
// Execute using step-wise execution infrastructure to trigger callbacks
|
|
2302
|
-
// Note: Hooks don't have a scriptFilePath, so tempDir will be used as fallback
|
|
2303
2179
|
const result = await this.executeStepsInPersistentContext(steps, page, browser, context, {
|
|
2304
2180
|
mode: types_1.ExecutionMode.RUN_EXACTLY,
|
|
2305
2181
|
jobId: jobId,
|
|
2306
2182
|
tempDir: tempDir,
|
|
2307
2183
|
originalScript: hookScript
|
|
2308
|
-
// No scriptFilePath for hooks - will use tempDir as fallback
|
|
2309
2184
|
}, hookContext // Pass shared context
|
|
2310
2185
|
);
|
|
2311
2186
|
if (!result.success) {
|