diff --git a/code_to_optimize/js/code_to_optimize_js/fibonacci.js b/code_to_optimize/js/code_to_optimize_js/fibonacci.js index 9ab921d90..b0ab2b51c 100644 --- a/code_to_optimize/js/code_to_optimize_js/fibonacci.js +++ b/code_to_optimize/js/code_to_optimize_js/fibonacci.js @@ -8,7 +8,7 @@ * @param {number} n - The index of the Fibonacci number to calculate * @returns {number} - The nth Fibonacci number */ -export function fibonacci(n) { +function fibonacci(n) { if (n <= 1) { return n; } @@ -20,7 +20,7 @@ export function fibonacci(n) { * @param {number} num - The number to check * @returns {boolean} - True if num is a Fibonacci number */ -export function isFibonacci(num) { +function isFibonacci(num) { // A number is Fibonacci if one of (5*n*n + 4) or (5*n*n - 4) is a perfect square const check1 = 5 * num * num + 4; const check2 = 5 * num * num - 4; @@ -33,7 +33,7 @@ export function isFibonacci(num) { * @param {number} n - The number to check * @returns {boolean} - True if n is a perfect square */ -export function isPerfectSquare(n) { +function isPerfectSquare(n) { const sqrt = Math.sqrt(n); return sqrt === Math.floor(sqrt); } @@ -43,7 +43,7 @@ export function isPerfectSquare(n) { * @param {number} n - The number of Fibonacci numbers to generate * @returns {number[]} - Array of Fibonacci numbers */ -export function fibonacciSequence(n) { +function fibonacciSequence(n) { const result = []; for (let i = 0; i < n; i++) { result.push(fibonacci(i)); diff --git a/codeflash/languages/javascript/instrument.py b/codeflash/languages/javascript/instrument.py index cadc96c81..9da58c405 100644 --- a/codeflash/languages/javascript/instrument.py +++ b/codeflash/languages/javascript/instrument.py @@ -56,6 +56,46 @@ codeflash_import_pattern = re.compile( ) +def is_inside_string(code: str, pos: int) -> bool: + """Check if a position in code is inside a string literal. + + Handles single quotes, double quotes, and template literals (backticks). + Properly handles escaped quotes. + + Args: + code: The source code. + pos: The position to check. + + Returns: + True if the position is inside a string literal. + + """ + in_string = False + string_char = None + i = 0 + + while i < pos: + char = code[i] + + if in_string: + # Check for escape sequence + if char == "\\" and i + 1 < len(code): + i += 2 # Skip escaped character + continue + # Check for end of string + if char == string_char: + in_string = False + string_char = None + # Check for start of string + elif char in "\"'`": + in_string = True + string_char = char + + i += 1 + + return in_string + + class StandaloneCallTransformer: """Transforms standalone func(...) calls in JavaScript test code. @@ -150,6 +190,10 @@ class StandaloneCallTransformer: def _should_skip_match(self, code: str, start: int, match: re.Match) -> bool: """Check if the match should be skipped (inside expect, already transformed, etc.).""" + # Skip if inside a string literal (e.g., test description) + if is_inside_string(code, start): + return True + # Look backwards to check context lookback_start = max(0, start - 200) lookback = code[lookback_start:start] @@ -439,6 +483,12 @@ class ExpectCallTransformer: result.append(code[pos:]) break + # Skip if inside a string literal (e.g., test description) + if is_inside_string(code, match.start()): + result.append(code[pos : match.end()]) + pos = match.end() + continue + # Add everything before the match result.append(code[pos : match.start()]) diff --git a/codeflash/languages/javascript/test_runner.py b/codeflash/languages/javascript/test_runner.py index bcc3a74de..1d79ad382 100644 --- a/codeflash/languages/javascript/test_runner.py +++ b/codeflash/languages/javascript/test_runner.py @@ -803,6 +803,8 @@ 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/tests/test_languages/test_javascript_instrumentation.py b/tests/test_languages/test_javascript_instrumentation.py index 27662bd59..e3457c231 100644 --- a/tests/test_languages/test_javascript_instrumentation.py +++ b/tests/test_languages/test_javascript_instrumentation.py @@ -856,4 +856,121 @@ jest.doMock('../environment', () => ({ isTest: jest.fn() })); 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) == " " \ No newline at end of file + 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 \ No newline at end of file