From 2e77f85834c6bb0bd39190b568f415a252bf0b95 Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 14:31:08 +0200 Subject: [PATCH 1/8] fix: resolve jest-runner from project's node_modules for Jest 30 compatibility The loop-runner was loading jest-runner from codeflash's node_modules (v29) instead of the project's (v30), causing "runtime.enterTestCode is not a function" errors. This fix: - Adds recursive search to find jest-runner in any node_modules structure - Works with npm, yarn, and pnpm (including non-hoisted deps) - Prefers higher versions when multiple are found - Removes internal looping in capturePerf when using external loop-runner - Creates fresh TestRunner per batch to avoid Jest 30 state corruption Co-Authored-By: Claude Opus 4.5 --- codeflash/languages/javascript/parse.py | 5 - codeflash/languages/javascript/test_runner.py | 2 - packages/codeflash/runtime/capture.js | 29 +-- packages/codeflash/runtime/loop-runner.js | 199 +++++++++++------- 4 files changed, 135 insertions(+), 100 deletions(-) diff --git a/codeflash/languages/javascript/parse.py b/codeflash/languages/javascript/parse.py index a5e7ae8c6..e3eee4831 100644 --- a/codeflash/languages/javascript/parse.py +++ b/codeflash/languages/javascript/parse.py @@ -527,10 +527,5 @@ def parse_jest_test_xml( f"[LOOP-SUMMARY] Results loop_index: min={min_idx}, max={max_idx}, " f"unique_count={len(unique_loop_indices)}, total_results={len(loop_indices)}" ) - if max_idx == 1 and len(loop_indices) > 1: - logger.warning( - f"[LOOP-WARNING] All {len(loop_indices)} results have loop_index=1. " - "Perf test markers may not have been parsed correctly." - ) return test_results diff --git a/codeflash/languages/javascript/test_runner.py b/codeflash/languages/javascript/test_runner.py index 1d79ad382..bcc3a74de 100644 --- a/codeflash/languages/javascript/test_runner.py +++ b/codeflash/languages/javascript/test_runner.py @@ -803,8 +803,6 @@ def run_jest_behavioral_tests( wall_clock_ns = time.perf_counter_ns() - start_time_ns logger.debug(f"Jest behavioral tests completed in {wall_clock_ns / 1e9:.2f}s") - print(result.stdout) - return result_file_path, result, coverage_json_path, None diff --git a/packages/codeflash/runtime/capture.js b/packages/codeflash/runtime/capture.js index 0fdcc5784..4ff9623fc 100644 --- a/packages/codeflash/runtime/capture.js +++ b/packages/codeflash/runtime/capture.js @@ -113,21 +113,26 @@ function checkSharedTimeLimit() { /** * Get the current loop index for a specific invocation. - * The loop index represents how many times ALL test files have been run through. - * This is the batch count from the loop-runner. + * When using external loop-runner (Jest), returns the batch number directly. + * When using internal looping (Vitest), tracks and returns the invocation count. + * * @param {string} invocationKey - Unique key for this test invocation - * @returns {number} The current batch number (loop index) + * @returns {number} The loop index for timing markers (1-based) */ function getInvocationLoopIndex(invocationKey) { - // Track local loop count for stopping logic (increments on each call) + // When using external loop-runner, use the batch number directly + // This is reliable because Jest resets module state between batches + const currentBatch = process.env.CODEFLASH_PERF_CURRENT_BATCH; + if (currentBatch !== undefined) { + return parseInt(currentBatch, 10); + } + + // For internal looping (Vitest), track the count locally if (!sharedPerfState.invocationLoopCounts[invocationKey]) { sharedPerfState.invocationLoopCounts[invocationKey] = 0; } ++sharedPerfState.invocationLoopCounts[invocationKey]; - - // Return the batch number as the loop index for timing markers - // This represents how many times all test files have been run through - return parseInt(process.env.CODEFLASH_PERF_CURRENT_BATCH || '1', 10); + return sharedPerfState.invocationLoopCounts[invocationKey]; } /** @@ -693,11 +698,9 @@ function capturePerf(funcName, lineId, fn, ...args) { // If not set, we're in Vitest mode and need to do all loops internally const hasExternalLoopRunner = process.env.CODEFLASH_PERF_CURRENT_BATCH !== undefined; - // Batched looping: run BATCH_SIZE loops per capturePerf call when using loop-runner + // When using external loop-runner (Jest), execute only once per call - the loop-runner handles batching // For Vitest (no loop-runner), do all loops internally in a single call - const batchSize = shouldLoop - ? (hasExternalLoopRunner ? getPerfBatchSize() : getPerfLoopCount()) - : 1; + const batchSize = hasExternalLoopRunner ? 1 : (shouldLoop ? getPerfLoopCount() : 1); // Initialize runtime tracking for this invocation if needed if (!sharedPerfState.invocationRuntimes[invocationKey]) { @@ -719,7 +722,7 @@ function capturePerf(funcName, lineId, fn, ...args) { break; } - // Get the loop index (batch number) for timing markers + // Get the loop index for timing markers const loopIndex = getInvocationLoopIndex(invocationKey); // Check if we've exceeded max loops for this invocation diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index c6d476f1f..1cd0803c9 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -35,69 +35,113 @@ const path = require('path'); const fs = require('fs'); /** - * Validates that a jest-runner path is valid by checking for package.json. - * @param {string} jestRunnerPath - Path to check - * @returns {boolean} True if valid jest-runner package + * Recursively find jest-runner package in node_modules. + * Works with any package manager (npm, yarn, pnpm) by searching for + * jest-runner/package.json anywhere in the tree. + * + * @param {string} nodeModulesPath - Path to node_modules directory + * @param {number} maxDepth - Maximum recursion depth (default: 5) + * @returns {string|null} Path to jest-runner or null if not found */ -function isValidJestRunnerPath(jestRunnerPath) { - if (!fs.existsSync(jestRunnerPath)) { - return false; +function findJestRunnerRecursive(nodeModulesPath, maxDepth = 5) { + function search(dir, depth) { + if (depth > maxDepth || !fs.existsSync(dir)) return null; + + try { + let entries = fs.readdirSync(dir, { withFileTypes: true }); + + // Sort entries: prefer higher versions for jest-runner@X.Y.Z directories + entries = entries.slice().sort((a, b) => { + const aMatch = a.name.match(/^jest-runner@(\d+)/); + const bMatch = b.name.match(/^jest-runner@(\d+)/); + if (aMatch && bMatch) { + return parseInt(bMatch[1], 10) - parseInt(aMatch[1], 10); + } + return a.name.localeCompare(b.name); + }); + + for (const entry of entries) { + if (!entry.isDirectory()) continue; + + const entryPath = path.join(dir, entry.name); + + // Found jest-runner directory - check if it's a valid package + if (entry.name === 'jest-runner') { + const pkgJsonPath = path.join(entryPath, 'package.json'); + if (fs.existsSync(pkgJsonPath)) { + try { + const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); + if (pkgJson.name === 'jest-runner') { + return entryPath; + } + } catch (e) { + // Ignore JSON parse errors + } + } + } + + // Recurse into: + // - node_modules subdirectories + // - scoped packages (@org/pkg) + // - hidden directories (.pnpm, .yarn, etc.) + // - pnpm versioned directories (jest-runner@30.0.5) + const shouldRecurse = entry.name === 'node_modules' || + entry.name.startsWith('@') || + entry.name.startsWith('.') || + entry.name.startsWith('jest-runner@'); + + if (shouldRecurse) { + const result = search(entryPath, depth + 1); + if (result) return result; + } + } + } catch (e) { + // Ignore permission errors + } + + return null; } - const packageJsonPath = path.join(jestRunnerPath, 'package.json'); - return fs.existsSync(packageJsonPath); + + return search(nodeModulesPath, 0); } /** - * Resolve jest-runner with monorepo support. - * Uses CODEFLASH_MONOREPO_ROOT environment variable if available, - * otherwise walks up the directory tree looking for node_modules/jest-runner. + * Resolve jest-runner from the PROJECT's node_modules (not codeflash's). + * + * Uses recursive search to find jest-runner anywhere in node_modules, + * working with any package manager (npm, yarn, pnpm). * * @returns {string} Path to jest-runner package * @throws {Error} If jest-runner cannot be found */ function resolveJestRunner() { - // Try standard resolution first (works in simple projects) - try { - return require.resolve('jest-runner'); - } catch (e) { - // Standard resolution failed - try monorepo-aware resolution - } + const monorepoMarkers = ['yarn.lock', 'pnpm-workspace.yaml', 'lerna.json', 'package-lock.json']; + + // Walk up from cwd to find all potential node_modules locations + let currentDir = process.cwd(); + const visitedDirs = new Set(); // If Python detected a monorepo root, check there first const monorepoRoot = process.env.CODEFLASH_MONOREPO_ROOT; - if (monorepoRoot) { - const jestRunnerPath = path.join(monorepoRoot, 'node_modules', 'jest-runner'); - if (isValidJestRunnerPath(jestRunnerPath)) { - return jestRunnerPath; - } + if (monorepoRoot && !visitedDirs.has(monorepoRoot)) { + visitedDirs.add(monorepoRoot); + const result = findJestRunnerRecursive(path.join(monorepoRoot, 'node_modules')); + if (result) return result; } - // Fallback: Walk up from cwd looking for node_modules/jest-runner - const monorepoMarkers = ['yarn.lock', 'pnpm-workspace.yaml', 'lerna.json', 'package-lock.json']; - let currentDir = process.cwd(); - const visitedDirs = new Set(); - while (currentDir !== path.dirname(currentDir)) { - // Avoid infinite loops if (visitedDirs.has(currentDir)) break; visitedDirs.add(currentDir); - // Try node_modules/jest-runner at this level - const jestRunnerPath = path.join(currentDir, 'node_modules', 'jest-runner'); - if (isValidJestRunnerPath(jestRunnerPath)) { - return jestRunnerPath; - } + const result = findJestRunnerRecursive(path.join(currentDir, 'node_modules')); + if (result) return result; - // Check if this is a workspace root (has monorepo markers) + // Check if this is a workspace root - stop after this const isWorkspaceRoot = monorepoMarkers.some(marker => fs.existsSync(path.join(currentDir, marker)) ); - if (isWorkspaceRoot) { - // Found workspace root but no jest-runner - stop searching - break; - } - + if (isWorkspaceRoot) break; currentDir = path.dirname(currentDir); } @@ -120,10 +164,15 @@ let jestVersion = 0; try { const jestRunnerPath = resolveJestRunner(); - const internalRequire = createRequire(jestRunnerPath); - // Try to get the TestRunner class (Jest 30+) - const jestRunner = internalRequire(jestRunnerPath); + // Read the package.json to find the actual entry point and version + const pkgJsonPath = path.join(jestRunnerPath, 'package.json'); + const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); + + // Require using the full path to the entry point + const entryPoint = path.join(jestRunnerPath, pkgJson.main || 'build/index.js'); + const jestRunner = require(entryPoint); + TestRunner = jestRunner.default || jestRunner.TestRunner; if (TestRunner && TestRunner.prototype && typeof TestRunner.prototype.runTests === 'function') { @@ -131,9 +180,11 @@ try { jestVersion = 30; jestRunnerAvailable = true; } else { - // Try Jest 29 style import + // Try Jest 29 style import - runTest is in build/runTest.js try { - runTest = internalRequire('./runTest').default; + const runTestPath = path.join(jestRunnerPath, 'build', 'runTest.js'); + const runTestModule = require(runTestPath); + runTest = runTestModule.default; if (typeof runTest === 'function') { // Jest 29 - use direct runTest function jestVersion = 29; @@ -141,10 +192,6 @@ try { } } catch (e29) { // Neither Jest 29 nor 30 style import worked - const errorMsg = `Found jest-runner at ${jestRunnerPath} but could not load it. ` + - `This may indicate an unsupported Jest version. ` + - `Supported versions: Jest 29.x and Jest 30.x`; - console.error(errorMsg); jestRunnerAvailable = false; } } @@ -233,15 +280,12 @@ class CodeflashLoopRunner { this._context = context || {}; this._eventEmitter = new SimpleEventEmitter(); - // For Jest 30+, create an instance of the base TestRunner for delegation - if (jestVersion >= 30) { - if (!TestRunner) { - throw new Error( - `Jest ${jestVersion} detected but TestRunner class not available. ` + - `This indicates an internal error in loop-runner initialization.` - ); - } - this._baseRunner = new TestRunner(globalConfig, context); + // For Jest 30+, verify TestRunner is available (we create fresh instances per batch) + if (jestVersion >= 30 && !TestRunner) { + throw new Error( + `Jest ${jestVersion} detected but TestRunner class not available. ` + + `This indicates an internal error in loop-runner initialization.` + ); } } @@ -270,7 +314,7 @@ class CodeflashLoopRunner { * @param {Object} options - Jest runner options * @returns {Promise} */ - async runTests(tests, watcher, options) { + async runTests(tests, watcher, ...rest) { const startTime = Date.now(); let batchCount = 0; let hasFailure = false; @@ -289,13 +333,11 @@ class CodeflashLoopRunner { // Check time limit BEFORE each batch if (batchCount > MIN_BATCHES && checkTimeLimit()) { - console.log(`[codeflash] Time limit reached after ${batchCount - 1} batches (${Date.now() - startTime}ms elapsed)`); break; } // Check if interrupted if (watcher.isInterrupted()) { - console.log(`[codeflash] Watcher is interrupted`) break; } @@ -303,57 +345,54 @@ class CodeflashLoopRunner { process.env.CODEFLASH_PERF_CURRENT_BATCH = String(batchCount); // Run all test files in this batch - const batchResult = await this._runAllTestsOnce(tests, watcher, options); + const batchResult = await this._runAllTestsOnce(tests, watcher, ...rest); allConsoleOutput += batchResult.consoleOutput; - // if (batchResult.hasFailure) { - // hasFailure = true; - // break; - // } - // Check time limit AFTER each batch if (checkTimeLimit()) { - console.log(`[codeflash] Time limit reached after ${batchCount} batches (${Date.now() - startTime}ms elapsed)`); break; } } const totalTimeMs = Date.now() - startTime; - console.log(`[codeflash] now: ${Date.now()}`) // Output all collected console logs - this is critical for timing marker extraction // The console output contains the !######...######! timing markers from capturePerf if (allConsoleOutput) { process.stdout.write(allConsoleOutput); } - - console.log(`[codeflash] Batched runner completed: ${batchCount} batches, ${tests.length} test files, ${totalTimeMs}ms total`); } /** * Run all test files once (one batch). * Uses different approaches for Jest 29 vs Jest 30. */ - async _runAllTestsOnce(tests, watcher, options) { + async _runAllTestsOnce(tests, watcher, ...args) { if (jestVersion >= 30) { - return this._runAllTestsOnceJest30(tests, watcher, options); + return this._runAllTestsOnceJest30(tests, watcher, ...args); } else { return this._runAllTestsOnceJest29(tests, watcher); } } /** - * Jest 30+ implementation - delegates to base TestRunner and collects results. + * Jest 30+ implementation - creates a fresh TestRunner for each batch to avoid + * state corruption issues that occur when reusing runners across batches. */ - async _runAllTestsOnceJest30(tests, watcher, options) { + async _runAllTestsOnceJest30(tests, watcher, ...args) { let hasFailure = false; let allConsoleOutput = ''; // For Jest 30, we need to collect results through event listeners const resultsCollector = []; - // Subscribe to events from the base runner - const unsubscribeSuccess = this._baseRunner.on('test-file-success', (testData) => { + // Create a FRESH TestRunner instance for each batch + // Jest 30's TestRunner corrupts its internal state after running tests, + // so we cannot reuse the same instance across multiple batches + const batchRunner = new TestRunner(this._globalConfig, this._context); + + // Subscribe to events from the batch runner + const unsubscribeSuccess = batchRunner.on('test-file-success', (testData) => { const [test, result] = testData; resultsCollector.push({ test, result, success: true }); @@ -369,7 +408,7 @@ class CodeflashLoopRunner { this._eventEmitter.emit('test-file-success', testData); }); - const unsubscribeFailure = this._baseRunner.on('test-file-failure', (testData) => { + const unsubscribeFailure = batchRunner.on('test-file-failure', (testData) => { const [test, error] = testData; resultsCollector.push({ test, error, success: false }); hasFailure = true; @@ -378,14 +417,14 @@ class CodeflashLoopRunner { this._eventEmitter.emit('test-file-failure', testData); }); - const unsubscribeStart = this._baseRunner.on('test-file-start', (testData) => { + const unsubscribeStart = batchRunner.on('test-file-start', (testData) => { // Forward to our event emitter this._eventEmitter.emit('test-file-start', testData); }); try { - // Run tests using the base runner (always serial for benchmarking) - await this._baseRunner.runTests(tests, watcher, { ...options, serial: true }); + // Run tests using the fresh batch runner (always serial for benchmarking) + await batchRunner.runTests(tests, watcher, ...args); } finally { // Cleanup subscriptions if (typeof unsubscribeSuccess === 'function') unsubscribeSuccess(); From 56941357c98bb7cd9237f5145adab929ec0dc9d0 Mon Sep 17 00:00:00 2001 From: mohammed ahmed <64513301+mohammedahmed18@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:35:33 +0200 Subject: [PATCH 2/8] Update packages/codeflash/runtime/loop-runner.js Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- packages/codeflash/runtime/loop-runner.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index 1cd0803c9..ffdaa3757 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -87,7 +87,10 @@ function findJestRunnerRecursive(nodeModulesPath, maxDepth = 5) { // - pnpm versioned directories (jest-runner@30.0.5) const shouldRecurse = entry.name === 'node_modules' || entry.name.startsWith('@') || - entry.name.startsWith('.') || + const shouldRecurse = entry.name === 'node_modules' || + entry.name.startsWith('@') || + entry.name === '.pnpm' || entry.name === '.yarn' || + entry.name.startsWith('jest-runner@'); entry.name.startsWith('jest-runner@'); if (shouldRecurse) { From 2fb4b2dbfdcf292d9beb7c808cbd4f24b66525b9 Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 14:36:39 +0200 Subject: [PATCH 3/8] cleaning up --- .github/workflows/js-tests.yml | 50 ---------------------------------- codeflash/version.py | 2 +- 2 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 .github/workflows/js-tests.yml diff --git a/.github/workflows/js-tests.yml b/.github/workflows/js-tests.yml deleted file mode 100644 index 0d56e8831..000000000 --- a/.github/workflows/js-tests.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: JavaScript/TypeScript Integration Tests - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref_name }} - cancel-in-progress: true - -jobs: - js-integration-tests: - name: JS/TS Integration Tests - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install uv - uses: astral-sh/setup-uv@v6 - - - name: Install Python dependencies - run: | - uv venv --seed - uv sync - - - name: Install npm dependencies for test projects - run: | - npm install --prefix code_to_optimize/js/code_to_optimize_js - npm install --prefix code_to_optimize/js/code_to_optimize_ts - npm install --prefix code_to_optimize/js/code_to_optimize_vitest - - - name: Run JavaScript integration tests - run: | - uv run pytest tests/languages/javascript/ -v - uv run pytest tests/test_languages/test_vitest_e2e.py -v - uv run pytest tests/test_languages/test_javascript_e2e.py -v - uv run pytest tests/test_languages/test_javascript_support.py -v - uv run pytest tests/code_utils/test_config_js.py -v diff --git a/codeflash/version.py b/codeflash/version.py index 6d60ab0c2..6225467e3 100644 --- a/codeflash/version.py +++ b/codeflash/version.py @@ -1,2 +1,2 @@ # These version placeholders will be replaced by uv-dynamic-versioning during build. -__version__ = "0.20.0.post510.dev0+b8932209" +__version__ = "0.20.0" From 2d73cf88bb199b0c6fa5d15c0fa90f3c6139a6da Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 14:55:49 +0200 Subject: [PATCH 4/8] typo --- packages/codeflash/runtime/loop-runner.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index ffdaa3757..994397044 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -85,8 +85,6 @@ function findJestRunnerRecursive(nodeModulesPath, maxDepth = 5) { // - scoped packages (@org/pkg) // - hidden directories (.pnpm, .yarn, etc.) // - pnpm versioned directories (jest-runner@30.0.5) - const shouldRecurse = entry.name === 'node_modules' || - entry.name.startsWith('@') || const shouldRecurse = entry.name === 'node_modules' || entry.name.startsWith('@') || entry.name === '.pnpm' || entry.name === '.yarn' || From 5e25b7f3b629b4c21c604cee0fc9d7e3e5a370c5 Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 18:29:17 +0200 Subject: [PATCH 5/8] debugging for failed workflow --- codeflash/languages/javascript/test_runner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codeflash/languages/javascript/test_runner.py b/codeflash/languages/javascript/test_runner.py index bcc3a74de..d33beee66 100644 --- a/codeflash/languages/javascript/test_runner.py +++ b/codeflash/languages/javascript/test_runner.py @@ -1044,6 +1044,10 @@ def run_jest_benchmarking_tests( # Create result with combined stdout result = subprocess.CompletedProcess(args=result.args, returncode=result.returncode, stdout=stdout, stderr="") + if result.returncode != 0: + logger.debug(f"Jest benchmarking failed with return code {result.returncode}") + logger.debug(f"Jest benchmarking stdout: {result.stdout}") + logger.debug(f"Jest benchmarking stderr: {result.stderr}") except subprocess.TimeoutExpired: logger.warning(f"Jest benchmarking timed out after {total_timeout}s") From bfe4224de880a98ab7247b99e0b442567e6ab2ed Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 18:35:31 +0200 Subject: [PATCH 6/8] just for testing --- codeflash/languages/javascript/test_runner.py | 6 +++--- codeflash/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codeflash/languages/javascript/test_runner.py b/codeflash/languages/javascript/test_runner.py index d33beee66..3a193602b 100644 --- a/codeflash/languages/javascript/test_runner.py +++ b/codeflash/languages/javascript/test_runner.py @@ -1045,9 +1045,9 @@ def run_jest_benchmarking_tests( # Create result with combined stdout result = subprocess.CompletedProcess(args=result.args, returncode=result.returncode, stdout=stdout, stderr="") if result.returncode != 0: - logger.debug(f"Jest benchmarking failed with return code {result.returncode}") - logger.debug(f"Jest benchmarking stdout: {result.stdout}") - logger.debug(f"Jest benchmarking stderr: {result.stderr}") + logger.info(f"Jest benchmarking failed with return code {result.returncode}") + logger.info(f"Jest benchmarking stdout: {result.stdout}") + logger.info(f"Jest benchmarking stderr: {result.stderr}") except subprocess.TimeoutExpired: logger.warning(f"Jest benchmarking timed out after {total_timeout}s") diff --git a/codeflash/version.py b/codeflash/version.py index 6225467e3..ca6d7615e 100644 --- a/codeflash/version.py +++ b/codeflash/version.py @@ -1,2 +1,2 @@ # These version placeholders will be replaced by uv-dynamic-versioning during build. -__version__ = "0.20.0" +__version__ = "0.20.0.post634.dev0+2d73cf88" From d13cdb559b39d1f2b0ce8b0ec5802fa5f8ede709 Mon Sep 17 00:00:00 2001 From: ali Date: Mon, 16 Feb 2026 19:11:27 +0200 Subject: [PATCH 7/8] fallback to directly require the jest-runner module inside the loop runner --- packages/codeflash/runtime/loop-runner.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index 994397044..43c167f32 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -89,7 +89,6 @@ function findJestRunnerRecursive(nodeModulesPath, maxDepth = 5) { entry.name.startsWith('@') || entry.name === '.pnpm' || entry.name === '.yarn' || entry.name.startsWith('jest-runner@'); - entry.name.startsWith('jest-runner@'); if (shouldRecurse) { const result = search(entryPath, depth + 1); @@ -197,9 +196,14 @@ try { } } } catch (e) { - // jest-runner not installed - this is expected for Vitest projects - // The runner will throw a helpful error if someone tries to use it without jest-runner - jestRunnerAvailable = false; + // try to directly import jest-runner + try { + const jestRunner = require('jest-runner'); + TestRunner = jestRunner.default || jestRunner.TestRunner; + jestRunnerAvailable = true; + } catch (e2) { + jestRunnerAvailable = false; + } } // Configuration From b4ea8b6bd694fc822e01b940027d7cce3794f36e Mon Sep 17 00:00:00 2001 From: mohammed ahmed <64513301+mohammedahmed18@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:26:51 +0200 Subject: [PATCH 8/8] Update packages/codeflash/runtime/loop-runner.js Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- packages/codeflash/runtime/loop-runner.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index 43c167f32..fc0b88f32 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -200,7 +200,12 @@ try { try { const jestRunner = require('jest-runner'); TestRunner = jestRunner.default || jestRunner.TestRunner; - jestRunnerAvailable = true; + if (TestRunner && TestRunner.prototype && typeof TestRunner.prototype.runTests === 'function') { + jestVersion = 30; + jestRunnerAvailable = true; + } else { + jestRunnerAvailable = false; + } } catch (e2) { jestRunnerAvailable = false; }