Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 54 additions & 13 deletions codeflash/languages/javascript/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,55 @@ def _has_ts_jest_dependency(project_root: Path) -> bool:
return False


def _detect_typescript_transformer(project_root: Path) -> tuple[str | None, str]:
package_json = project_root / "package.json"
if not package_json.exists():
return (None, "")

try:
content = json.loads(package_json.read_text(encoding="utf-8"))
deps = {**content.get("dependencies", {}), **content.get("devDependencies", {})}

# Check for various TypeScript transformers in order of preference
if "ts-jest" in deps:
config = """
// Ensure TypeScript files are transformed using ts-jest
transform: {
'^.+\\\\.(ts|tsx)$': ['ts-jest', { isolatedModules: true }],
// Use ts-jest for JS files in ESM packages too
'^.+\\\\.js$': ['ts-jest', { isolatedModules: true }],
},"""
return ("ts-jest", config)

if "@swc/jest" in deps:
config = """
// Ensure TypeScript files are transformed using @swc/jest
transform: {
'^.+\\\\.(ts|tsx)$': '@swc/jest',
},"""
return ("@swc/jest", config)

if "babel-jest" in deps and "@babel/preset-typescript" in deps:
config = """
// Ensure TypeScript files are transformed using babel-jest
transform: {
'^.+\\\\.(ts|tsx)$': 'babel-jest',
},"""
return ("babel-jest", config)

if "esbuild-jest" in deps:
config = """
// Ensure TypeScript files are transformed using esbuild-jest
transform: {
'^.+\\\\.(ts|tsx)$': 'esbuild-jest',
},"""
return ("esbuild-jest", config)

return (None, "")
except (json.JSONDecodeError, OSError):
return (None, "")


def _create_codeflash_jest_config(
project_root: Path, original_jest_config: Path | None, *, for_esm: bool = False
) -> Path | None:
Expand Down Expand Up @@ -278,21 +327,13 @@ def _create_codeflash_jest_config(
]
esm_pattern = "|".join(esm_packages)

# Check if ts-jest is available in the project
has_ts_jest = _has_ts_jest_dependency(project_root)
# Detect TypeScript transformer in the project
transformer_name, transform_config = _detect_typescript_transformer(project_root)

# Build transform config only if ts-jest is available
if has_ts_jest:
transform_config = """
// Ensure TypeScript files are transformed using ts-jest
transform: {
'^.+\\\\.(ts|tsx)$': ['ts-jest', { isolatedModules: true }],
// Use ts-jest for JS files in ESM packages too
'^.+\\\\.js$': ['ts-jest', { isolatedModules: true }],
},"""
if transformer_name:
logger.debug(f"Detected TypeScript transformer: {transformer_name}")
else:
transform_config = ""
logger.debug("ts-jest not found in project dependencies, skipping transform config")
logger.debug("No TypeScript transformer found in project dependencies")

# Create a wrapper Jest config
if original_jest_config:
Expand Down
87 changes: 87 additions & 0 deletions tests/test_languages/test_typescript_transform_extraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from pathlib import Path


class TestTypeScriptTransformExtraction:
def test_extracts_swc_transform_from_typescript_config(self, tmp_path: Path) -> None:
from codeflash.languages.javascript.test_runner import (
_create_codeflash_jest_config,
clear_created_config_files,
)

tmpdir_path = tmp_path.resolve()

# Create package.json without ts-jest (mimics budibase)
(tmpdir_path / "package.json").write_text(
'{"name": "test", "devDependencies": {"@swc/jest": "^0.2.0"}}', encoding="utf-8"
)

# Create jest.config.ts with @swc/jest transform (mimics budibase)
jest_config_content = """import { Config } from "jest"

const config: Config = {
transform: {
"^.+\\\\.ts?$": "@swc/jest",
},
}

export default config
"""
jest_config_path = tmpdir_path / "jest.config.ts"
jest_config_path.write_text(jest_config_content, encoding="utf-8")

clear_created_config_files()

result = _create_codeflash_jest_config(tmpdir_path, jest_config_path)

assert result is not None, "Should create a codeflash config"

generated_config = result.read_text(encoding="utf-8")

# The generated config MUST include a transform directive for TypeScript files
assert "transform" in generated_config, f"Generated config must include transform directive. Got:\n{generated_config}"

# Verify it can actually transform TypeScript files (.ts extension)
assert (
"@swc/jest" in generated_config or "ts-jest" in generated_config or '"^.+\\\\.ts' in generated_config
), f"Generated config must include TypeScript file transformation. Got:\n{generated_config}"

clear_created_config_files()

def test_extracts_ts_jest_transform_from_typescript_config(self, tmp_path: Path) -> None:
from codeflash.languages.javascript.test_runner import (
_create_codeflash_jest_config,
clear_created_config_files,
)

tmpdir_path = tmp_path.resolve()

# Create package.json with ts-jest
(tmpdir_path / "package.json").write_text(
'{"name": "test", "devDependencies": {"ts-jest": "^29.0.0"}}', encoding="utf-8"
)

# Create jest.config.ts with ts-jest transform
jest_config_content = """import { Config } from "jest"

const config: Config = {
transform: {
"^.+\\\\.(ts|tsx)$": "ts-jest",
},
}

export default config
"""
jest_config_path = tmpdir_path / "jest.config.ts"
jest_config_path.write_text(jest_config_content, encoding="utf-8")

clear_created_config_files()

result = _create_codeflash_jest_config(tmpdir_path, jest_config_path)

assert result is not None, "Should create a codeflash config"

generated_config = result.read_text(encoding="utf-8")

assert "ts-jest" in generated_config, f"Generated config should include ts-jest transform. Got:\n{generated_config}"

clear_created_config_files()
Loading