generated from coulomb/repo-seed
feat: add refactored testdrive-jsui capability with consolidated architecture
Complete integration of refactored testdrive-jsui capability: ## Refactored Architecture - js/ - All JavaScript source (controls, components, core) - static/ - CSS, images, templates - src/testdrive_jsui/ - Python package - tests/ - Python tests ## Plugin Self-Declaration - get_plugin_source_dir() - plugin declares own location - get_asset_paths() - organized asset paths - No hardcoded discovery logic ## Merged Content - Baseline UI scaffold (tutorials, LICENSE, INTRODUCTION.md) - Refactored capability implementation - Comprehensive documentation Ready for standalone use or integration with markitect. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
187
src/testdrive_jsui/testing/integration.py
Normal file
187
src/testdrive_jsui/testing/integration.py
Normal file
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Python-JavaScript Integration Bridge
|
||||
|
||||
Provides seamless integration between Python test suite and JavaScript tests.
|
||||
Enables pytest to discover and run JavaScript tests as if they were Python tests.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Generator
|
||||
from .js_test_runner import JavaScriptTestRunner, JSTestResult
|
||||
|
||||
|
||||
class PythonJSBridge:
|
||||
"""
|
||||
Bridge between Python and JavaScript testing environments.
|
||||
Enables JavaScript tests to be run from Python test suite.
|
||||
"""
|
||||
|
||||
def __init__(self, capability_root: Optional[Path] = None):
|
||||
"""
|
||||
Initialize the bridge.
|
||||
|
||||
Args:
|
||||
capability_root: Root directory of the testdrive-jsui capability
|
||||
"""
|
||||
self.js_runner = JavaScriptTestRunner(capability_root)
|
||||
|
||||
def pytest_collect_file(self, path: Path, parent) -> Optional["JSTestFile"]:
|
||||
"""
|
||||
Pytest hook to collect JavaScript test files.
|
||||
"""
|
||||
if path.suffix == ".js" and "test" in path.name:
|
||||
if self._is_js_test_file(path):
|
||||
return JSTestFile.from_parent(parent, fspath=path)
|
||||
return None
|
||||
|
||||
def _is_js_test_file(self, path: Path) -> bool:
|
||||
"""Check if a file is a JavaScript test file."""
|
||||
return (
|
||||
path.name.startswith("test-") or
|
||||
path.name.endswith(".test.js") or
|
||||
"test" in path.parts
|
||||
)
|
||||
|
||||
def run_all_js_tests(self) -> JSTestResult:
|
||||
"""Run all JavaScript tests."""
|
||||
return self.js_runner.run_js_tests()
|
||||
|
||||
def run_js_test_by_name(self, test_name: str) -> JSTestResult:
|
||||
"""Run a specific JavaScript test by name."""
|
||||
return self.js_runner.run_specific_test(test_name)
|
||||
|
||||
|
||||
class JSTestFile(pytest.File):
|
||||
"""
|
||||
Represents a JavaScript test file in pytest.
|
||||
"""
|
||||
|
||||
def collect(self) -> Generator["JSTestItem", None, None]:
|
||||
"""Collect test items from this JavaScript file."""
|
||||
# For now, treat each JS file as a single test item
|
||||
# In the future, this could be enhanced to parse individual test functions
|
||||
yield JSTestItem.from_parent(self, name=self.fspath.basename)
|
||||
|
||||
|
||||
class JSTestItem(pytest.Item):
|
||||
"""
|
||||
Represents a JavaScript test item in pytest.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, parent: JSTestFile):
|
||||
super().__init__(name, parent)
|
||||
self.js_runner = JavaScriptTestRunner()
|
||||
|
||||
def runtest(self) -> None:
|
||||
"""Run the JavaScript test."""
|
||||
test_file = str(self.fspath.relative_to(self.js_runner.js_dir))
|
||||
result = self.js_runner.run_specific_test(test_file)
|
||||
|
||||
if not result.success:
|
||||
failure_messages = []
|
||||
for failure in result.failures:
|
||||
failure_messages.append(f"{failure.get('title', 'Unknown')}: {failure.get('message', 'Unknown error')}")
|
||||
|
||||
failure_msg = "\n".join(failure_messages) if failure_messages else result.stderr
|
||||
raise JSTestFailure(failure_msg, result)
|
||||
|
||||
def repr_failure(self, excinfo) -> str:
|
||||
"""Represent test failure."""
|
||||
if isinstance(excinfo.value, JSTestFailure):
|
||||
return f"JavaScript test failed:\n{excinfo.value.message}"
|
||||
return super().repr_failure(excinfo)
|
||||
|
||||
def reportinfo(self):
|
||||
"""Report information about this test item."""
|
||||
return self.fspath, 0, f"JavaScript test: {self.name}"
|
||||
|
||||
|
||||
class JSTestFailure(Exception):
|
||||
"""Exception raised when a JavaScript test fails."""
|
||||
|
||||
def __init__(self, message: str, result: JSTestResult):
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
self.result = result
|
||||
|
||||
|
||||
# Pytest fixtures for JavaScript testing
|
||||
@pytest.fixture
|
||||
def js_test_runner() -> JavaScriptTestRunner:
|
||||
"""Provide a JavaScript test runner instance."""
|
||||
return JavaScriptTestRunner()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def js_bridge() -> PythonJSBridge:
|
||||
"""Provide a Python-JavaScript bridge instance."""
|
||||
return PythonJSBridge()
|
||||
|
||||
|
||||
# Pytest markers
|
||||
def pytest_configure(config):
|
||||
"""Configure pytest markers for JavaScript tests."""
|
||||
config.addinivalue_line(
|
||||
"markers", "javascript: mark test as JavaScript integration test"
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers", "js_component: mark test as JavaScript component test"
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers", "js_integration: mark test as JavaScript integration test"
|
||||
)
|
||||
|
||||
|
||||
# Test discovery function for manual use
|
||||
def discover_js_tests(capability_root: Optional[Path] = None) -> List[str]:
|
||||
"""
|
||||
Discover all JavaScript test files.
|
||||
|
||||
Args:
|
||||
capability_root: Root directory of the testdrive-jsui capability
|
||||
|
||||
Returns:
|
||||
List of JavaScript test file paths
|
||||
"""
|
||||
runner = JavaScriptTestRunner(capability_root)
|
||||
return runner.list_available_tests()
|
||||
|
||||
|
||||
# Main test execution functions
|
||||
def test_javascript_components(js_test_runner: JavaScriptTestRunner) -> None:
|
||||
"""
|
||||
Main test function that runs all JavaScript component tests.
|
||||
This can be called from Python test suite.
|
||||
"""
|
||||
result = js_test_runner.run_js_tests(verbose=True)
|
||||
|
||||
assert result.success, f"JavaScript tests failed: {result.failures}"
|
||||
assert result.tests_total > 0, "No JavaScript tests were found or executed"
|
||||
assert result.tests_passed > 0, "No JavaScript tests passed"
|
||||
|
||||
|
||||
def test_javascript_integration(js_test_runner: JavaScriptTestRunner) -> None:
|
||||
"""
|
||||
Test JavaScript integration components.
|
||||
"""
|
||||
integration_tests = [
|
||||
test for test in js_test_runner.list_available_tests()
|
||||
if "integration" in test.lower()
|
||||
]
|
||||
|
||||
if integration_tests:
|
||||
result = js_test_runner.run_js_tests(test_patterns=integration_tests)
|
||||
assert result.success, f"JavaScript integration tests failed: {result.failures}"
|
||||
|
||||
|
||||
def test_javascript_environment(js_test_runner: JavaScriptTestRunner) -> None:
|
||||
"""
|
||||
Test that JavaScript testing environment is properly set up.
|
||||
"""
|
||||
assert js_test_runner.check_node_environment(), "Node.js environment not available"
|
||||
|
||||
info = js_test_runner.get_test_info()
|
||||
assert info["node_available"], "Node.js not available"
|
||||
assert info["package_json_exists"], "package.json not found"
|
||||
assert len(info["available_tests"]) > 0, "No JavaScript tests found"
|
||||
Reference in New Issue
Block a user