1use serde::{Deserialize, Serialize};
2
3use crate::doc_entry::DocEntry;
4
5#[derive(Debug, Clone, Serialize)]
6pub struct Ticket {
7 pub id: String,
8 #[serde(skip_serializing_if = "Option::is_none")]
9 pub url: Option<String>,
10}
11
12pub const SCHEMA_URL: &str = "https://executable-stories.dev/schemas/raw-run.schema.json";
16
17#[derive(Serialize, Clone)]
18#[serde(rename_all = "camelCase")]
19pub struct RawRun {
20 pub schema_version: u32,
21 pub test_cases: Vec<RawTestCase>,
22 #[serde(skip_serializing_if = "Vec::is_empty")]
23 pub features: Vec<RawFeature>,
24 pub project_root: String,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub started_at_ms: Option<f64>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub finished_at_ms: Option<f64>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub ci: Option<RawCIInfo>,
31 #[serde(skip_serializing_if = "Option::is_none")]
34 pub git_sha: Option<String>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub package_version: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
43 pub run_scope: Option<String>,
44}
45
46#[derive(Serialize, Clone, Default, Debug)]
51#[serde(rename_all = "camelCase")]
52pub struct RawFeature {
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub source_file: Option<String>,
55 pub title: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub kind: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub narrative: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub tags: Option<Vec<String>>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub glossary: Option<Vec<RawGlossaryTerm>>,
64}
65
66#[derive(Serialize, Clone, Default, Debug)]
68#[serde(rename_all = "camelCase")]
69pub struct RawGlossaryTerm {
70 pub term: String,
71 pub definition: String,
72}
73
74#[derive(Serialize, Clone, Default)]
75#[serde(rename_all = "camelCase")]
76pub struct RawTestCase {
77 pub status: String,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub external_id: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub title: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub story: Option<StoryMeta>,
84 #[serde(skip_serializing_if = "Option::is_none")]
87 pub source_file: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub duration_ms: Option<f64>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub error: Option<RawError>,
92 pub retry: i32,
93 pub retries: i32,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub attachments: Option<Vec<RawAttachment>>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub step_events: Option<Vec<RawStepEvent>>,
98}
99
100#[derive(Serialize, Clone)]
101#[serde(rename_all = "camelCase")]
102pub struct StoryMeta {
103 pub scenario: String,
104 pub steps: Vec<StoryStep>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub tags: Option<Vec<String>>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub tickets: Option<Vec<Ticket>>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub covers: Option<Vec<String>>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub meta: Option<serde_json::Value>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub docs: Option<Vec<DocEntry>>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub source_order: Option<u32>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub otel_spans: Option<Vec<serde_json::Value>>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub started_at_ms: Option<f64>,
122}
123
124#[derive(Serialize, Clone)]
125#[serde(rename_all = "camelCase")]
126pub struct StoryStep {
127 pub keyword: String,
128 pub text: String,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub id: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub mode: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub wrapped: Option<bool>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub duration_ms: Option<f64>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub docs: Option<Vec<DocEntry>>,
139 #[serde(skip_serializing_if = "Option::is_none")]
145 pub assertions: Option<u32>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub started_at_ms: Option<f64>,
149}
150
151#[derive(Serialize, Clone)]
152#[serde(rename_all = "camelCase")]
153pub struct RawError {
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub message: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub stack: Option<String>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct RawAttachment {
163 pub name: String,
164 pub media_type: String,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub path: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub body: Option<String>,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub encoding: Option<String>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub charset: Option<String>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub file_name: Option<String>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub byte_length: Option<usize>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub step_index: Option<usize>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub step_id: Option<String>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184#[serde(rename_all = "camelCase")]
185pub struct RawStepEvent {
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub index: Option<usize>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub title: Option<String>,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub status: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub duration_ms: Option<f64>,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct RawCIInfo {
199 pub name: String,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub url: Option<String>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub build_number: Option<String>,
204}