Skip to content

Commit 7cde435

Browse files
Merge pull request #1212 from codeflash-ai/fix/jest-roots-configuration
fix: add --roots flag to Jest to include test file directories
2 parents 6e2e376 + cd3ef60 commit 7cde435

2 files changed

Lines changed: 280 additions & 3 deletions

File tree

codeflash/languages/javascript/test_runner.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,14 @@ def run_jest_behavioral_tests(
257257

258258
if test_files:
259259
jest_cmd.append("--runTestsByPath")
260-
jest_cmd.extend(str(Path(f).resolve()) for f in test_files)
260+
resolved_test_files = [str(Path(f).resolve()) for f in test_files]
261+
jest_cmd.extend(resolved_test_files)
262+
# Add --roots to include directories containing test files
263+
# This is needed because some projects configure Jest with restricted roots
264+
# (e.g., roots: ["<rootDir>/src"]) which excludes the test directory
265+
test_dirs = {str(Path(f).resolve().parent) for f in test_files}
266+
for test_dir in sorted(test_dirs):
267+
jest_cmd.extend(["--roots", test_dir])
261268

262269
if timeout:
263270
jest_cmd.append(f"--testTimeout={timeout * 1000}") # Jest uses milliseconds
@@ -467,7 +474,12 @@ def run_jest_benchmarking_tests(
467474

468475
if test_files:
469476
jest_cmd.append("--runTestsByPath")
470-
jest_cmd.extend(str(Path(f).resolve()) for f in test_files)
477+
resolved_test_files = [str(Path(f).resolve()) for f in test_files]
478+
jest_cmd.extend(resolved_test_files)
479+
# Add --roots to include directories containing test files
480+
test_dirs = {str(Path(f).resolve().parent) for f in test_files}
481+
for test_dir in sorted(test_dirs):
482+
jest_cmd.extend(["--roots", test_dir])
471483

472484
if timeout:
473485
jest_cmd.append(f"--testTimeout={timeout * 1000}")
@@ -596,7 +608,12 @@ def run_jest_line_profile_tests(
596608

597609
if test_files:
598610
jest_cmd.append("--runTestsByPath")
599-
jest_cmd.extend(str(Path(f).resolve()) for f in test_files)
611+
resolved_test_files = [str(Path(f).resolve()) for f in test_files]
612+
jest_cmd.extend(resolved_test_files)
613+
# Add --roots to include directories containing test files
614+
test_dirs = {str(Path(f).resolve().parent) for f in test_files}
615+
for test_dir in sorted(test_dirs):
616+
jest_cmd.extend(["--roots", test_dir])
600617

601618
if timeout:
602619
jest_cmd.append(f"--testTimeout={timeout * 1000}")
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
"""Tests for JavaScript/Jest test runner functionality."""
2+
3+
import tempfile
4+
from pathlib import Path
5+
from unittest.mock import patch, MagicMock
6+
7+
import pytest
8+
9+
10+
class TestJestRootsConfiguration:
11+
"""Tests for Jest --roots flag handling."""
12+
13+
def test_behavioral_tests_adds_roots_for_test_directories(self):
14+
"""Test that run_jest_behavioral_tests adds --roots for test directories."""
15+
from codeflash.languages.javascript.test_runner import run_jest_behavioral_tests
16+
from codeflash.models.models import TestFile, TestFiles
17+
from codeflash.models.test_type import TestType
18+
19+
# Create mock test files in a test directory
20+
with tempfile.TemporaryDirectory() as tmpdir:
21+
tmpdir_path = Path(tmpdir)
22+
test_dir = tmpdir_path / "test"
23+
test_dir.mkdir()
24+
25+
# Create package.json to simulate a Node project
26+
(tmpdir_path / "package.json").write_text('{"name": "test"}')
27+
28+
# Create mock test files
29+
test_file1 = test_dir / "test_func__unit_test_0.test.ts"
30+
test_file2 = test_dir / "test_func__unit_test_1.test.ts"
31+
test_file1.write_text("// test 1")
32+
test_file2.write_text("// test 2")
33+
34+
mock_test_files = TestFiles(
35+
test_files=[
36+
TestFile(
37+
original_file_path=test_file1,
38+
instrumented_behavior_file_path=test_file1,
39+
benchmarking_file_path=test_file1,
40+
test_type=TestType.GENERATED_REGRESSION,
41+
),
42+
TestFile(
43+
original_file_path=test_file2,
44+
instrumented_behavior_file_path=test_file2,
45+
benchmarking_file_path=test_file2,
46+
test_type=TestType.GENERATED_REGRESSION,
47+
),
48+
]
49+
)
50+
51+
# Mock subprocess.run to capture the command
52+
with patch("subprocess.run") as mock_run:
53+
mock_result = MagicMock()
54+
mock_result.stdout = ""
55+
mock_result.stderr = ""
56+
mock_result.returncode = 1
57+
mock_run.return_value = mock_result
58+
59+
try:
60+
run_jest_behavioral_tests(
61+
test_paths=mock_test_files,
62+
test_env={},
63+
cwd=tmpdir_path,
64+
project_root=tmpdir_path,
65+
)
66+
except Exception:
67+
pass # Expected to fail since no real Jest
68+
69+
# Verify the command included --roots
70+
if mock_run.called:
71+
call_args = mock_run.call_args
72+
cmd = call_args[0][0]
73+
74+
# Find --roots flags in the command
75+
roots_flags = []
76+
for i, arg in enumerate(cmd):
77+
if arg == "--roots" and i + 1 < len(cmd):
78+
roots_flags.append(cmd[i + 1])
79+
80+
# Should have added the test directory as a root
81+
assert len(roots_flags) > 0, "Expected --roots flag in Jest command"
82+
assert str(test_dir) in roots_flags or any(
83+
str(test_dir) in root for root in roots_flags
84+
), f"Expected test directory {test_dir} in --roots flags: {roots_flags}"
85+
86+
def test_benchmarking_tests_adds_roots_for_test_directories(self):
87+
"""Test that run_jest_benchmarking_tests adds --roots for test directories."""
88+
from codeflash.languages.javascript.test_runner import run_jest_benchmarking_tests
89+
from codeflash.models.models import TestFile, TestFiles
90+
from codeflash.models.test_type import TestType
91+
92+
with tempfile.TemporaryDirectory() as tmpdir:
93+
tmpdir_path = Path(tmpdir)
94+
test_dir = tmpdir_path / "test"
95+
test_dir.mkdir()
96+
97+
(tmpdir_path / "package.json").write_text('{"name": "test"}')
98+
99+
test_file = test_dir / "test_func__perf_test_0.test.ts"
100+
test_file.write_text("// perf test")
101+
102+
mock_test_files = TestFiles(
103+
test_files=[
104+
TestFile(
105+
original_file_path=test_file,
106+
instrumented_behavior_file_path=test_file,
107+
benchmarking_file_path=test_file,
108+
test_type=TestType.GENERATED_REGRESSION,
109+
),
110+
]
111+
)
112+
113+
with patch("subprocess.run") as mock_run:
114+
mock_result = MagicMock()
115+
mock_result.stdout = ""
116+
mock_result.stderr = ""
117+
mock_result.returncode = 1
118+
mock_run.return_value = mock_result
119+
120+
try:
121+
run_jest_benchmarking_tests(
122+
test_paths=mock_test_files,
123+
test_env={},
124+
cwd=tmpdir_path,
125+
project_root=tmpdir_path,
126+
)
127+
except Exception:
128+
pass
129+
130+
if mock_run.called:
131+
call_args = mock_run.call_args
132+
cmd = call_args[0][0]
133+
134+
roots_flags = []
135+
for i, arg in enumerate(cmd):
136+
if arg == "--roots" and i + 1 < len(cmd):
137+
roots_flags.append(cmd[i + 1])
138+
139+
assert len(roots_flags) > 0, "Expected --roots flag in Jest command"
140+
141+
def test_line_profile_tests_adds_roots_for_test_directories(self):
142+
"""Test that run_jest_line_profile_tests adds --roots for test directories."""
143+
from codeflash.languages.javascript.test_runner import run_jest_line_profile_tests
144+
from codeflash.models.models import TestFile, TestFiles
145+
from codeflash.models.test_type import TestType
146+
147+
with tempfile.TemporaryDirectory() as tmpdir:
148+
tmpdir_path = Path(tmpdir)
149+
test_dir = tmpdir_path / "test"
150+
test_dir.mkdir()
151+
152+
(tmpdir_path / "package.json").write_text('{"name": "test"}')
153+
154+
test_file = test_dir / "test_func__line_profile.test.ts"
155+
test_file.write_text("// line profile test")
156+
157+
mock_test_files = TestFiles(
158+
test_files=[
159+
TestFile(
160+
original_file_path=test_file,
161+
instrumented_behavior_file_path=test_file,
162+
benchmarking_file_path=test_file,
163+
test_type=TestType.GENERATED_REGRESSION,
164+
),
165+
]
166+
)
167+
168+
with patch("subprocess.run") as mock_run:
169+
mock_result = MagicMock()
170+
mock_result.stdout = ""
171+
mock_result.stderr = ""
172+
mock_result.returncode = 1
173+
mock_run.return_value = mock_result
174+
175+
try:
176+
run_jest_line_profile_tests(
177+
test_paths=mock_test_files,
178+
test_env={},
179+
cwd=tmpdir_path,
180+
project_root=tmpdir_path,
181+
)
182+
except Exception:
183+
pass
184+
185+
if mock_run.called:
186+
call_args = mock_run.call_args
187+
cmd = call_args[0][0]
188+
189+
roots_flags = []
190+
for i, arg in enumerate(cmd):
191+
if arg == "--roots" and i + 1 < len(cmd):
192+
roots_flags.append(cmd[i + 1])
193+
194+
assert len(roots_flags) > 0, "Expected --roots flag in Jest command"
195+
196+
def test_multiple_test_directories_all_added_to_roots(self):
197+
"""Test that multiple test directories are all added as --roots."""
198+
from codeflash.languages.javascript.test_runner import run_jest_behavioral_tests
199+
from codeflash.models.models import TestFile, TestFiles
200+
from codeflash.models.test_type import TestType
201+
202+
with tempfile.TemporaryDirectory() as tmpdir:
203+
tmpdir_path = Path(tmpdir)
204+
test_dir1 = tmpdir_path / "test"
205+
test_dir2 = tmpdir_path / "spec"
206+
test_dir1.mkdir()
207+
test_dir2.mkdir()
208+
209+
(tmpdir_path / "package.json").write_text('{"name": "test"}')
210+
211+
test_file1 = test_dir1 / "test_func__unit_test_0.test.ts"
212+
test_file2 = test_dir2 / "test_func__unit_test_1.test.ts"
213+
test_file1.write_text("// test 1")
214+
test_file2.write_text("// test 2")
215+
216+
mock_test_files = TestFiles(
217+
test_files=[
218+
TestFile(
219+
original_file_path=test_file1,
220+
instrumented_behavior_file_path=test_file1,
221+
benchmarking_file_path=test_file1,
222+
test_type=TestType.GENERATED_REGRESSION,
223+
),
224+
TestFile(
225+
original_file_path=test_file2,
226+
instrumented_behavior_file_path=test_file2,
227+
benchmarking_file_path=test_file2,
228+
test_type=TestType.GENERATED_REGRESSION,
229+
),
230+
]
231+
)
232+
233+
with patch("subprocess.run") as mock_run:
234+
mock_result = MagicMock()
235+
mock_result.stdout = ""
236+
mock_result.stderr = ""
237+
mock_result.returncode = 1
238+
mock_run.return_value = mock_result
239+
240+
try:
241+
run_jest_behavioral_tests(
242+
test_paths=mock_test_files,
243+
test_env={},
244+
cwd=tmpdir_path,
245+
project_root=tmpdir_path,
246+
)
247+
except Exception:
248+
pass
249+
250+
if mock_run.called:
251+
call_args = mock_run.call_args
252+
cmd = call_args[0][0]
253+
254+
roots_flags = []
255+
for i, arg in enumerate(cmd):
256+
if arg == "--roots" and i + 1 < len(cmd):
257+
roots_flags.append(cmd[i + 1])
258+
259+
# Should have two --roots flags (one for each directory)
260+
assert len(roots_flags) == 2, f"Expected 2 --roots flags, got {len(roots_flags)}"

0 commit comments

Comments
 (0)