testchimp-runner-core 0.1.21 → 0.1.22

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.
@@ -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,EAA+C,MAAM,qBAAqB,CAAC;AAmbpG;;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;IASX,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;;;;;;;;;;OAUG;IACG,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE,GAAG,EACb,cAAc,CAAC,EAAE,GAAG,EACpB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAuBhB;;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;IAowB7C;;;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"}
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;AAY3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAA+C,MAAM,qBAAqB,CAAC;AAmbpG;;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;IASX,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;;;;;;;;;;OAUG;IACG,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE,GAAG,EACb,cAAc,CAAC,EAAE,GAAG,EACpB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAuBhB;;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;IAm2B7C;;;OAGG;YACW,iBAAiB;YAiGjB,UAAU;YAoMV,eAAe;YAsKf,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;IAsxBxF;;;;;OAKG;YACW,WAAW;CAoI1B"}
@@ -51,6 +51,7 @@ const import_utils_1 = require("./utils/import-utils");
51
51
  const initialization_code_utils_1 = require("./utils/initialization-code-utils");
52
52
  const script_parser_utils_1 = require("./utils/script-parser-utils");
53
53
  const script_generator_utils_1 = require("./utils/script-generator-utils");
54
+ const pom_denormalizer_1 = require("./utils/pom-denormalizer");
54
55
  const progress_reporter_1 = require("./progress-reporter");
55
56
  const backend_proxy_llm_provider_1 = require("./providers/backend-proxy-llm-provider");
56
57
  const build_info_1 = require("./build-info");
