ferridriver_test/reporter/
github.rs1use async_trait::async_trait;
7
8use super::{Reporter, ReporterEvent};
9use crate::model::TestStatus;
10
11pub struct GithubReporter {
19 delegate: Box<dyn Reporter>,
20 enabled: bool,
21}
22
23impl GithubReporter {
24 #[must_use]
29 pub fn new(delegate: Box<dyn Reporter>) -> Self {
30 let enabled = std::env::var("GITHUB_ACTIONS").is_ok();
31 Self { delegate, enabled }
32 }
33
34 pub fn with_enabled(mut self, enabled: bool) -> Self {
36 self.enabled = enabled;
37 self
38 }
39}
40
41#[async_trait]
42impl Reporter for GithubReporter {
43 async fn on_event(&mut self, event: &ReporterEvent) {
44 if self.enabled {
45 if let ReporterEvent::TestFinished { test_id, outcome } = event {
46 if matches!(outcome.status, TestStatus::Failed | TestStatus::TimedOut) {
47 let title = test_id.full_name().replace(['\r', '\n'], " ");
48 let message = outcome
49 .error
50 .as_ref()
51 .map(|e| escape(&e.message))
52 .unwrap_or_else(|| "test failed".to_string());
53 let file = test_id.file.replace(['\r', '\n'], " ");
54 let line = test_id.line.unwrap_or(1);
55 println!("::error file={file},line={line},title={title}::{message}");
58 }
59 }
60 }
61 self.delegate.on_event(event).await;
62 }
63
64 async fn finalize(&mut self) -> ferridriver::error::Result<()> {
65 self.delegate.finalize().await
66 }
67}
68
69fn escape(s: &str) -> String {
70 s.replace('%', "%25").replace('\r', "%0D").replace('\n', "%0A")
73}