codeflash/tests/test_languages/test_javascript_requirements.py
2026-03-31 08:24:31 +02:00

274 lines
12 KiB
Python

"""Tests for JavaScript requirements verification.
Tests the verify_requirements function that checks Node.js, npm, and test framework availability.
"""
import json
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
from codeflash.languages.javascript.support import JavaScriptSupport
class TestVerifyRequirements:
"""Tests for JavaScriptSupport.verify_requirements()."""
@pytest.fixture
def js_support(self):
"""Create a JavaScriptSupport instance."""
return JavaScriptSupport()
@pytest.fixture
def project_with_jest(self, tmp_path):
"""Create a project directory with Jest installed."""
node_modules = tmp_path / "node_modules"
node_modules.mkdir()
(node_modules / "jest").mkdir()
(node_modules / "codeflash").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test-project", "devDependencies": {"jest": "^29.0.0"}}))
return tmp_path
@pytest.fixture
def project_with_vitest(self, tmp_path):
"""Create a project directory with Vitest installed."""
node_modules = tmp_path / "node_modules"
node_modules.mkdir()
(node_modules / "vitest").mkdir()
(node_modules / "codeflash").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test-project", "devDependencies": {"vitest": "^2.0.0"}}))
return tmp_path
@pytest.fixture
def project_without_node_modules(self, tmp_path):
"""Create a project directory without node_modules."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test-project"}))
return tmp_path
@pytest.fixture
def project_without_jest(self, tmp_path):
"""Create a project directory with node_modules but without Jest."""
node_modules = tmp_path / "node_modules"
node_modules.mkdir()
(node_modules / "some-other-package").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test-project"}))
return tmp_path
def test_verify_requirements_success_with_jest(self, js_support, project_with_jest):
"""Test successful verification when Jest is installed."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_with_jest, "jest")
assert success is True
assert errors == []
def test_verify_requirements_success_with_vitest(self, js_support, project_with_vitest):
"""Test successful verification when Vitest is installed."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_with_vitest, "vitest")
assert success is True
assert errors == []
def test_verify_requirements_fails_without_node(self, js_support, project_with_jest):
"""Test verification fails when Node.js is not installed."""
with patch("subprocess.run") as mock_run:
mock_run.side_effect = FileNotFoundError("node not found")
success, errors = js_support.verify_requirements(project_with_jest, "jest")
assert success is False
assert len(errors) >= 1
node_error_found = any("Node.js" in error.message for error in errors)
assert node_error_found is True
def test_verify_requirements_fails_without_npm(self, js_support, project_with_jest):
"""Test verification fails when npm is not available."""
def mock_run_side_effect(cmd, **kwargs):
if cmd[0] == "node":
return MagicMock(returncode=0)
if cmd[0] == "npm":
raise FileNotFoundError("npm not found")
return MagicMock(returncode=0)
with patch("subprocess.run", side_effect=mock_run_side_effect):
success, errors = js_support.verify_requirements(project_with_jest, "jest")
assert success is False
npm_error_found = any("npm" in error.message for error in errors)
assert npm_error_found is True
def test_verify_requirements_fails_without_node_modules(self, js_support, project_without_node_modules):
"""Test verification fails when node_modules doesn't exist."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_without_node_modules, "jest")
assert success is False
assert len(errors) == 1
expected_message = (
f"node_modules not found in {project_without_node_modules}. "
f"Please run 'npm install' to install dependencies."
)
assert errors[0].message == expected_message
def test_verify_requirements_fails_without_test_framework(self, js_support, project_without_jest):
"""Test verification fails when test framework is not installed."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_without_jest, "jest")
assert success is False
assert len(errors) == 1
expected_message = "jest is not installed. Please run 'npm install --save-dev jest' to install it."
assert errors[0].message == expected_message
def test_verify_requirements_returns_multiple_errors(self, js_support, project_without_node_modules):
"""Test that multiple errors can be returned."""
with patch("subprocess.run") as mock_run:
mock_run.side_effect = FileNotFoundError("command not found")
success, errors = js_support.verify_requirements(project_without_node_modules, "jest")
assert success is False
assert len(errors) >= 2
# Should have errors for Node.js, npm, and node_modules
error_text = " ".join(e.message for e in errors)
assert "Node.js" in error_text
assert "npm" in error_text
def test_verify_requirements_vitest_not_installed(self, js_support, project_with_jest):
"""Test verification fails when Vitest is requested but only Jest is installed."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_with_jest, "vitest")
assert success is False
assert len(errors) == 1
expected_message = "vitest is not installed. Please run 'npm install --save-dev vitest' to install it."
assert errors[0].message == expected_message
def test_verify_requirements_jest_not_installed(self, js_support, project_with_vitest):
"""Test verification fails when Jest is requested but only Vitest is installed."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_with_vitest, "jest")
assert success is False
assert len(errors) == 1
expected_message = "jest is not installed. Please run 'npm install --save-dev jest' to install it."
assert errors[0].message == expected_message
class TestSetupTestConfig:
"""Tests for JavaScriptSupport.setup_test_config() early-exit behavior."""
@pytest.fixture
def js_support(self):
return JavaScriptSupport()
def test_setup_test_config_returns_false_on_abort_error(self, js_support, tmp_path):
"""setup_test_config returns False when verify_js_requirements reports a should_abort error."""
from codeflash.languages.base import SetupError
abort_error = SetupError("Node.js is not installed", should_abort=True)
with (
patch("codeflash.languages.javascript.test_runner.find_node_project_root", return_value=tmp_path.resolve()),
patch("codeflash.languages.javascript.optimizer.verify_js_requirements", return_value=[abort_error]),
):
test_cfg = MagicMock()
result = js_support.setup_test_config(test_cfg, tmp_path.resolve(), current_worktree=None)
assert result is False
def test_setup_test_config_returns_true_on_no_errors(self, js_support, tmp_path):
"""setup_test_config returns True when verify_js_requirements reports no errors."""
with (
patch("codeflash.languages.javascript.test_runner.find_node_project_root", return_value=tmp_path.resolve()),
patch("codeflash.languages.javascript.optimizer.verify_js_requirements", return_value=[]),
):
test_cfg = MagicMock()
result = js_support.setup_test_config(test_cfg, tmp_path.resolve(), current_worktree=None)
assert result is True
def test_setup_test_config_returns_false_when_project_root_is_none(self, js_support, tmp_path):
"""setup_test_config returns False when find_node_project_root returns None."""
with patch("codeflash.languages.javascript.test_runner.find_node_project_root", return_value=None):
test_cfg = MagicMock()
result = js_support.setup_test_config(test_cfg, tmp_path.resolve(), current_worktree=None)
assert result is False
class TestVerifyRequirementsIntegration:
"""Integration tests for verify_requirements with real filesystem."""
@pytest.fixture
def js_support(self):
"""Create a JavaScriptSupport instance."""
return JavaScriptSupport()
def test_verify_on_real_vitest_project(self, js_support):
"""Test verification on the real vitest sample project."""
project_root = Path(__file__).parent.parent.parent / "code_to_optimize" / "js" / "code_to_optimize_vitest"
if not project_root.exists():
pytest.skip("code_to_optimize_vitest directory not found")
node_modules = project_root / "node_modules"
if not node_modules.exists():
pytest.skip("node_modules not installed in vitest project")
# This test verifies the real project structure
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_root, "vitest")
# If vitest is installed, should succeed
vitest_installed = (node_modules / "vitest").exists()
if vitest_installed:
assert success is True
assert errors == []
else:
assert success is False
assert len(errors) >= 1
def test_verify_on_real_jest_project(self, js_support):
"""Test verification on the real Jest sample project."""
project_root = Path(__file__).parent.parent.parent / "code_to_optimize" / "js" / "code_to_optimize_ts"
if not project_root.exists():
pytest.skip("code_to_optimize_ts directory not found")
node_modules = project_root / "node_modules"
if not node_modules.exists():
pytest.skip("node_modules not installed in jest project")
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
success, errors = js_support.verify_requirements(project_root, "jest")
jest_installed = (node_modules / "jest").exists()
if jest_installed:
assert success is True
assert errors == []
else:
assert success is False
assert len(errors) >= 1