@@ -339,6 +339,73 @@ def _create_codeflash_jest_config(
339339 return None
340340
341341
342+ def _create_runtime_jest_config (base_config_path : Path | None , project_root : Path , test_dirs : set [str ]) -> Path | None :
343+ """Create a runtime Jest config that includes test directories in roots and testMatch.
344+
345+ This is needed because test files generated by codeflash may be placed
346+ outside the project root (e.g., in a monorepo where the source file lives
347+ in a subpackage but tests are generated at the repo root). Jest requires
348+ test files to be within configured ``roots`` and to match ``testMatch``
349+ patterns (which typically use ``<rootDir>``). Since ``roots`` set via CLI
350+ can be overridden by config, and ``testMatch`` patterns using ``<rootDir>``
351+ won't match files outside the project root, we must create a wrapper config.
352+
353+ Args:
354+ base_config_path: Path to the base Jest config to extend, or None.
355+ project_root: The project root directory (where package.json lives).
356+ test_dirs: Set of absolute directory paths containing test files.
357+
358+ Returns:
359+ Path to the created runtime config file.
360+
361+ """
362+ is_esm = _is_esm_project (project_root )
363+ config_ext = ".cjs" if is_esm else ".js"
364+
365+ if base_config_path :
366+ config_dir = base_config_path .parent
367+ else :
368+ config_dir = project_root
369+
370+ runtime_config_path = config_dir / f"jest.codeflash.runtime.config{ config_ext } "
371+
372+ test_dirs_js = ", " .join (f"'{ d } '" for d in sorted (test_dirs ))
373+
374+ if base_config_path :
375+ require_path = f"./{ base_config_path .name } "
376+ config_content = f"""// Auto-generated by codeflash - runtime config with test roots
377+ const baseConfig = require('{ require_path } ');
378+ module.exports = {{
379+ ...baseConfig,
380+ roots: [
381+ ...(baseConfig.roots || [__dirname]),
382+ { test_dirs_js } ,
383+ ],
384+ testMatch: ['**/*.test.ts', '**/*.test.js', '**/*.test.tsx', '**/*.test.jsx'],
385+ }};
386+ """
387+ else :
388+ config_content = f"""// Auto-generated by codeflash - runtime config with test roots
389+ module.exports = {{
390+ roots: ['{ project_root } ', { test_dirs_js } ],
391+ testMatch: ['**/*.test.ts', '**/*.test.js', '**/*.test.tsx', '**/*.test.jsx'],
392+ }};
393+ """
394+
395+ try :
396+ runtime_config_path .write_text (config_content , encoding = "utf-8" )
397+ _created_config_files .add (runtime_config_path )
398+ logger .debug (f"Created runtime Jest config with test roots: { runtime_config_path } " )
399+ except Exception as e :
400+ logger .warning (f"Failed to create runtime Jest config: { e } " )
401+ # Fall back to base config
402+ if base_config_path :
403+ return base_config_path
404+ return None
405+
406+ return runtime_config_path
407+
408+
342409def _get_jest_config_for_project (project_root : Path ) -> Path | None :
343410 """Get the appropriate Jest config for the project.
344411
@@ -712,6 +779,17 @@ def run_jest_behavioral_tests(
712779 # Add Jest config if found - needed for TypeScript transformation
713780 # Uses codeflash-compatible config if project has bundler moduleResolution
714781 jest_config = _get_jest_config_for_project (effective_cwd )
782+
783+ # If test files are outside the project root, create a runtime wrapper config
784+ # that adds their directories to Jest's `roots` and overrides `testMatch`.
785+ # This is necessary because Jest's testMatch patterns use <rootDir> which
786+ # resolves to the config file's directory, excluding external test files.
787+ if test_files :
788+ resolved_root = effective_cwd .resolve ()
789+ test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
790+ if any (not Path (d ).is_relative_to (resolved_root ) for d in test_dirs ):
791+ jest_config = _create_runtime_jest_config (jest_config , effective_cwd , test_dirs )
792+
715793 if jest_config :
716794 jest_cmd .append (f"--config={ jest_config } " )
717795
@@ -723,12 +801,6 @@ def run_jest_behavioral_tests(
723801 jest_cmd .append ("--runTestsByPath" )
724802 resolved_test_files = [str (Path (f ).resolve ()) for f in test_files ]
725803 jest_cmd .extend (resolved_test_files )
726- # Add --roots to include directories containing test files
727- # This is needed because some projects configure Jest with restricted roots
728- # (e.g., roots: ["<rootDir>/src"]) which excludes the test directory
729- test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
730- for test_dir in sorted (test_dirs ):
731- jest_cmd .extend (["--roots" , test_dir ])
732804
733805 if timeout :
734806 jest_cmd .append (f"--testTimeout={ timeout * 1000 } " ) # Jest uses milliseconds
@@ -962,17 +1034,21 @@ def run_jest_benchmarking_tests(
9621034 # Add Jest config if found - needed for TypeScript transformation
9631035 # Uses codeflash-compatible config if project has bundler moduleResolution
9641036 jest_config = _get_jest_config_for_project (effective_cwd )
1037+
1038+ # If test files are outside the project root, create a runtime wrapper config
1039+ if test_files :
1040+ resolved_root = effective_cwd .resolve ()
1041+ test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
1042+ if any (not Path (d ).is_relative_to (resolved_root ) for d in test_dirs ):
1043+ jest_config = _create_runtime_jest_config (jest_config , effective_cwd , test_dirs )
1044+
9651045 if jest_config :
9661046 jest_cmd .append (f"--config={ jest_config } " )
9671047
9681048 if test_files :
9691049 jest_cmd .append ("--runTestsByPath" )
9701050 resolved_test_files = [str (Path (f ).resolve ()) for f in test_files ]
9711051 jest_cmd .extend (resolved_test_files )
972- # Add --roots to include directories containing test files
973- test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
974- for test_dir in sorted (test_dirs ):
975- jest_cmd .extend (["--roots" , test_dir ])
9761052
9771053 if timeout :
9781054 jest_cmd .append (f"--testTimeout={ timeout * 1000 } " )
@@ -1127,17 +1203,21 @@ def run_jest_line_profile_tests(
11271203 # Add Jest config if found - needed for TypeScript transformation
11281204 # Uses codeflash-compatible config if project has bundler moduleResolution
11291205 jest_config = _get_jest_config_for_project (effective_cwd )
1206+
1207+ # If test files are outside the project root, create a runtime wrapper config
1208+ if test_files :
1209+ resolved_root = effective_cwd .resolve ()
1210+ test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
1211+ if any (not Path (d ).is_relative_to (resolved_root ) for d in test_dirs ):
1212+ jest_config = _create_runtime_jest_config (jest_config , effective_cwd , test_dirs )
1213+
11301214 if jest_config :
11311215 jest_cmd .append (f"--config={ jest_config } " )
11321216
11331217 if test_files :
11341218 jest_cmd .append ("--runTestsByPath" )
11351219 resolved_test_files = [str (Path (f ).resolve ()) for f in test_files ]
11361220 jest_cmd .extend (resolved_test_files )
1137- # Add --roots to include directories containing test files
1138- test_dirs = {str (Path (f ).resolve ().parent ) for f in test_files }
1139- for test_dir in sorted (test_dirs ):
1140- jest_cmd .extend (["--roots" , test_dir ])
11411221
11421222 if timeout :
11431223 jest_cmd .append (f"--testTimeout={ timeout * 1000 } " )
0 commit comments