ignore calls inside string litrals for instrumentation and fix e2e test

This commit is contained in:
ali 2026-02-12 18:14:33 +02:00
parent e07fd1d439
commit 6b77be56ef
No known key found for this signature in database
GPG key ID: 44F9B42770617B9B
4 changed files with 174 additions and 5 deletions

View file

@ -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));

View file

@ -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()])

View file

@ -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

View file

@ -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) == " "
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