codeflash/tests/test_languages/test_javascript_instrumentation.py
Kevin Turcios 892bff485d feat(js): add JavaScript function tracer with Babel instrumentation
Replaces source-level JavaScript function tracing with Babel AST
transformation via babel-tracer-plugin.js and trace-runner.js. Adds
replay test generation, Python-side tracer runner, and --language
flag to the tracer CLI for explicit JS/TS routing.
2026-04-23 04:33:58 -05:00

942 lines
34 KiB
Python

"""Tests for JavaScript instrumentation (line profiling and tracing).
This module tests the line profiling and tracing instrumentation for JavaScript code.
"""
from __future__ import annotations
import tempfile
from pathlib import Path
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
from codeflash.languages.base import FunctionInfo, Language
from codeflash.languages.javascript.line_profiler import JavaScriptLineProfiler
from codeflash.languages.javascript.tracer import JavaScriptTracer
from codeflash.models.models import FunctionParent
def make_func(name: str, class_name: str | None = None) -> FunctionToOptimize:
"""Helper to create FunctionToOptimize for testing."""
parents = [FunctionParent(name=class_name, type="ClassDef")] if class_name else []
return FunctionToOptimize(
function_name=name, file_path=Path("/test/file.js"), parents=parents, language="javascript"
)
class TestJavaScriptLineProfiler:
"""Tests for JavaScript line profiling instrumentation."""
def test_line_profiler_initialization(self):
"""Test line profiler can be initialized."""
output_file = Path("/tmp/test_profile.json")
profiler = JavaScriptLineProfiler(output_file)
assert profiler.output_file == output_file
assert profiler.profiler_var == "__codeflash_line_profiler__"
def test_line_profiler_generates_init_code(self):
"""Test line profiler generates initialization code."""
output_file = Path("/tmp/test_profile.json")
profiler = JavaScriptLineProfiler(output_file)
init_code = profiler._generate_profiler_init()
assert profiler.profiler_var in init_code
assert "hit" in init_code # Changed from recordLine to hit
assert "save" in init_code
assert output_file.as_posix() in init_code
def test_line_profiler_instruments_simple_function(self):
"""Test line profiler can instrument a simple function."""
source = """
function add(a, b) {
const result = a + b;
return result;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
file_path = Path(f.name)
func_info = FunctionInfo(
function_name="add", file_path=file_path, starting_line=2, ending_line=5, language="javascript"
)
output_file = Path("/tmp/test_profile.json")
profiler = JavaScriptLineProfiler(output_file)
instrumented = profiler.instrument_source(source, file_path, [func_info])
# Check that profiler initialization is added
assert profiler.profiler_var in instrumented
assert "hit" in instrumented # Changed from recordLine to hit
# Clean up
file_path.unlink()
def test_line_profiler_parse_results_empty(self):
"""Test parsing results when file doesn't exist."""
output_file = Path("/tmp/nonexistent_profile.json")
results = JavaScriptLineProfiler.parse_results(output_file)
assert results["timings"] == {}
assert results["unit"] == 1e-9
class TestJavaScriptTracer:
"""Tests for JavaScript function tracing instrumentation."""
def test_tracer_initialization(self):
"""Test tracer can be initialized."""
output_db = Path("/tmp/test_traces.db")
tracer = JavaScriptTracer(output_db)
assert tracer.output_db == output_db
def test_tracer_parse_results_empty(self):
"""Test parsing results when file doesn't exist."""
output_db = Path("/tmp/nonexistent_traces.db")
results = JavaScriptTracer.parse_results(output_db)
assert results == []
class TestJavaScriptSupportInstrumentation:
"""Integration tests for JavaScript support instrumentation methods."""
def test_javascript_support_instrument_for_behavior(self):
"""Test JavaScriptSupport.instrument_for_behavior returns source unchanged.
JavaScript tracing is now handled at runtime via Babel plugin, not source transformation.
"""
from codeflash.languages import get_language_support
js_support = get_language_support(Language.JAVASCRIPT)
source = """
function greet(name) {
return "Hello, " + name;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
file_path = Path(f.name)
func_info = FunctionInfo(
function_name="greet", file_path=file_path, starting_line=2, ending_line=4, language="javascript"
)
output_file = file_path.parent / ".codeflash" / "traces.db"
instrumented = js_support.instrument_for_behavior(source, [func_info], output_file=output_file)
assert instrumented == source
# Clean up
file_path.unlink()
def test_javascript_support_instrument_for_line_profiling(self):
"""Test JavaScriptSupport.instrument_source_for_line_profiler method."""
from codeflash.languages import get_language_support
js_support = get_language_support(Language.JAVASCRIPT)
source = """
function square(n) {
const result = n * n;
return result;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
file_path = Path(f.name)
func_info = FunctionInfo(
function_name="square", file_path=file_path, starting_line=2, ending_line=5, language="javascript"
)
output_file = file_path.parent / ".codeflash" / "line_profile.json"
# instrument_source_for_line_profiler modifies the file directly
result = js_support.instrument_source_for_line_profiler(
func_info=func_info, line_profiler_output_file=output_file
)
assert result is True
# Read the instrumented code from the file
instrumented = file_path.read_text()
assert "__codeflash_line_profiler__" in instrumented
assert "hit" in instrumented # Changed from recordLine to hit
# Clean up
file_path.unlink()
class TestImportStyleValidation:
"""Tests for import style validation and fixing."""
def test_fix_named_import_for_default_export_commonjs(self):
"""Test fixing named require to default when source uses default export."""
from codeflash.languages.javascript.instrument import validate_and_fix_import_style
# Source file with default export (module.exports = function)
source = """
module.exports = function decrypt(data) {
return data;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
source_path = Path(f.name)
# Test code using wrong import style (named import for default export)
test_code = f"""
const {{ decrypt }} = require('{source_path.as_posix()}');
test('decrypt works', () => {{
expect(decrypt('hello')).toBe('hello');
}});
"""
fixed_code = validate_and_fix_import_style(test_code, source_path, "decrypt")
# Should be fixed to default import
assert f"const decrypt = require('{source_path.as_posix()}')" in fixed_code
assert "{ decrypt }" not in fixed_code
# Clean up
source_path.unlink()
def test_fix_named_import_for_default_export_esm(self):
"""Test fixing named import to default when source uses default export."""
from codeflash.languages.javascript.instrument import validate_and_fix_import_style
# Source file with default export
source = """
export default function decrypt(data) {
return data;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
source_path = Path(f.name)
# Test code using wrong import style
test_code = f"""
import {{ decrypt }} from '{source_path.as_posix()}';
test('decrypt works', () => {{
expect(decrypt('hello')).toBe('hello');
}});
"""
fixed_code = validate_and_fix_import_style(test_code, source_path, "decrypt")
# Should be fixed to default import
assert f"import decrypt from '{source_path.as_posix()}'" in fixed_code
assert "{ decrypt }" not in fixed_code
# Clean up
source_path.unlink()
def test_fix_default_import_for_named_export(self):
"""Test fixing default import to named when source uses named export."""
from codeflash.languages.javascript.instrument import validate_and_fix_import_style
# Source file with named export
source = """
function decrypt(data) {
return data;
}
module.exports = { decrypt };
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
source_path = Path(f.name)
# Test code using wrong import style (default import for named export)
test_code = f"""
const decrypt = require('{source_path.as_posix()}');
test('decrypt works', () => {{
expect(decrypt('hello')).toBe('hello');
}});
"""
fixed_code = validate_and_fix_import_style(test_code, source_path, "decrypt")
# Should be fixed to named import
assert f"const {{ decrypt }} = require('{source_path.as_posix()}')" in fixed_code
# Clean up
source_path.unlink()
def test_no_change_when_import_correct(self):
"""Test that correct imports are not modified."""
from codeflash.languages.javascript.instrument import validate_and_fix_import_style
# Source file with named export
source = """
export function decrypt(data) {
return data;
}
"""
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
f.write(source)
f.flush()
source_path = Path(f.name)
# Test code with correct import style
test_code = f"""
import {{ decrypt }} from '{source_path.as_posix()}';
test('decrypt works', () => {{
expect(decrypt('hello')).toBe('hello');
}});
"""
fixed_code = validate_and_fix_import_style(test_code, source_path, "decrypt")
# Should not be changed
assert fixed_code == test_code
# Clean up
source_path.unlink()
class TestClassMethodInstrumentation:
"""Tests for class method instrumentation."""
def test_instrument_method_call_on_instance(self):
"""Test that method calls on instances are correctly instrumented."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
const calc = new Calculator();
const result = calc.fibonacci(10);
console.log(result);
"""
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Calculator"), capture_func="capture"
)
# Should transform calc.fibonacci(10) to codeflash.capture(..., calc.fibonacci.bind(calc), 10)
assert "codeflash.capture('Calculator.fibonacci'" in transformed
assert "calc.fibonacci.bind(calc)" in transformed
assert counter == 1
def test_instrument_expect_with_method_call(self):
"""Test that expect() with method calls are correctly instrumented."""
from codeflash.languages.javascript.instrument import transform_expect_calls
code = """
test('fibonacci works', () => {
const calc = new FibonacciCalculator();
expect(calc.fibonacci(10)).toBe(55);
});
"""
transformed, counter = transform_expect_calls(
code=code,
function_to_optimize=make_func("fibonacci", class_name="FibonacciCalculator"),
capture_func="capture",
)
# Should transform expect(calc.fibonacci(10)) to
# expect(codeflash.capture(..., calc.fibonacci.bind(calc), 10))
assert "codeflash.capture('FibonacciCalculator.fibonacci'" in transformed
assert "calc.fibonacci.bind(calc)" in transformed
assert ".toBe(55)" in transformed
assert counter == 1
def test_instrument_expect_with_method_removes_assertion(self):
"""Test that expect() with method calls are correctly instrumented with assertion removal."""
from codeflash.languages.javascript.instrument import transform_expect_calls
code = """
test('fibonacci works', () => {
const calc = new FibonacciCalculator();
expect(calc.fibonacci(10)).toBe(55);
});
"""
transformed, counter = transform_expect_calls(
code=code,
function_to_optimize=make_func("fibonacci", class_name="FibonacciCalculator"),
capture_func="capture",
remove_assertions=True,
)
# Should remove expect wrapper and assertion
assert "codeflash.capture('FibonacciCalculator.fibonacci'" in transformed
assert "calc.fibonacci.bind(calc)" in transformed
assert ".toBe(55)" not in transformed # Assertion removed
assert "expect(" not in transformed # expect wrapper removed
assert counter == 1
def test_does_not_instrument_function_definition(self):
"""Test that function definitions are NOT transformed."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
class FibonacciCalculator {
fibonacci(n) {
if (n <= 1) return n;
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
"""
transformed, counter = transform_standalone_calls(
code=code,
function_to_optimize=make_func("fibonacci", class_name="FibonacciCalculator"),
capture_func="capture",
)
# The method definition should NOT be transformed
# Only the recursive calls this.fibonacci(...) should potentially be transformed
assert "fibonacci(n) {" in transformed # Method definition unchanged
assert counter >= 0 # May or may not transform the recursive calls
def test_does_not_instrument_prototype_assignment(self):
"""Test that prototype assignments are NOT transformed."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
FibonacciCalculator.prototype.fibonacci = function(n) {
if (n <= 1) return n;
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
};
"""
transformed, counter = transform_standalone_calls(
code=code,
function_to_optimize=make_func("fibonacci", class_name="FibonacciCalculator"),
capture_func="capture",
)
# The prototype assignment should NOT be transformed
# It should still have the original pattern
assert "FibonacciCalculator.prototype.fibonacci = function(n)" in transformed
def test_instrument_multiple_method_calls(self):
"""Test that multiple method calls are correctly instrumented."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
const calc = new Calculator();
const a = calc.fibonacci(5);
const b = calc.fibonacci(10);
const sum = a + b;
"""
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Calculator"), capture_func="capture"
)
# Should transform both calls
assert transformed.count("codeflash.capture") == 2
assert counter == 2
def test_instrument_this_method_call(self):
"""Test that this.method() calls are correctly instrumented."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
class Wrapper {
callFibonacci(n) {
return this.fibonacci(n);
}
}
"""
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Wrapper"), capture_func="capture"
)
# Should transform this.fibonacci(n)
assert "codeflash.capture('Wrapper.fibonacci'" in transformed
assert "this.fibonacci.bind(this)" in transformed
assert counter == 1
def test_full_instrumentation_produces_valid_syntax(self):
"""Test that full instrumentation produces syntactically valid JavaScript."""
from codeflash.languages import get_language_support
from codeflash.languages.base import Language
from codeflash.languages.javascript.instrument import _instrument_js_test_code
js_support = get_language_support(Language.JAVASCRIPT)
test_code = """
const { FibonacciCalculator } = require('../fibonacci_class');
describe('FibonacciCalculator', () => {
let calc;
beforeEach(() => {
calc = new FibonacciCalculator();
});
test('fibonacci returns correct values', () => {
expect(calc.fibonacci(0)).toBe(0);
expect(calc.fibonacci(1)).toBe(1);
expect(calc.fibonacci(10)).toBe(55);
});
test('standalone call', () => {
const result = calc.fibonacci(5);
expect(result).toBe(5);
});
});
"""
instrumented = _instrument_js_test_code(
code=test_code,
function_to_optimize=make_func("fibonacci", class_name="FibonacciCalculator"),
test_file_path="test.js",
mode="behavior",
)
# Check that codeflash import was added
assert "codeflash" in instrumented
# Check that method calls were instrumented
assert "codeflash.capture" in instrumented
# Check that the instrumented code is valid JavaScript
assert js_support.validate_syntax(instrumented) is True, f"Invalid syntax:\n{instrumented}"
def test_instrumentation_preserves_test_structure(self):
"""Test that instrumentation preserves the test structure."""
from codeflash.languages.javascript.instrument import _instrument_js_test_code
test_code = """
const { Calculator } = require('../calculator');
describe('Calculator', () => {
test('add works', () => {
const calc = new Calculator();
expect(calc.add(1, 2)).toBe(3);
});
});
"""
instrumented = _instrument_js_test_code(
code=test_code,
function_to_optimize=make_func("add", class_name="Calculator"),
test_file_path="test.js",
mode="behavior",
)
# describe and test structure should be preserved
assert "describe('Calculator'" in instrumented
assert "test('add works'" in instrumented
assert "beforeEach" in instrumented or "beforeEach" not in test_code # Only if it was there
# Method call should be instrumented
assert "codeflash.capture('Calculator.add'" in instrumented
assert "calc.add.bind(calc)" in instrumented
def test_instrumentation_with_async_methods(self):
"""Test instrumentation with async method calls."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = """
const api = new ApiClient();
const data = await api.fetchData('http://example.com');
console.log(data);
"""
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fetchData", class_name="ApiClient"), capture_func="capture"
)
# Should preserve await
assert "await codeflash.capture" in transformed
assert "api.fetchData.bind(api)" in transformed
assert counter == 1
class TestInstrumentationFullStringEquality:
"""Tests with full string equality for precise verification."""
def test_standalone_method_call_exact_output(self):
"""Test exact output of standalone method call instrumentation."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = " calc.fibonacci(10);"
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Calculator"), capture_func="capture"
)
expected = " codeflash.capture('Calculator.fibonacci', '1', calc.fibonacci.bind(calc), 10);"
assert transformed == expected, f"Expected:\n{expected}\nGot:\n{transformed}"
assert counter == 1
def test_expect_method_call_exact_output(self):
"""Test exact output of expect() method call instrumentation."""
from codeflash.languages.javascript.instrument import transform_expect_calls
code = " expect(calc.fibonacci(10)).toBe(55);"
transformed, counter = transform_expect_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Calculator"), capture_func="capture"
)
expected = " expect(codeflash.capture('Calculator.fibonacci', '1', calc.fibonacci.bind(calc), 10)).toBe(55);"
assert transformed == expected, f"Expected:\n{expected}\nGot:\n{transformed}"
assert counter == 1
def test_expect_method_call_remove_assertions_exact_output(self):
"""Test exact output when removing assertions."""
from codeflash.languages.javascript.instrument import transform_expect_calls
code = " expect(calc.fibonacci(10)).toBe(55);"
transformed, counter = transform_expect_calls(
code=code,
function_to_optimize=make_func("fibonacci", class_name="Calculator"),
capture_func="capture",
remove_assertions=True,
)
expected = " codeflash.capture('Calculator.fibonacci', '1', calc.fibonacci.bind(calc), 10);"
assert transformed == expected, f"Expected:\n{expected}\nGot:\n{transformed}"
assert counter == 1
def test_standalone_function_call_no_object_prefix(self):
"""Test that standalone function calls (no object) work correctly."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = " fibonacci(10);"
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci"), capture_func="capture"
)
expected = " codeflash.capture('fibonacci', '1', fibonacci, 10);"
assert transformed == expected, f"Expected:\n{expected}\nGot:\n{transformed}"
assert counter == 1
def test_this_method_call_exact_output(self):
"""Test exact output for this.method() call."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
code = " return this.fibonacci(n - 1);"
transformed, counter = transform_standalone_calls(
code=code, function_to_optimize=make_func("fibonacci", class_name="Class"), capture_func="capture"
)
expected = " return codeflash.capture('Class.fibonacci', '1', this.fibonacci.bind(this), n - 1);"
assert transformed == expected, f"Expected:\n{expected}\nGot:\n{transformed}"
assert counter == 1
class TestFixImportsInsideTestBlocks:
"""Tests for fix_imports_inside_test_blocks function."""
def test_fix_named_import_inside_test_block(self):
"""Test fixing named import inside test function."""
from codeflash.languages.javascript.instrument import fix_imports_inside_test_blocks
code = """
test('should work', () => {
const mock = jest.fn();
import { foo } from '../src/module';
expect(foo()).toBe(true);
});
"""
fixed = fix_imports_inside_test_blocks(code)
assert "const { foo } = require('../src/module');" in fixed
assert "import { foo }" not in fixed
def test_fix_default_import_inside_test_block(self):
"""Test fixing default import inside test function."""
from codeflash.languages.javascript.instrument import fix_imports_inside_test_blocks
code = """
test('should work', () => {
env.isTest.mockReturnValue(false);
import queuesModule from '../src/queue/queue';
expect(queuesModule).toBeDefined();
});
"""
fixed = fix_imports_inside_test_blocks(code)
assert "const queuesModule = require('../src/queue/queue');" in fixed
assert "import queuesModule from" not in fixed
def test_fix_namespace_import_inside_test_block(self):
"""Test fixing namespace import inside test function."""
from codeflash.languages.javascript.instrument import fix_imports_inside_test_blocks
code = """
test('should work', () => {
import * as utils from '../src/utils';
expect(utils.foo()).toBe(true);
});
"""
fixed = fix_imports_inside_test_blocks(code)
assert "const utils = require('../src/utils');" in fixed
assert "import * as utils" not in fixed
def test_preserve_top_level_imports(self):
"""Test that top-level imports are not modified."""
from codeflash.languages.javascript.instrument import fix_imports_inside_test_blocks
code = """
import { jest, describe, test, expect } from '@jest/globals';
import { foo } from '../src/module';
describe('test suite', () => {
test('should work', () => {
expect(foo()).toBe(true);
});
});
"""
fixed = fix_imports_inside_test_blocks(code)
# Top-level imports should remain unchanged
assert "import { jest, describe, test, expect } from '@jest/globals';" in fixed
assert "import { foo } from '../src/module';" in fixed
def test_empty_code(self):
"""Test handling empty code."""
from codeflash.languages.javascript.instrument import fix_imports_inside_test_blocks
assert fix_imports_inside_test_blocks("") == ""
assert fix_imports_inside_test_blocks(" ") == " "
class TestFixJestMockPaths:
"""Tests for fix_jest_mock_paths function."""
def test_fix_mock_path_when_source_relative(self):
"""Test fixing mock path that's relative to source file."""
from codeflash.languages.javascript.instrument import fix_jest_mock_paths
with tempfile.TemporaryDirectory() as tmpdir:
# Create directory structure
src_dir = Path(tmpdir) / "src" / "queue"
tests_dir = Path(tmpdir) / "tests"
env_file = Path(tmpdir) / "src" / "environment.ts"
src_dir.mkdir(parents=True)
tests_dir.mkdir(parents=True)
env_file.parent.mkdir(parents=True, exist_ok=True)
env_file.write_text("export const env = {};")
source_file = src_dir / "queue.ts"
source_file.write_text("import env from '../environment';")
test_file = tests_dir / "test_queue.test.ts"
# Test code with incorrect mock path (relative to source, not test)
test_code = """
import { jest, describe, test, expect } from '@jest/globals';
jest.mock('../environment');
jest.mock('../redis/utils');
describe('queue', () => {
test('works', () => {});
});
"""
fixed = fix_jest_mock_paths(test_code, test_file, source_file, tests_dir)
# Should fix the path to be relative to the test file
assert "jest.mock('../src/environment')" in fixed
def test_preserve_valid_mock_path(self):
"""Test that valid mock paths are not modified."""
from codeflash.languages.javascript.instrument import fix_jest_mock_paths
with tempfile.TemporaryDirectory() as tmpdir:
# Create directory structure
src_dir = Path(tmpdir) / "src"
tests_dir = Path(tmpdir) / "tests"
src_dir.mkdir(parents=True)
tests_dir.mkdir(parents=True)
# Create the file being mocked at the correct location
mock_file = src_dir / "utils.ts"
mock_file.write_text("export const utils = {};")
source_file = src_dir / "main.ts"
source_file.write_text("")
test_file = tests_dir / "test_main.test.ts"
# Test code with correct mock path (valid from test location)
test_code = """
jest.mock('../src/utils');
describe('main', () => {
test('works', () => {});
});
"""
fixed = fix_jest_mock_paths(test_code, test_file, source_file, tests_dir)
# Should keep the path unchanged since it's valid
assert "jest.mock('../src/utils')" in fixed
def test_fix_doMock_path(self):
"""Test fixing jest.doMock path."""
from codeflash.languages.javascript.instrument import fix_jest_mock_paths
with tempfile.TemporaryDirectory() as tmpdir:
# Create directory structure: src/queue/queue.ts imports ../environment (-> src/environment.ts)
src_dir = Path(tmpdir) / "src"
queue_dir = src_dir / "queue"
tests_dir = Path(tmpdir) / "tests"
env_file = src_dir / "environment.ts"
queue_dir.mkdir(parents=True)
tests_dir.mkdir(parents=True)
env_file.write_text("export const env = {};")
source_file = queue_dir / "queue.ts"
source_file.write_text("")
test_file = tests_dir / "test_queue.test.ts"
# From src/queue/queue.ts, ../environment resolves to src/environment.ts
# Test file is at tests/test_queue.test.ts
# So the correct mock path from test should be ../src/environment
test_code = """
jest.doMock('../environment', () => ({ isTest: jest.fn() }));
"""
fixed = fix_jest_mock_paths(test_code, test_file, source_file, tests_dir)
# Should fix the doMock path
assert "jest.doMock('../src/environment'" in fixed
def test_empty_code(self):
"""Test handling empty code."""
from codeflash.languages.javascript.instrument import fix_jest_mock_paths
with tempfile.TemporaryDirectory() as tmpdir:
tests_dir = Path(tmpdir) / "tests"
tests_dir.mkdir()
source_file = Path(tmpdir) / "src" / "main.ts"
test_file = tests_dir / "test.ts"
assert fix_jest_mock_paths("", test_file, source_file, tests_dir) == ""
assert fix_jest_mock_paths(" ", test_file, source_file, tests_dir) == " "
class TestFunctionCallsInStrings:
"""Tests for skipping function calls inside string literals."""
def test_skip_function_in_test_description_single_quotes(self):
"""Test that function calls in single-quoted test descriptions are not transformed."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
func = make_func("fibonacci")
code = """
test('should compute fibonacci(20) and fibonacci(30) to known values', () => {
const result = fibonacci(10);
expect(result).toBe(55);
});
"""
transformed, _counter = transform_standalone_calls(code, func, "capture")
# The function call in the test description should NOT be transformed
assert "fibonacci(20)" in transformed
assert "fibonacci(30)" in transformed
# The actual call should be transformed
assert "codeflash.capture('fibonacci'" in transformed
def test_skip_function_in_test_description_double_quotes(self):
"""Test that function calls in double-quoted test descriptions are not transformed."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
func = make_func("fibonacci")
code = """
test("should compute fibonacci(20) correctly", () => {
const result = fibonacci(10);
});
"""
transformed, _counter = transform_standalone_calls(code, func, "capture")
# The function call in the test description should NOT be transformed
assert "fibonacci(20)" in transformed
# The actual call should be transformed
assert "codeflash.capture('fibonacci'" in transformed
def test_skip_function_in_template_literal(self):
"""Test that function calls in template literals are not transformed."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
func = make_func("fibonacci")
code = """
test(`should compute fibonacci(20) correctly`, () => {
const result = fibonacci(10);
});
"""
transformed, _counter = transform_standalone_calls(code, func, "capture")
# The function call in the template literal should NOT be transformed
assert "fibonacci(20)" in transformed
# The actual call should be transformed
assert "codeflash.capture('fibonacci'" in transformed
def test_skip_expect_in_string_literal(self):
"""Test that expect(func()) in string literals is not transformed."""
from codeflash.languages.javascript.instrument import transform_expect_calls
func = make_func("fibonacci")
code = """
describe('testing expect(fibonacci(n)) patterns', () => {
test('works', () => {
expect(fibonacci(10)).toBe(55);
});
});
"""
transformed, _counter = transform_expect_calls(code, func, "capture")
# The expect in the describe string should NOT be transformed
assert "expect(fibonacci(n))" in transformed
# The actual expect call should be transformed
assert "codeflash.capture('fibonacci'" in transformed
def test_handle_escaped_quotes_in_string(self):
"""Test that escaped quotes in strings are handled correctly."""
from codeflash.languages.javascript.instrument import transform_standalone_calls
func = make_func("fibonacci")
code = """
test('test \\'fibonacci(5)\\' escaping', () => {
const result = fibonacci(10);
});
"""
transformed, _counter = transform_standalone_calls(code, func, "capture")
# The function call in the escaped string should NOT be transformed
assert "fibonacci(5)" in transformed
# The actual call should be transformed
assert "codeflash.capture('fibonacci'" in transformed
def test_is_inside_string_helper(self):
"""Test the is_inside_string helper function directly."""
from codeflash.languages.javascript.instrument import is_inside_string
# Position inside single-quoted string
code1 = "test('fibonacci(5)', () => {})"
assert is_inside_string(code1, 10) is True # Inside the string
# Position outside string
assert is_inside_string(code1, 0) is False # Before string
assert is_inside_string(code1, 25) is False # After string
# Double quotes
code2 = 'test("fibonacci(5)", () => {})'
assert is_inside_string(code2, 10) is True
# Template literal
code3 = "test(`fibonacci(5)`, () => {})"
assert is_inside_string(code3, 10) is True
# Escaped quote doesn't end string
code4 = "test('fib\\'s result', () => {})"
assert is_inside_string(code4, 15) is True # Still inside after escaped quote