@@ -645,12 +646,71 @@ class ExecutionService {
645
646
  * Variables persist across steps without re-execution
646
647
  */
647
648
  async executeStepsInPersistentContext(steps, page, browser, browserContext, options, sharedContext) {
648
- this.log(`[executeStepsInPersistentContext] Called with ${steps.length} steps, mode=${options.mode}, originalScript=${!!options.originalScript}, tempDir=${!!options.tempDir}, sharedContext=${!!sharedContext}`);
649
+ this.log(`[executeStepsInPersistentContext] Called with ${steps.length} steps, mode=${options.mode}, originalScript=${!!options.originalScript}, tempDir=${!!options.tempDir}, sharedContext=${!!sharedContext}, runPomDenormalized=${!!options.runPomDenormalized}`);
649
650
  const { expect, test } = require('@playwright/test');
650
651
  const { ai } = require('ai-wright');
651
652
  // Use shared context if provided, otherwise create new context
652
653
  const persistentContext = sharedContext || new PersistentExecutionContext(page, expect, test, ai, browser, browserContext, options.tempDir);
653
654
  const isSharedContext = !!sharedContext;
655
+ const resolvedTestFileDir = (() => {
656
+ if (!options.tempDir) {
657
+ return undefined;
658
+ }
659
+ if (options.testFolderPath && options.testFolderPath.length > 0) {
660
+ return path.join(options.tempDir, ...options.testFolderPath);
661
+ }
662
+ const defaultDir = path.join(options.tempDir, 'tests');
663
+ try {
664
+ const testsDir = defaultDir;
665
+ const fs = require('fs');
666
+ if (!fs.existsSync(testsDir)) {
667
+ this.log(`Tests directory ${testsDir} does not exist, using default for module resolution`, 'warn');
668
+ return defaultDir;
669
+ }
670
+ const findTestFile = (dir, depth = 0, maxDepth = 10) => {
671
+ if (depth > maxDepth) {
672
+ return null;
673
+ }
674
+ try {
675
+ const files = fs.readdirSync(dir);
676
+ for (const file of files) {
677
+ const fullPath = path.join(dir, file);
678
+ try {
679
+ const stat = fs.statSync(fullPath);
680
+ if (stat.isDirectory()) {
681
+ const found = findTestFile(fullPath, depth + 1, maxDepth);
682
+ if (found)
683
+ return found;
684
+ }
685
+ else if (file.endsWith('.spec.ts') || file.endsWith('.spec.js') ||
686
+ file.endsWith('.test.ts') || file.endsWith('.test.js')) {
687
+ return fullPath;
688
+ }
689
+ }
690
+ catch (statError) {
691
+ continue;
692
+ }
693
+ }
694
+ }
695
+ catch (e) {
696
+ return null;
697
+ }
698
+ return null;
699
+ };
700
+ const testFilePath = findTestFile(testsDir);
701
+ if (testFilePath) {
702
+ const testFileDir = path.dirname(testFilePath);
703
+ this.log(`Found test file at ${testFilePath}, using directory ${testFileDir} for module resolution`);
704
+ return testFileDir;
705
+ }
706
+ this.log(`No test file found in ${testsDir}, using tests directory for module resolution`, 'warn');
707
+ return testsDir;
708
+ }
709
+ catch (searchError) {
710
+ this.log(`Could not search for test file: ${searchError.message}, using tests directory for module resolution`, 'warn');
711
+ return defaultDir;
712
+ }
713
+ })();
654
714
  // Extract and execute imports from original script if provided
655
715
  this.log(`[executeStepsInPersistentContext] Checking imports: originalScript=${!!options.originalScript}, tempDir=${!!options.tempDir}`);
656
716
  if (options.originalScript && options.tempDir) {
@@ -664,60 +724,7 @@ class ExecutionService {
664
724
  // Execute imports with proper context - find the actual test file path
665
725
  // All tests are within the tests folder
666
726
  // We search for test files in tempDir/tests directory tree
667
- let testFileDir = path.join(options.tempDir, 'tests'); // Default to tests directory
668
- try {
669
- const fs = require('fs');
670
- const testsDir = path.join(options.tempDir, 'tests');
671
- // Check if tests directory exists
672
- if (!fs.existsSync(testsDir)) {
673
- this.log(`Tests directory ${testsDir} does not exist, using default for module resolution`, 'warn');
674
- }
675
- else {
676
- // Recursive search with depth limit to prevent infinite loops
677
- const findTestFile = (dir, depth = 0, maxDepth = 10) => {
678
- if (depth > maxDepth) {
679
- return null; // Prevent infinite recursion
680
- }
681
- try {
682
- const files = fs.readdirSync(dir);
683
- for (const file of files) {
684
- const fullPath = path.join(dir, file);
685
- try {
686
- const stat = fs.statSync(fullPath);
687
- if (stat.isDirectory()) {
688
- const found = findTestFile(fullPath, depth + 1, maxDepth);
689
- if (found)
690
- return found;
691
- }
692
- else if (file.endsWith('.spec.ts') || file.endsWith('.spec.js') ||
693
- file.endsWith('.test.ts') || file.endsWith('.test.js')) {
694
- return fullPath; // Found a test file
695
- }
696
- }
697
- catch (statError) {
698
- // Skip files we can't stat
699
- continue;
700
- }
701
- }
702
- }
703
- catch (e) {
704
- // Continue searching in other directories
705
- }
706
- return null;
707
- };
708
- const testFilePath = findTestFile(testsDir);
709
- if (testFilePath) {
710
- testFileDir = path.dirname(testFilePath);
711
- this.log(`Found test file at ${testFilePath}, using directory ${testFileDir} for module resolution`);
712
- }
713
- else {
714
- this.log(`No test file found in ${testsDir}, using tests directory for module resolution`, 'warn');
715
- }
716
- }
717
- }
718
- catch (searchError) {
719
- this.log(`Could not search for test file: ${searchError.message}, using tests directory for module resolution`, 'warn');
720
- }
727
+ const testFileDir = resolvedTestFileDir || path.join(options.tempDir, 'tests');
721
728
  // Store test file directory in context for module resolution
722
729
  persistentContext.setTestFileDir(testFileDir);
723
730
  this.log(`Module resolution context: testFileDir=${testFileDir}, tempDir=${options.tempDir}`);
@@ -963,6 +970,78 @@ class ExecutionService {
963
970
  }
964
971
  return { success: false, error: 'No script or steps provided for execution' };
965
972
  }
973
+ if (options.runPomDenormalized && options.originalScript) {
974
+ const originalCount = allStatements.length;
975
+ const originalStepIds = allStatements
976
+ .map((stmt, idx) => ({ idx, stepId: stmt.stepId, code: stmt.code.substring(0, 50) }))
977
+ .filter(s => s.stepId);
978
+ this.log(`[executeStepsInPersistentContext] Starting POM denormalization: ${originalCount} original statements, ` +
979
+ `${originalStepIds.length} with stepIds`);
980
+ // Convert to ScriptStep[] for denormalization
981
+ const scriptSteps = allStatements.map(stmt => ({
982
+ code: stmt.code,
983
+ description: stmt.intentComment || '',
984
+ id: stmt.stepId, // stepId may not exist for InitializationCodeUtils results
985
+ isVariableDeclaration: stmt.isVariableDeclaration
986
+ }));
987
+ // Denormalize POM calls
988
+ const denormalizedSteps = pom_denormalizer_1.PomDenormalizer.denormalizePomSteps(scriptSteps, {
989
+ originalScript: options.originalScript,
990
+ tempDir: options.tempDir || '',
991
+ testFileDir: resolvedTestFileDir,
992
+ logger: (msg, level) => this.log(msg, level)
993
+ });
994
+ const denormalizedCount = denormalizedSteps.length;
995
+ const denormalizedStepIds = denormalizedSteps
996
+ .map((step, idx) => ({ idx, stepId: step.id, isVar: step.isVariableDeclaration, code: step.code.substring(0, 50) }))
997
+ .filter(s => s.stepId && !s.isVar);
998
+ this.log(`[executeStepsInPersistentContext] Denormalization complete: ${originalCount} -> ${denormalizedCount} statements ` +
999
+ `(${denormalizedCount - originalCount > 0 ? '+' : ''}${denormalizedCount - originalCount}), ` +
1000
+ `${denormalizedStepIds.length} non-variable steps with stepIds`);
1001
+ if (denormalizedStepIds.length > 0 && this.log) {
1002
+ const stepIdPreview = denormalizedStepIds.slice(0, 5).map(s => `idx=${s.idx} stepId=${s.stepId?.substring(0, 16)}...`).join(', ');
1003
+ this.log(`[executeStepsInPersistentContext] Sample denormalized stepIds: ${stepIdPreview}${denormalizedStepIds.length > 5 ? '...' : ''}`);
1004
+ }
1005
+ // Convert back to inline type format
1006
+ allStatements = denormalizedSteps.map(step => ({
1007
+ code: step.code,
1008
+ isVariableDeclaration: step.isVariableDeclaration || false,
1009
+ intentComment: step.description !== step.code ? step.description : undefined,
1010
+ screenStateAnnotation: undefined,
1011
+ stepId: step.id
1012
+ }));
1013
+ // Verify stepId preservation
1014
+ const preservedStepIds = allStatements
1015
+ .map((stmt, idx) => ({ idx, stepId: stmt.stepId, isVar: stmt.isVariableDeclaration }))
1016
+ .filter(s => s.stepId && !s.isVar);
1017
+ this.log(`[executeStepsInPersistentContext] StepId preservation: ${preservedStepIds.length} non-variable statements have stepIds ` +
1018
+ `(expected: ${denormalizedStepIds.length})`);
1019
+ // Verify stepId matching between denormalized and preserved
1020
+ if (preservedStepIds.length !== denormalizedStepIds.length) {
1021
+ this.log(`[executeStepsInPersistentContext] WARNING: StepId count mismatch! ` +
1022
+ `Denormalized: ${denormalizedStepIds.length}, Preserved: ${preservedStepIds.length}`, 'warn');
1023
+ }
1024
+ else {
1025
+ // Check if stepIds match
1026
+ let matchCount = 0;
1027
+ for (let i = 0; i < Math.min(denormalizedStepIds.length, preservedStepIds.length); i++) {
1028
+ const denorm = denormalizedStepIds[i];
1029
+ const preserved = preservedStepIds.find(p => p.idx === denorm.idx);
1030
+ if (preserved && preserved.stepId === denorm.stepId) {
1031
+ matchCount++;
1032
+ }
1033
+ }
1034
+ this.log(`[executeStepsInPersistentContext] StepId matching: ${matchCount}/${Math.min(denormalizedStepIds.length, preservedStepIds.length)} stepIds match`);
1035
+ }
1036
+ }
1037
+ else {
1038
+ if (!options.runPomDenormalized) {
1039
+ this.log(`[executeStepsInPersistentContext] POM denormalization skipped: runPomDenormalized=false`);
1040
+ }
1041
+ else if (!options.originalScript) {
1042
+ this.log(`[executeStepsInPersistentContext] POM denormalization skipped: originalScript not provided`);
1043
+ }
1044
+ }
966
1045
  // Build stepId -> statement index map before execution
967
1046
  const statementIndexByStepId = new Map();
968
1047
  for (let idx = 0; idx < allStatements.length; idx++) {
@@ -1079,14 +1158,6 @@ class ExecutionService {
1079
1158
  let shouldSkipStep = false;
1080
1159
  if (this.progressReporter?.beforeStepStart && options.jobId) {
1081
1160
  const { description, baseStepId, stepId, runNumber } = stepMeta;
1082
- // Log stepId usage for debugging
1083
- if (stmt.stepId) {
1084
- const stepIdPreview = stmt.stepId.length > 16 ? `${stmt.stepId.substring(0, 16)}...` : stmt.stepId;
1085
- this.log(`[stepId] beforeStepStart: Using hash-based stepId: ${stepIdPreview} (stepNumber: ${stepNumber}, run: ${runNumber})`);
1086
- }
1087
- else {
1088
- this.log(`[stepId] beforeStepStart: WARNING: No hash-based stepId, using fallback: ${baseStepId} (stepNumber: ${stepNumber}, run: ${runNumber})`);
1089
- }
1090
1161
  const beforeStepResult = await this.progressReporter.beforeStepStart({
1091
1162
  stepId,
1092
1163
  stepNumber,
@@ -1401,12 +1472,18 @@ class ExecutionService {
1401
1472
  }
1402
1473
  // Execute script in persistent context (STOP on first error)
1403
1474
  // Pre-parsed steps from consumer (if any) will be ignored - AST extracts from script
1475
+ this.log(`[runExactly] Calling executeStepsInPersistentContext (existing browser): ` +
1476
+ `runPomDenormalized=${request.runPomDenormalized}, ` +
1477
+ `originalScript=${!!request.script} (length: ${request.script?.length || 0}), ` +
1478
+ `tempDir=${!!request.tempDir}`);
1404
1479
  const result = await this.executeStepsInPersistentContext(request.steps || [], // Pre-parsed steps (optional, for backward compatibility)
1405
1480
  page, browser, context, {
1406
1481
  mode: 'RUN_EXACTLY',
1407
1482
  jobId: request.jobId,
1408
1483
  tempDir: request.tempDir,
1409
- originalScript: request.script // AST will extract statements from this
1484
+ originalScript: request.script, // AST will extract statements from this
1485
+ testFolderPath: request.testFolderPath,
1486
+ runPomDenormalized: request.runPomDenormalized
1410
1487
  });
1411
1488
  // LIFECYCLE: afterEndTest
1412
1489
  if (this.progressReporter?.afterEndTest) {
@@ -1454,12 +1531,18 @@ class ExecutionService {
1454
1531
  }
1455
1532
  // Execute script in persistent context (STOP on first error)
1456
1533
  // Pre-parsed steps from consumer (if any) will be ignored - AST extracts from script
1534
+ this.log(`[runExactly] Calling executeStepsInPersistentContext (new browser, attempt ${attempt}): ` +
1535
+ `runPomDenormalized=${request.runPomDenormalized}, ` +
1536
+ `originalScript=${!!request.script} (length: ${request.script?.length || 0}), ` +
1537
+ `tempDir=${!!request.tempDir}`);
1457
1538
  const result = await this.executeStepsInPersistentContext(request.steps || [], // Pre-parsed steps (optional, for backward compatibility)
1458
1539
  page, browser, context, {
1459
1540
  mode: 'RUN_EXACTLY',
1460
1541
  jobId: request.jobId,
1461
1542
  tempDir: request.tempDir,
1462
- originalScript: request.script // AST will extract statements from this
1543
+ originalScript: request.script, // AST will extract statements from this
1544
+ testFolderPath: request.testFolderPath,
1545
+ runPomDenormalized: request.runPomDenormalized
1463
1546
  });
1464
1547
  // LIFECYCLE: afterEndTest
1465
1548
  if (this.progressReporter?.afterEndTest) {
@@ -1575,7 +1658,9 @@ class ExecutionService {
1575
1658
  jobId: request.jobId,
1576
1659
  model,
1577
1660
  tempDir: request.tempDir,
1578
- originalScript: request.script // Pass original script for import extraction
1661
+ originalScript: request.script, // Pass original script for import extraction
1662
+ testFolderPath: request.testFolderPath,
1663
+ runPomDenormalized: request.runPomDenormalized
1579
1664
  });
1580
1665
  const updatedSteps = result.updatedSteps || steps;
1581
1666
  const allStepsSuccessful = result.success;
@@ -1886,7 +1971,7 @@ class ExecutionService {
1886
1971
  this.log(`Executing ${flattened.fileLevelHooks.beforeAll.length} file-level beforeAll hook(s)...`);
1887
1972
  try {
1888
1973
  for (const hook of flattened.fileLevelHooks.beforeAll) {
1889
- await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, fileLevelContext || undefined, hook);
1974
+ await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, fileLevelContext || undefined, hook);
1890
1975
  }
1891
1976
  this.log('File-level beforeAll hooks executed successfully');
1892
1977
  }
@@ -1957,7 +2042,7 @@ class ExecutionService {
1957
2042
  // Get or create suite context (includes file variables + suite variables)
1958
2043
  const suiteContext = await getOrCreateSuiteContext(suitePath, suite.suiteVariables, flattened.fileVariables);
1959
2044
  for (const hook of suite.beforeAll) {
1960
- await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, suiteContext, hook);
2045
+ await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, suiteContext, hook);
1961
2046
  }
1962
2047
  suiteBeforeAllExecuted.add(suiteKey);
1963
2048
  this.log(`Suite-level beforeAll hooks executed for suite: ${suiteDisplayName}`);
@@ -1982,7 +2067,7 @@ class ExecutionService {
1982
2067
  this.log(`Executing ${flattened.fileLevelHooks.beforeEach.length} file-level beforeEach hook(s) for test: ${test.fullName}`);
1983
2068
  try {
1984
2069
  for (const hook of flattened.fileLevelHooks.beforeEach) {
1985
- const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, fileLevelContext || undefined, hook, jobId, testStepCounter);
2070
+ const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, fileLevelContext || undefined, hook, jobId, testStepCounter);
1986
2071
  testStepCounter += stepsExecuted;
1987
2072
  }
1988
2073
  }
@@ -2000,7 +2085,7 @@ class ExecutionService {
2000
2085
  // Get or create suite context for this test's suite
2001
2086
  const suiteContext = await getOrCreateSuiteContext(testData.suitePath, testData.suiteVariables, flattened.fileVariables);
2002
2087
  for (const hook of testData.suiteBeforeEachHooks) {
2003
- const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, suiteContext, hook, jobId, testStepCounter);
2088
+ const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, suiteContext, hook, jobId, testStepCounter);
2004
2089
  testStepCounter += stepsExecuted;
2005
2090
  }
2006
2091
  }
@@ -2061,7 +2146,9 @@ class ExecutionService {
2061
2146
  jobId: jobId,
2062
2147
  tempDir: request.tempDir,
2063
2148
  originalScript: testScript, // For module resolution (imports)
2064
- model: request.model
2149
+ model: request.model,
2150
+ testFolderPath: request.testFolderPath,
2151
+ runPomDenormalized: request.runPomDenormalized
2065
2152
  }, suiteContext // Pass shared context
2066
2153
  );
2067
2154
  if (!result.success) {
@@ -2110,7 +2197,7 @@ class ExecutionService {
2110
2197
  // Get or create suite context for this test's suite
2111
2198
  const suiteContext = await getOrCreateSuiteContext(testData.suitePath, testData.suiteVariables, flattened.fileVariables);
2112
2199
  for (const hook of testData.suiteAfterEachHooks) {
2113
- const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, suiteContext, hook, jobId, testStepCounter);
2200
+ const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, suiteContext, hook, jobId, testStepCounter);
2114
2201
  testStepCounter += stepsExecuted;
2115
2202
  }
2116
2203
  }
@@ -2125,7 +2212,7 @@ class ExecutionService {
2125
2212
  this.log(`Executing ${flattened.fileLevelHooks.afterEach.length} file-level afterEach hook(s) for test: ${test.fullName}`);
2126
2213
  try {
2127
2214
  for (const hook of flattened.fileLevelHooks.afterEach) {
2128
- const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, fileLevelContext || undefined, hook, jobId, testStepCounter);
2215
+ const stepsExecuted = await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, fileLevelContext || undefined, hook, jobId, testStepCounter);
2129
2216
  testStepCounter += stepsExecuted;
2130
2217
  }
2131
2218
  }
@@ -2164,7 +2251,7 @@ class ExecutionService {
2164
2251
  // Get or create suite context (should already exist, but safe to call)
2165
2252
  const suiteContext = await getOrCreateSuiteContext(suitePath, suite.suiteVariables, flattened.fileVariables);
2166
2253
  for (const hook of suite.afterAll) {
2167
- await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, suiteContext, hook);
2254
+ await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, suiteContext, hook);
2168
2255
  }
