Skip to main content
Glama
test_layout_transition.py13.2 kB
"""Tests for LayoutTransition component.""" import asyncio import json from unittest.mock import Mock import pytest from tests.components.conftest import ( assert_has_interface, assert_has_timing_props, assert_has_visibility_check, assert_valid_typescript, ) class TestLayoutTransitionBasic: """Basic LayoutTransition generation tests.""" def test_basic_generation(self, component_builder, theme_name): """Test basic LayoutTransition generation.""" tsx = component_builder.build_component("LayoutTransition", {}, theme_name) assert tsx is not None assert "LayoutTransition" in tsx assert_valid_typescript(tsx) assert_has_interface(tsx, "LayoutTransition") assert_has_timing_props(tsx) assert_has_visibility_check(tsx) class TestLayoutTransitionBuilderMethod: """Tests for LayoutTransition builder method.""" def test_add_to_composition_basic(self): """Test add_to_composition creates ComponentInstance.""" from chuk_motion.components.transitions.LayoutTransition.builder import ( add_to_composition, ) from chuk_motion.generator.composition_builder import CompositionBuilder builder = CompositionBuilder() result = add_to_composition(builder, start_time=0.0) assert result is builder assert len(builder.components) == 1 assert builder.components[0].component_type == "LayoutTransition" def test_add_to_composition_all_props(self): """Test all props are set correctly.""" from chuk_motion.components.transitions.LayoutTransition.builder import ( add_to_composition, ) from chuk_motion.generator.composition_builder import CompositionBuilder builder = CompositionBuilder() add_to_composition( builder, start_time=1.0, first_content={"type": "first"}, second_content={"type": "second"}, transition_type="slide_horizontal", transition_start=2.5, transition_duration=0.8, duration=10.0, ) props = builder.components[0].props assert props["firstContent"] == {"type": "first"} assert props["secondContent"] == {"type": "second"} assert props["transitionType"] == "slide_horizontal" assert props["transitionStart"] == int(2.5 * 30) # Default FPS is 30 assert props["transitionDuration"] == int(0.8 * 30) def test_add_to_composition_timing(self): """Test add_to_composition handles timing correctly.""" from chuk_motion.components.transitions.LayoutTransition.builder import ( add_to_composition, ) from chuk_motion.generator.composition_builder import CompositionBuilder builder = CompositionBuilder(fps=30) add_to_composition(builder, start_time=2.0, duration=5.0) component = builder.components[0] assert component.start_frame == 60 assert component.duration_frames == 150 class TestLayoutTransitionToolRegistration: """Tests for LayoutTransition MCP tool registration.""" def test_register_tool(self): """Test tool registration.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool mcp_mock = Mock() pm_mock = Mock() register_tool(mcp_mock, pm_mock) mcp_mock.tool.assert_called_once() def test_tool_execution_basic(self): """Test basic tool execution with valid content.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool # Mock ProjectManager with current_timeline pm_mock = Mock() timeline_mock = Mock() component_mock = Mock() component_mock.start_frame = 0 timeline_mock.add_component = Mock(return_value=component_mock) timeline_mock.frames_to_seconds = Mock(return_value=0.0) pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] # Create valid first and second content first_content = json.dumps({"type": "TitleScene", "config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run(tool_func(first_content=first_content, second_content=second_content)) result_data = json.loads(result) assert result_data["component"] == "LayoutTransition" # Verify component was added timeline_mock.add_component.assert_called_once() def test_tool_execution_all_params(self): """Test tool execution with all parameters.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() component_mock = Mock() component_mock.start_frame = 60 timeline_mock.add_component = Mock(return_value=component_mock) timeline_mock.frames_to_seconds = Mock(return_value=2.0) pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] first_content = json.dumps({"type": "Grid", "config": {"layout": "2x2"}}) second_content = json.dumps({"type": "Container", "config": {"position": "center"}}) result = asyncio.run( tool_func( first_content=first_content, second_content=second_content, transition_type="slide_horizontal", transition_start=1.5, transition_duration=0.8, duration=5.0, track="overlay", gap_before=1.0, ) ) result_data = json.loads(result) assert result_data["component"] == "LayoutTransition" assert result_data["start_time"] == 2.0 # Verify component was added with correct params call_args = timeline_mock.add_component.call_args assert call_args[1]["duration"] == 5.0 assert call_args[1]["track"] == "overlay" assert call_args[1]["gap_before"] == 1.0 @pytest.mark.parametrize( "transition_type", ["crossfade", "slide_horizontal", "slide_vertical", "cube_rotate", "parallax_push"], ) def test_tool_execution_transition_types(self, transition_type): """Test tool execution with different transition types.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() component_mock = Mock() component_mock.start_frame = 0 timeline_mock.add_component = Mock(return_value=component_mock) timeline_mock.frames_to_seconds = Mock(return_value=0.0) pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] first_content = json.dumps({"type": "TitleScene", "config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run( tool_func( first_content=first_content, second_content=second_content, transition_type=transition_type, ) ) result_data = json.loads(result) assert result_data["component"] == "LayoutTransition" def test_tool_execution_invalid_transition_type(self): """Test tool execution with invalid transition type.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] first_content = json.dumps({"type": "TitleScene", "config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run( tool_func( first_content=first_content, second_content=second_content, transition_type="invalid_type", ) ) result_data = json.loads(result) assert "error" in result_data assert "Invalid transition_type" in result_data["error"] def test_tool_execution_invalid_json_first_content(self): """Test tool execution with invalid JSON in first_content.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] result = asyncio.run( tool_func( first_content="invalid json", second_content='{"type":"TitleScene","config":{"text":"Second"}}', ) ) result_data = json.loads(result) assert "error" in result_data assert "Invalid JSON" in result_data["error"] def test_tool_execution_invalid_json_second_content(self): """Test tool execution with invalid JSON in second_content.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] result = asyncio.run( tool_func( first_content='{"type":"TitleScene","config":{"text":"First"}}', second_content="invalid json", ) ) result_data = json.loads(result) assert "error" in result_data assert "Invalid JSON" in result_data["error"] def test_tool_execution_invalid_content_format(self): """Test tool execution with invalid content format.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool pm_mock = Mock() timeline_mock = Mock() component_mock = Mock() component_mock.start_frame = 0 timeline_mock.add_component = Mock(return_value=component_mock) timeline_mock.frames_to_seconds = Mock(return_value=0.0) pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] # Missing 'type' key first_content = json.dumps({"config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run(tool_func(first_content=first_content, second_content=second_content)) result_data = json.loads(result) assert "error" in result_data # Error message may vary depending on parse_nested_component implementation assert "Invalid content format" in result_data["error"] or "error" in result_data def test_tool_execution_no_project(self): """Test tool execution without active project.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool # Mock ProjectManager with no current_timeline pm_mock = Mock() pm_mock.current_timeline = None mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] first_content = json.dumps({"type": "TitleScene", "config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run(tool_func(first_content=first_content, second_content=second_content)) result_data = json.loads(result) assert "error" in result_data assert "No active project" in result_data["error"] def test_tool_execution_error_handling(self): """Test tool handles errors gracefully.""" from chuk_motion.components.transitions.LayoutTransition.tool import register_tool # Mock ProjectManager with timeline that raises an error pm_mock = Mock() timeline_mock = Mock() timeline_mock.add_component = Mock(side_effect=Exception("Test error")) pm_mock.current_timeline = timeline_mock mcp_mock = Mock() register_tool(mcp_mock, pm_mock) tool_func = mcp_mock.tool.call_args[0][0] first_content = json.dumps({"type": "TitleScene", "config": {"text": "First"}}) second_content = json.dumps({"type": "TitleScene", "config": {"text": "Second"}}) result = asyncio.run(tool_func(first_content=first_content, second_content=second_content)) result_data = json.loads(result) assert "error" in result_data assert "Test error" in result_data["error"]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server