thirtyfour_testing_library_ext/options/
role.rs1use crate::options::common::{TestingLibraryOptions, TextMatch};
2use serde::{Serialize, Serializer};
3
4#[cfg(test)]
5use crate::options::common::process_raw_javascript_markers;
6
7#[cfg(test)]
8use serde_json::Value;
9
10#[derive(Debug, Clone, Default, Serialize)]
12#[serde(rename_all = "camelCase")]
13pub struct ValueOptions {
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub min: Option<i32>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub max: Option<i32>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub now: Option<i32>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub text: Option<TextMatch>,
26}
27
28#[derive(Debug, Clone, Default, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct ByRoleOptions {
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub hidden: Option<bool>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub name: Option<TextMatch>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub description: Option<TextMatch>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub selected: Option<bool>,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub busy: Option<bool>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub checked: Option<bool>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub pressed: Option<bool>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub suggest: Option<bool>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub current: Option<CurrentState>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub expanded: Option<bool>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub query_fallbacks: Option<bool>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub level: Option<u8>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub value: Option<ValueOptions>,
71}
72
73#[derive(Debug, Clone)]
75pub enum CurrentState {
76 False,
78 True,
80 Page,
82 Step,
84 Location,
86 Date,
88 Time,
90 Custom(String),
92}
93
94impl Serialize for CurrentState {
95 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96 where
97 S: Serializer,
98 {
99 match self {
100 CurrentState::False => false.serialize(serializer),
101 CurrentState::True => true.serialize(serializer),
102 CurrentState::Page => "page".serialize(serializer),
103 CurrentState::Step => "step".serialize(serializer),
104 CurrentState::Location => "location".serialize(serializer),
105 CurrentState::Date => "date".serialize(serializer),
106 CurrentState::Time => "time".serialize(serializer),
107 CurrentState::Custom(s) => s.serialize(serializer),
108 }
109 }
110}
111
112impl ByRoleOptions {
113 pub fn new() -> Self {
115 Self::default()
116 }
117
118 pub fn hidden(mut self, hidden: bool) -> Self {
120 self.hidden = Some(hidden);
121 self
122 }
123
124 pub fn name(mut self, name: impl Into<String>) -> Self {
127 let name_str = name.into();
128 self.name = Some(TextMatch::from(name_str));
129 self
130 }
131
132 pub fn description(mut self, description: impl Into<String>) -> Self {
135 let desc_str = description.into();
136 self.description = Some(TextMatch::from(desc_str));
137 self
138 }
139
140 pub fn selected(mut self, selected: bool) -> Self {
142 self.selected = Some(selected);
143 self
144 }
145
146 pub fn busy(mut self, busy: bool) -> Self {
148 self.busy = Some(busy);
149 self
150 }
151
152 pub fn checked(mut self, checked: bool) -> Self {
154 self.checked = Some(checked);
155 self
156 }
157
158 pub fn pressed(mut self, pressed: bool) -> Self {
160 self.pressed = Some(pressed);
161 self
162 }
163
164 pub fn suggest(mut self, suggest: bool) -> Self {
166 self.suggest = Some(suggest);
167 self
168 }
169
170 pub fn current(mut self, current: CurrentState) -> Self {
172 self.current = Some(current);
173 self
174 }
175
176 pub fn expanded(mut self, expanded: bool) -> Self {
178 self.expanded = Some(expanded);
179 self
180 }
181
182 pub fn query_fallbacks(mut self, query_fallbacks: bool) -> Self {
184 self.query_fallbacks = Some(query_fallbacks);
185 self
186 }
187
188 pub fn level(mut self, level: u8) -> Self {
190 self.level = Some(level);
191 self
192 }
193
194 pub fn value(mut self, value: ValueOptions) -> Self {
196 self.value = Some(value);
197 self
198 }
199}
200
201impl TestingLibraryOptions for ByRoleOptions {}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_empty_options_serialization() {
209 let options = ByRoleOptions::new();
210 let json = options.to_json_string().unwrap();
211 assert_eq!(json, "{}");
212 }
213
214 #[test]
215 fn test_basic_options_serialization() {
216 let options = ByRoleOptions::new().hidden(true).selected(false);
217
218 let json_value = options.to_json_value().unwrap();
219 assert_eq!(json_value["hidden"], true);
220 assert_eq!(json_value["selected"], false);
221 assert!(json_value["name"].is_null());
222 }
223
224 #[test]
225 fn test_text_match_exact_serialization() {
226 let options = ByRoleOptions::new().name("Submit");
227
228 let json_value = options.to_json_value().unwrap();
229 assert_eq!(json_value["name"], "Submit");
230 }
231
232 #[test]
233 fn test_text_match_regex_serialization() {
234 let options = ByRoleOptions::new().name("/^submit.*/");
235
236 let json_string = options.to_json_string().unwrap();
238 assert!(json_string.contains("/^submit.*/"));
239
240 let json_value = options.to_json_value().unwrap();
243 assert_eq!(json_value["name"], "__RAW_JS__/^submit.*/");
244 }
245
246 #[test]
247 fn test_current_state_serialization() {
248 let options_false = ByRoleOptions::new().current(CurrentState::False);
249 let json_false = options_false.to_json_value().unwrap();
250 assert_eq!(json_false["current"], false);
251
252 let options_page = ByRoleOptions::new().current(CurrentState::Page);
253 let json_page = options_page.to_json_value().unwrap();
254 assert_eq!(json_page["current"], "page");
255
256 let options_custom =
257 ByRoleOptions::new().current(CurrentState::Custom("custom-value".to_string()));
258 let json_custom = options_custom.to_json_value().unwrap();
259 assert_eq!(json_custom["current"], "custom-value");
260 }
261
262 #[test]
263 fn test_value_options_serialization() {
264 let value_opts = ValueOptions {
265 min: Some(0),
266 max: Some(100),
267 now: Some(50),
268 text: Some(TextMatch::String("medium".to_string())),
269 };
270
271 let options = ByRoleOptions::new().value(value_opts);
272
273 let json_value = options.to_json_value().unwrap();
274 assert_eq!(json_value["value"]["min"], 0);
275 assert_eq!(json_value["value"]["max"], 100);
276 assert_eq!(json_value["value"]["now"], 50);
277 assert_eq!(json_value["value"]["text"], "medium");
278 }
279
280 #[test]
281 fn test_query_fallbacks_rename() {
282 let options = ByRoleOptions::new().query_fallbacks(true);
283
284 let json_value = options.to_json_value().unwrap();
285 assert_eq!(json_value["queryFallbacks"], true);
286 assert!(json_value["query_fallbacks"].is_null());
287 }
288
289 #[test]
290 fn test_complex_options_serialization() {
291 let options = ByRoleOptions::new()
292 .name("button")
293 .hidden(false)
294 .pressed(true)
295 .level(2)
296 .current(CurrentState::Page);
297
298 let json_string = options.to_json_string().unwrap();
299
300 let parsed: Value = serde_json::from_str(&json_string).unwrap();
302 assert_eq!(parsed["name"], "button");
303 assert_eq!(parsed["hidden"], false);
304 assert_eq!(parsed["pressed"], true);
305 assert_eq!(parsed["level"], 2);
306 assert_eq!(parsed["current"], "page");
307 }
308
309 #[test]
310 fn test_serialization_example() {
311 let options = ByRoleOptions::new()
313 .name("/submit|send/")
314 .pressed(false)
315 .hidden(false)
316 .suggest(true);
317
318 let json_string = options.to_json_string().unwrap();
319 println!("Serialized options: {json_string}");
320
321 assert!(json_string.contains("/submit|send/"));
327 assert!(json_string.contains("\"pressed\":false"));
328 assert!(json_string.contains("\"hidden\":false"));
329 assert!(json_string.contains("\"suggest\":true"));
330 }
331
332 #[test]
333 fn test_regex_validation() {
334 let valid_regex = TextMatch::Regex("/test.*/".to_string());
336 assert!(valid_regex.validate_regex().is_ok());
337
338 let valid_regex_with_flags = TextMatch::Regex("/test.*/i".to_string());
339 assert!(valid_regex_with_flags.validate_regex().is_ok());
340
341 let invalid_no_slashes = TextMatch::Regex("test.*".to_string());
343 assert!(invalid_no_slashes.validate_regex().is_err());
344
345 let invalid_pattern = TextMatch::Regex("/[/".to_string());
346 assert!(invalid_pattern.validate_regex().is_err());
347
348 let string_match = TextMatch::String("test".to_string());
350 assert!(string_match.validate_regex().is_ok());
351 }
352
353 #[test]
354 fn test_raw_javascript_marker_processing() {
355 let json_with_markers = r#"{"name":"__RAW_JS__/Save.*/","pressed":false}"#;
357 let processed = process_raw_javascript_markers(json_with_markers);
358 assert_eq!(processed, r#"{"name":/Save.*/,"pressed":false}"#);
359
360 let json_with_flags = r#"{"name":"__RAW_JS__/save/i","hidden":true}"#;
362 let processed_flags = process_raw_javascript_markers(json_with_flags);
363 assert_eq!(processed_flags, r#"{"name":/save/i,"hidden":true}"#);
364
365 let json_multiple = r#"{"name":"__RAW_JS__/button/","description":"__RAW_JS__/click.*/i"}"#;
367 let processed_multiple = process_raw_javascript_markers(json_multiple);
368 assert_eq!(
369 processed_multiple,
370 r#"{"name":/button/,"description":/click.*/i}"#
371 );
372
373 let json_no_markers = r#"{"name":"button","pressed":true}"#;
375 let processed_no_markers = process_raw_javascript_markers(json_no_markers);
376 assert_eq!(processed_no_markers, r#"{"name":"button","pressed":true}"#);
377 }
378}