ctrf_rs/
test.rs

1use crate::{
2    extra::Extra,
3    impl_extra,
4    test::{attachment::Attachment, step::Step},
5};
6
7use std::{collections::HashMap, path::PathBuf, time::Duration, vec};
8
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12pub mod attachment;
13pub mod step;
14
15#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq)]
16#[serde(rename_all = "camelCase")]
17pub enum Status {
18    Pending,
19    Skipped,
20    Failed,
21    Passed,
22    Other,
23}
24
25#[derive(Deserialize, Serialize, Debug, PartialEq)]
26#[serde(rename_all = "camelCase")]
27pub struct Test {
28    name: String,
29    status: Status,
30    duration: u64,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub start: Option<u64>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub stop: Option<u64>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub suite: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub message: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub trace: Option<String>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub line: Option<usize>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub ai: Option<String>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub raw_status: Option<String>,
47    #[serde(skip_serializing_if = "Vec::is_empty")]
48    pub tags: Vec<String>,
49    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
50    pub test_type: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub filepath: Option<PathBuf>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub retries: Option<usize>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub flaky: Option<bool>,
57    #[serde(skip_serializing_if = "Vec::is_empty")]
58    pub stdout: Vec<String>,
59    #[serde(skip_serializing_if = "Vec::is_empty")]
60    pub stderr: Vec<String>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub thread_id: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub browser: Option<String>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub device: Option<String>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub screenshot: Option<String>,
69    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
70    pub parameters: HashMap<String, Value>,
71    #[serde(skip_serializing_if = "Vec::is_empty")]
72    pub steps: Vec<Step>,
73    #[serde(skip_serializing_if = "Vec::is_empty")]
74    pub attachments: Vec<Attachment>,
75    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
76    extra: HashMap<String, Value>,
77}
78
79impl Test {
80    pub fn new(name: String, status: Status, duration: Duration) -> Self {
81        Self {
82            name,
83            status,
84            duration: duration.as_millis() as u64,
85            start: None,
86            stop: None,
87            suite: None,
88            message: None,
89            trace: None,
90            line: None,
91            ai: None,
92            raw_status: None,
93            tags: vec![],
94            test_type: None,
95            filepath: None,
96            retries: None,
97            flaky: None,
98            stdout: vec![],
99            stderr: vec![],
100            thread_id: None,
101            browser: None,
102            device: None,
103            screenshot: None,
104            parameters: HashMap::new(),
105            steps: vec![],
106            attachments: vec![],
107            extra: HashMap::new(),
108        }
109    }
110
111    pub fn status(&self) -> Status {
112        self.status
113    }
114
115    pub fn suite(&self) -> &Option<String> {
116        &self.suite
117    }
118}
119
120impl_extra!(Test);