Skip to main content

ralph_workflow/rendering/
ui_event.rs

1//! UI event rendering dispatch.
2//!
3//! This is the single entrypoint for all UI event rendering.
4//! The event loop calls `render_ui_event()` and displays the result.
5
6use crate::reducer::ui_event::UIEvent;
7
8/// Render a UIEvent to a displayable string.
9///
10/// This is the single entrypoint for all UI event rendering.
11/// The event loop calls this function and displays the result.
12pub fn render_ui_event(event: &UIEvent) -> String {
13    match event {
14        UIEvent::PhaseTransition { to, .. } => {
15            format!("{} {}", UIEvent::phase_emoji(to), to)
16        }
17        UIEvent::IterationProgress { current, total } => {
18            format!("🔄 Development iteration {}/{}", current, total)
19        }
20        UIEvent::ReviewProgress { pass, total } => {
21            format!("👁 Review pass {}/{}", pass, total)
22        }
23        UIEvent::AgentActivity { agent, message } => {
24            format!("🤖 [{}] {}", agent, message)
25        }
26        UIEvent::XmlOutput {
27            xml_type,
28            content,
29            context,
30        } => super::xml::render_xml(xml_type, content, context),
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use crate::reducer::event::PipelinePhase;
38    use crate::reducer::ui_event::{XmlOutputContext, XmlOutputType};
39
40    #[test]
41    fn test_render_phase_transition() {
42        let event = UIEvent::PhaseTransition {
43            from: Some(PipelinePhase::Planning),
44            to: PipelinePhase::Development,
45        };
46        let output = render_ui_event(&event);
47        assert!(output.contains("🔨"));
48        assert!(output.contains("Development"));
49    }
50
51    #[test]
52    fn test_render_iteration_progress() {
53        let event = UIEvent::IterationProgress {
54            current: 2,
55            total: 5,
56        };
57        let output = render_ui_event(&event);
58        assert!(output.contains("2/5"));
59        assert!(output.contains("🔄"));
60    }
61
62    #[test]
63    fn test_render_review_progress() {
64        let event = UIEvent::ReviewProgress { pass: 1, total: 3 };
65        let output = render_ui_event(&event);
66        assert!(output.contains("1/3"));
67        assert!(output.contains("👁"));
68    }
69
70    #[test]
71    fn test_render_agent_activity() {
72        let event = UIEvent::AgentActivity {
73            agent: "claude".to_string(),
74            message: "Processing request".to_string(),
75        };
76        let output = render_ui_event(&event);
77        assert!(output.contains("[claude]"));
78        assert!(output.contains("Processing request"));
79    }
80
81    #[test]
82    fn test_render_xml_output_routes_to_xml_module() {
83        let event = UIEvent::XmlOutput {
84            xml_type: XmlOutputType::DevelopmentResult,
85            content: r#"<ralph-development-result>
86<ralph-status>completed</ralph-status>
87<ralph-summary>Done</ralph-summary>
88</ralph-development-result>"#
89                .to_string(),
90            context: Some(XmlOutputContext::default()),
91        };
92        let output = render_ui_event(&event);
93        // Should be semantically rendered, not raw XML
94        assert!(output.contains("✅") || output.contains("Completed"));
95        assert!(output.contains("Done"));
96    }
97
98    #[test]
99    fn test_phase_emoji_via_ui_event() {
100        // Verify all phases have non-empty emojis via UIEvent::phase_emoji
101        let phases = [
102            PipelinePhase::Planning,
103            PipelinePhase::Development,
104            PipelinePhase::Review,
105            PipelinePhase::CommitMessage,
106            PipelinePhase::FinalValidation,
107            PipelinePhase::Finalizing,
108            PipelinePhase::Complete,
109            PipelinePhase::Interrupted,
110        ];
111        for phase in phases {
112            let emoji = UIEvent::phase_emoji(&phase);
113            assert!(!emoji.is_empty(), "Phase {:?} should have an emoji", phase);
114        }
115    }
116}