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