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);