ralph_workflow/rendering/
ui_event.rs1use crate::reducer::ui_event::UIEvent;
7
8#[must_use]
13pub fn render_ui_event(event: &UIEvent) -> String {
14 match event {
15 UIEvent::PhaseTransition { to, .. } => {
16 format!("{} {}", UIEvent::phase_emoji(to), to)
17 }
18 UIEvent::IterationProgress { current, total } => {
19 format!("🔄 Development iteration {current}/{total}")
20 }
21 UIEvent::ReviewProgress { pass, total } => {
22 format!("👁 Review pass {pass}/{total}")
23 }
24 UIEvent::AgentActivity { agent, message } => {
25 format!("🤖 [{agent}] {message}")
26 }
27 UIEvent::PushCompleted {
28 remote,
29 branch,
30 commit_sha,
31 } => {
32 let short = &commit_sha[..7.min(commit_sha.len())];
33 format!("⬆️ Pushed {short} to {remote}/{branch}")
34 }
35 UIEvent::PushFailed {
36 remote,
37 branch,
38 error,
39 } => {
40 format!("⚠️ Push failed for {remote}/{branch}: {error}")
41 }
42 UIEvent::PullRequestCreated { url, number } => {
43 if *number > 0 {
44 format!("🔀 PR created #{number}: {url}")
45 } else {
46 format!("🔀 PR created: {url}")
47 }
48 }
49 UIEvent::PullRequestFailed { error } => {
50 format!("⚠️ PR creation failed: {error}")
51 }
52 UIEvent::XmlOutput {
53 xml_type,
54 content,
55 context,
56 } => super::xml::render_xml(xml_type, content, context),
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::reducer::event::PipelinePhase;
64 use crate::reducer::ui_event::{XmlOutputContext, XmlOutputType};
65
66 #[test]
67 fn test_render_phase_transition() {
68 let event = UIEvent::PhaseTransition {
69 from: Some(PipelinePhase::Planning),
70 to: PipelinePhase::Development,
71 };
72 let output = render_ui_event(&event);
73 assert!(output.contains("🔨"));
74 assert!(output.contains("Development"));
75 }
76
77 #[test]
78 fn test_render_iteration_progress() {
79 let event = UIEvent::IterationProgress {
80 current: 2,
81 total: 5,
82 };
83 let output = render_ui_event(&event);
84 assert!(output.contains("2/5"));
85 assert!(output.contains("🔄"));
86 }
87
88 #[test]
89 fn test_render_review_progress() {
90 let event = UIEvent::ReviewProgress { pass: 1, total: 3 };
91 let output = render_ui_event(&event);
92 assert!(output.contains("1/3"));
93 assert!(output.contains("👁"));
94 }
95
96 #[test]
97 fn test_render_agent_activity() {
98 let event = UIEvent::AgentActivity {
99 agent: "claude".to_string(),
100 message: "Processing request".to_string(),
101 };
102 let output = render_ui_event(&event);
103 assert!(output.contains("[claude]"));
104 assert!(output.contains("Processing request"));
105 }
106
107 #[test]
108 fn test_render_xml_output_routes_to_xml_module() {
109 let event = UIEvent::XmlOutput {
110 xml_type: XmlOutputType::DevelopmentResult,
111 content: r"<ralph-development-result>
112<ralph-status>completed</ralph-status>
113<ralph-summary>Done</ralph-summary>
114</ralph-development-result>"
115 .to_string(),
116 context: Some(XmlOutputContext::default()),
117 };
118 let output = render_ui_event(&event);
119 assert!(output.contains("✅") || output.contains("Completed"));
121 assert!(output.contains("Done"));
122 }
123
124 #[test]
125 fn test_phase_emoji_via_ui_event() {
126 let phases = [
128 PipelinePhase::Planning,
129 PipelinePhase::Development,
130 PipelinePhase::Review,
131 PipelinePhase::CommitMessage,
132 PipelinePhase::FinalValidation,
133 PipelinePhase::Finalizing,
134 PipelinePhase::Complete,
135 PipelinePhase::Interrupted,
136 ];
137 for phase in phases {
138 let emoji = UIEvent::phase_emoji(&phase);
139 assert!(!emoji.is_empty(), "Phase {phase:?} should have an emoji");
140 }
141 }
142}