2169
2256
  this.log(`Suite-level afterAll hooks executed for suite: ${suiteDisplayName}`);
2170
2257
  }
@@ -2229,7 +2316,7 @@ class ExecutionService {
2229
2316
  this.log(`Executing ${flattened.fileLevelHooks.afterAll.length} file-level afterAll hook(s)...`);
2230
2317
  try {
2231
2318
  for (const hook of flattened.fileLevelHooks.afterAll) {
2232
- await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, fileLevelContext || undefined, hook);
2319
+ await this.executeHook(hook.code, page, context, browser, request.tempDir, originalScript, request.testFolderPath, request.runPomDenormalized, fileLevelContext || undefined, hook);
2233
2320
  }
2234
2321
  this.log('File-level afterAll hooks executed successfully');
2235
2322
  }
@@ -2295,7 +2382,7 @@ class ExecutionService {
2295
2382
  * If jobId is provided, executes step-wise with callbacks for step reporting
2296
2383
  * @returns Number of non-variable steps executed (for step counter tracking)
2297
2384
  */
2298
- async executeHook(hookCode, page, context, browser, tempDir, originalScript, sharedContext, hook, jobId, stepIdOffset = 0) {
2385
+ async executeHook(hookCode, page, context, browser, tempDir, originalScript, testFolderPath, runPomDenormalized, sharedContext, hook, jobId, stepIdOffset = 0) {
2299
2386
  const { expect, test } = require('@playwright/test');
2300
2387
  const { ai } = require('ai-wright');
2301
2388
  // Construct hook script with imports from original script (for module resolution)
@@ -2334,7 +2421,9 @@ class ExecutionService {
2334
2421
  mode: types_1.ExecutionMode.RUN_EXACTLY,
2335
2422
  jobId: jobId,
2336
2423
  tempDir: tempDir,
2337
- originalScript: hookScript
2424
+ originalScript: hookScript,
2425
+ testFolderPath: testFolderPath,
2426
+ runPomDenormalized: runPomDenormalized
2338
2427
  }, hookContext // Pass shared context
2339
2428
  );
2340
2429
  if (!result.success) {