1use serde::{Deserialize, Serialize};
10
11pub mod builder;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct DiagTransportMessageV1 {
19 pub schema_version: u32,
20 pub r#type: String,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub session_id: Option<String>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub request_id: Option<u64>,
25 #[serde(default)]
26 pub payload: serde_json::Value,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct DevtoolsHelloV1 {
32 pub client_kind: String,
33 pub client_version: String,
34 #[serde(default)]
35 pub capabilities: Vec<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct DevtoolsHelloAckV1 {
41 pub server_version: String,
42 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 pub server_capabilities: Vec<String>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct DevtoolsSessionDescriptorV1 {
48 pub session_id: String,
49 pub client_kind: String,
50 pub client_version: String,
51 #[serde(default)]
52 pub capabilities: Vec<String>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct DevtoolsSessionListV1 {
57 pub sessions: Vec<DevtoolsSessionDescriptorV1>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct DevtoolsSessionAddedV1 {
62 pub session: DevtoolsSessionDescriptorV1,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct DevtoolsSessionRemovedV1 {
67 pub session_id: String,
68}
69
70#[derive(Debug, Clone, Default, Serialize, Deserialize)]
71pub struct UiScriptMetaV1 {
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub name: Option<String>,
74 #[serde(default, skip_serializing_if = "Vec::is_empty")]
75 pub tags: Vec<String>,
76 #[serde(default, skip_serializing_if = "Vec::is_empty")]
77 pub required_capabilities: Vec<String>,
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
79 pub target_hints: Vec<String>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(tag = "kind", rename_all = "snake_case")]
84pub enum UiImeEventV1 {
85 Enabled,
86 Disabled,
87 Commit {
88 text: String,
89 },
90 Preedit {
95 text: String,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 cursor_bytes: Option<(u32, u32)>,
98 },
99 DeleteSurrounding {
103 before_bytes: u32,
104 after_bytes: u32,
105 },
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct UiActionScriptV1 {
113 pub schema_version: u32,
114 #[serde(default, skip_serializing_if = "Option::is_none")]
115 pub meta: Option<UiScriptMetaV1>,
116 pub steps: Vec<UiActionStepV1>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(tag = "type", rename_all = "snake_case")]
121pub enum UiActionStepV1 {
122 Click {
123 target: UiSelectorV1,
124 #[serde(default)]
125 button: UiMouseButtonV1,
126 #[serde(
127 default = "default_click_count",
128 skip_serializing_if = "is_default_click_count"
129 )]
130 click_count: u8,
131 },
132 ResetDiagnostics,
133 MovePointer {
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 window: Option<UiWindowTargetV1>,
136 target: UiSelectorV1,
137 },
138 DragPointer {
139 target: UiSelectorV1,
140 #[serde(default)]
141 button: UiMouseButtonV1,
142 delta_x: f32,
143 delta_y: f32,
144 #[serde(default = "default_drag_steps")]
145 steps: u32,
146 },
147 Wheel {
148 target: UiSelectorV1,
149 #[serde(default)]
150 delta_x: f32,
151 #[serde(default)]
152 delta_y: f32,
153 },
154 PressKey {
155 key: String,
156 #[serde(default)]
157 modifiers: UiKeyModifiersV1,
158 #[serde(default)]
159 repeat: bool,
160 },
161 TypeText {
162 text: String,
163 },
164 WaitFrames {
165 n: u32,
166 },
167 WaitUntil {
168 predicate: UiPredicateV1,
169 timeout_frames: u32,
170 },
171 Assert {
172 predicate: UiPredicateV1,
173 },
174 CaptureBundle {
175 label: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 max_snapshots: Option<u32>,
178 },
179 CaptureScreenshot {
180 label: Option<String>,
181 #[serde(default = "default_capture_screenshot_timeout_frames")]
182 timeout_frames: u32,
183 },
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct UiActionScriptV2 {
191 pub schema_version: u32,
192 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub meta: Option<UiScriptMetaV1>,
194 pub steps: Vec<UiActionStepV2>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct FilesystemCapabilitiesHintsV1 {
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub allow_script_schema_v1: Option<bool>,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub write_bundle_schema2: Option<bool>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct FilesystemCapabilitiesV1 {
207 pub schema_version: u32,
208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
209 pub capabilities: Vec<String>,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub runner_kind: Option<String>,
213 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub runner_version: Option<String>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub hints: Option<FilesystemCapabilitiesHintsV1>,
218}
219
220#[derive(Debug, Clone, Default, Serialize, Deserialize)]
221pub struct UiDiagnosticsConfigPathsV1 {
222 #[serde(default, skip_serializing_if = "Option::is_none")]
223 pub trigger_path: Option<String>,
224 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub ready_path: Option<String>,
226 #[serde(default, skip_serializing_if = "Option::is_none")]
227 pub exit_path: Option<String>,
228
229 #[serde(default, skip_serializing_if = "Option::is_none")]
230 pub screenshot_request_path: Option<String>,
231 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub screenshot_trigger_path: Option<String>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub screenshot_result_path: Option<String>,
235 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub screenshot_result_trigger_path: Option<String>,
237
238 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub script_path: Option<String>,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub script_trigger_path: Option<String>,
242 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub script_result_path: Option<String>,
244 #[serde(default, skip_serializing_if = "Option::is_none")]
245 pub script_result_trigger_path: Option<String>,
246
247 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub pick_trigger_path: Option<String>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub pick_result_path: Option<String>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub pick_result_trigger_path: Option<String>,
253
254 #[serde(default, skip_serializing_if = "Option::is_none")]
255 pub inspect_path: Option<String>,
256 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub inspect_trigger_path: Option<String>,
258}
259
260#[derive(Debug, Clone, Default, Serialize, Deserialize)]
261pub struct UiDiagnosticsConfigFileV1 {
262 pub schema_version: u32,
263
264 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub enabled: Option<bool>,
266 #[serde(default, skip_serializing_if = "Option::is_none")]
267 pub out_dir: Option<String>,
268 #[serde(default, skip_serializing_if = "Option::is_none")]
269 pub paths: Option<UiDiagnosticsConfigPathsV1>,
270
271 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub allow_script_schema_v1: Option<bool>,
277
278 #[serde(default, skip_serializing_if = "Option::is_none")]
279 pub script_keepalive: Option<bool>,
280 #[serde(default, skip_serializing_if = "Option::is_none")]
281 pub script_auto_dump: Option<bool>,
282 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub pick_auto_dump: Option<bool>,
284
285 #[serde(default, skip_serializing_if = "Option::is_none")]
286 pub max_events: Option<u32>,
287 #[serde(default, skip_serializing_if = "Option::is_none")]
288 pub max_snapshots: Option<u32>,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub script_dump_max_snapshots: Option<u32>,
291
292 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub capture_semantics: Option<bool>,
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub max_semantics_nodes: Option<u32>,
296 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub semantics_test_ids_only: Option<bool>,
298
299 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub screenshots_enabled: Option<bool>,
301 #[serde(default, skip_serializing_if = "Option::is_none")]
302 pub screenshot_on_dump: Option<bool>,
303
304 #[serde(default, skip_serializing_if = "Option::is_none")]
310 pub write_bundle_json: Option<bool>,
311 #[serde(default, skip_serializing_if = "Option::is_none")]
317 pub write_bundle_schema2: Option<bool>,
318
319 #[serde(default, skip_serializing_if = "Option::is_none")]
320 pub redact_text: Option<bool>,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub max_debug_string_bytes: Option<u32>,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub max_gating_trace_entries: Option<u32>,
325
326 #[serde(default, skip_serializing_if = "Option::is_none")]
332 pub isolate_external_pointer_input_while_script_running: Option<bool>,
333
334 #[serde(default, skip_serializing_if = "Option::is_none")]
341 pub isolate_external_keyboard_input_while_script_running: Option<bool>,
342
343 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub frame_clock_fixed_delta_ms: Option<u64>,
345
346 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub devtools_embed_bundle: Option<bool>,
348}
349
350#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
351pub struct UiPaddingInsetsV1 {
352 pub left_px: f32,
353 pub top_px: f32,
354 pub right_px: f32,
355 pub bottom_px: f32,
356}
357
358impl UiPaddingInsetsV1 {
359 pub fn uniform(padding_px: f32) -> Self {
360 let p = padding_px.max(0.0);
361 Self {
362 left_px: p,
363 top_px: p,
364 right_px: p,
365 bottom_px: p,
366 }
367 }
368}
369
370#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
371#[serde(tag = "kind", rename_all = "snake_case")]
372pub enum UiWindowTargetV1 {
373 Current,
375 FirstSeen,
377 FirstSeenOther,
379 LastSeen,
381 LastSeenOther,
383 WindowFfi { window: u64 },
385}
386
387#[derive(Default, Debug, Clone, Serialize, Deserialize)]
388#[serde(tag = "kind", rename_all = "snake_case")]
389pub enum UiInsetsOverrideV1 {
390 #[default]
391 NoChange,
392 Clear,
393 Set {
394 insets_px: UiPaddingInsetsV1,
395 },
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
399#[serde(tag = "kind", rename_all = "snake_case")]
400pub enum UiIncomingOpenInjectItemV1 {
401 FileUtf8 {
405 name: String,
406 text: String,
407 #[serde(default, skip_serializing_if = "Option::is_none")]
408 media_type: Option<String>,
409 },
410 Text {
411 text: String,
412 #[serde(default, skip_serializing_if = "Option::is_none")]
413 media_type: Option<String>,
414 },
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
418#[serde(rename_all = "snake_case")]
419pub enum UiClipboardWriteResultV1 {
420 Success,
421 Failure,
422}
423
424#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
425#[serde(rename_all = "snake_case")]
426pub enum UiClipboardAccessErrorKindV1 {
427 Unavailable,
428 PermissionDenied,
429 UserActivationRequired,
430 Unsupported,
431 BackendError,
432 Unknown,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
436#[serde(tag = "type", rename_all = "snake_case")]
437pub enum UiActionStepV2 {
438 Click {
440 #[serde(default, skip_serializing_if = "Option::is_none")]
441 window: Option<UiWindowTargetV1>,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pointer_kind: Option<UiPointerKindV1>,
444 target: UiSelectorV1,
445 #[serde(default)]
446 button: UiMouseButtonV1,
447 #[serde(
448 default = "default_click_count",
449 skip_serializing_if = "is_default_click_count"
450 )]
451 click_count: u8,
452 #[serde(default, skip_serializing_if = "Option::is_none")]
453 modifiers: Option<UiKeyModifiersV1>,
454 },
455 Tap {
460 #[serde(default, skip_serializing_if = "Option::is_none")]
461 window: Option<UiWindowTargetV1>,
462 #[serde(default, skip_serializing_if = "Option::is_none")]
464 pointer_kind: Option<UiPointerKindV1>,
465 target: UiSelectorV1,
466 #[serde(default, skip_serializing_if = "Option::is_none")]
467 modifiers: Option<UiKeyModifiersV1>,
468 },
469 LongPress {
474 #[serde(default, skip_serializing_if = "Option::is_none")]
475 window: Option<UiWindowTargetV1>,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
478 pointer_kind: Option<UiPointerKindV1>,
479 target: UiSelectorV1,
480 #[serde(
481 default = "default_long_press_duration_ms",
482 skip_serializing_if = "is_default_long_press_duration_ms"
483 )]
484 duration_ms: u64,
485 #[serde(default, skip_serializing_if = "Option::is_none")]
486 modifiers: Option<UiKeyModifiersV1>,
487 },
488 Swipe {
493 #[serde(default, skip_serializing_if = "Option::is_none")]
494 window: Option<UiWindowTargetV1>,
495 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pointer_kind: Option<UiPointerKindV1>,
498 target: UiSelectorV1,
499 delta_x: f32,
500 delta_y: f32,
501 #[serde(
502 default = "default_drag_steps",
503 skip_serializing_if = "is_default_drag_steps"
504 )]
505 steps: u32,
506 #[serde(default, skip_serializing_if = "Option::is_none")]
507 modifiers: Option<UiKeyModifiersV1>,
508 },
509 Pinch {
513 #[serde(default, skip_serializing_if = "Option::is_none")]
514 window: Option<UiWindowTargetV1>,
515 #[serde(default, skip_serializing_if = "Option::is_none")]
517 pointer_kind: Option<UiPointerKindV1>,
518 target: UiSelectorV1,
519 delta: f32,
521 #[serde(
522 default = "default_drag_steps",
523 skip_serializing_if = "is_default_drag_steps"
524 )]
525 steps: u32,
526 #[serde(default, skip_serializing_if = "Option::is_none")]
527 modifiers: Option<UiKeyModifiersV1>,
528 },
529 ResetDiagnostics,
530 SetBaseRef {
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 window: Option<UiWindowTargetV1>,
540 target: UiSelectorV1,
541 },
542 ClearBaseRef,
544 Activate {
549 #[serde(default, skip_serializing_if = "Option::is_none")]
550 window: Option<UiWindowTargetV1>,
551 target: UiSelectorV1,
552 },
553 Focus {
558 #[serde(default, skip_serializing_if = "Option::is_none")]
559 window: Option<UiWindowTargetV1>,
560 target: UiSelectorV1,
561 },
562 MovePointer {
563 #[serde(default, skip_serializing_if = "Option::is_none")]
564 window: Option<UiWindowTargetV1>,
565 #[serde(default, skip_serializing_if = "Option::is_none")]
566 pointer_kind: Option<UiPointerKindV1>,
567 target: UiSelectorV1,
568 },
569 PointerDown {
574 #[serde(default, skip_serializing_if = "Option::is_none")]
575 window: Option<UiWindowTargetV1>,
576 #[serde(default, skip_serializing_if = "Option::is_none")]
577 pointer_kind: Option<UiPointerKindV1>,
578 target: UiSelectorV1,
579 #[serde(default)]
580 button: UiMouseButtonV1,
581 #[serde(default, skip_serializing_if = "Option::is_none")]
582 modifiers: Option<UiKeyModifiersV1>,
583 },
584 DragPointer {
585 #[serde(default, skip_serializing_if = "Option::is_none")]
586 window: Option<UiWindowTargetV1>,
587 #[serde(default, skip_serializing_if = "Option::is_none")]
588 pointer_kind: Option<UiPointerKindV1>,
589 target: UiSelectorV1,
590 #[serde(default)]
591 button: UiMouseButtonV1,
592 #[serde(default = "default_true")]
593 clamp_to_window_bounds: bool,
594 delta_x: f32,
595 delta_y: f32,
596 #[serde(default = "default_drag_steps")]
597 steps: u32,
598 },
599 PointerMove {
605 #[serde(default, skip_serializing_if = "Option::is_none")]
606 window: Option<UiWindowTargetV1>,
607 #[serde(default, skip_serializing_if = "Option::is_none")]
608 pointer_kind: Option<UiPointerKindV1>,
609 delta_x: f32,
610 delta_y: f32,
611 #[serde(default = "default_drag_steps")]
612 steps: u32,
613 },
614 PointerUp {
619 #[serde(default, skip_serializing_if = "Option::is_none")]
620 window: Option<UiWindowTargetV1>,
621 #[serde(default, skip_serializing_if = "Option::is_none")]
622 pointer_kind: Option<UiPointerKindV1>,
623 #[serde(default, skip_serializing_if = "Option::is_none")]
624 button: Option<UiMouseButtonV1>,
625 },
626 PointerCancel {
631 #[serde(default, skip_serializing_if = "Option::is_none")]
632 window: Option<UiWindowTargetV1>,
633 #[serde(default, skip_serializing_if = "Option::is_none")]
634 pointer_kind: Option<UiPointerKindV1>,
635 },
636 MovePointerSweep {
637 #[serde(default, skip_serializing_if = "Option::is_none")]
638 window: Option<UiWindowTargetV1>,
639 #[serde(default, skip_serializing_if = "Option::is_none")]
640 pointer_kind: Option<UiPointerKindV1>,
641 target: UiSelectorV1,
642 delta_x: f32,
643 delta_y: f32,
644 #[serde(default = "default_drag_steps")]
645 steps: u32,
646 #[serde(default = "default_move_frames_per_step")]
647 frames_per_step: u32,
648 },
649 Wheel {
650 #[serde(default, skip_serializing_if = "Option::is_none")]
651 window: Option<UiWindowTargetV1>,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pointer_kind: Option<UiPointerKindV1>,
654 target: UiSelectorV1,
655 #[serde(default)]
656 delta_x: f32,
657 #[serde(default)]
658 delta_y: f32,
659 },
660 WheelBurst {
668 #[serde(default, skip_serializing_if = "Option::is_none")]
669 window: Option<UiWindowTargetV1>,
670 #[serde(default, skip_serializing_if = "Option::is_none")]
671 pointer_kind: Option<UiPointerKindV1>,
672 target: UiSelectorV1,
673 #[serde(default)]
674 delta_x: f32,
675 #[serde(default)]
676 delta_y: f32,
677 #[serde(
678 default = "default_wheel_burst_count",
679 skip_serializing_if = "is_default_wheel_burst_count"
680 )]
681 count: u32,
682 },
683 PressKey {
684 key: String,
685 #[serde(default)]
686 modifiers: UiKeyModifiersV1,
687 #[serde(default)]
688 repeat: bool,
689 },
690 PressShortcut {
691 shortcut: String,
692 #[serde(default)]
693 repeat: bool,
694 },
695 TypeText {
696 text: String,
697 },
698 Ime {
703 event: UiImeEventV1,
704 },
705 WaitFrames {
706 #[serde(default, skip_serializing_if = "Option::is_none")]
707 window: Option<UiWindowTargetV1>,
708 n: u32,
709 },
710 WaitMs {
715 #[serde(default, skip_serializing_if = "Option::is_none")]
716 window: Option<UiWindowTargetV1>,
717 n_ms: u32,
718 },
719 WaitUntil {
720 #[serde(default, skip_serializing_if = "Option::is_none")]
721 window: Option<UiWindowTargetV1>,
722 predicate: UiPredicateV1,
723 #[serde(default = "default_action_timeout_frames")]
724 timeout_frames: u32,
725 #[serde(default, skip_serializing_if = "Option::is_none")]
726 timeout_ms: Option<u32>,
727 },
728 WaitShortcutRoutingTrace {
733 query: UiShortcutRoutingTraceQueryV1,
734 #[serde(default = "default_action_timeout_frames")]
735 timeout_frames: u32,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
737 timeout_ms: Option<u32>,
738 },
739 WaitCommandDispatchTrace {
744 query: UiCommandDispatchTraceQueryV1,
745 #[serde(default = "default_action_timeout_frames")]
746 timeout_frames: u32,
747 #[serde(default, skip_serializing_if = "Option::is_none")]
748 timeout_ms: Option<u32>,
749 },
750 WaitOverlayPlacementTrace {
756 query: UiOverlayPlacementTraceQueryV1,
757 #[serde(default = "default_action_timeout_frames")]
758 timeout_frames: u32,
759 #[serde(default, skip_serializing_if = "Option::is_none")]
760 timeout_ms: Option<u32>,
761 },
762 Assert {
763 #[serde(default, skip_serializing_if = "Option::is_none")]
764 window: Option<UiWindowTargetV1>,
765 predicate: UiPredicateV1,
766 },
767 CaptureBundle {
768 label: Option<String>,
769 #[serde(default, skip_serializing_if = "Option::is_none")]
770 max_snapshots: Option<u32>,
771 },
772 CaptureScreenshot {
773 label: Option<String>,
774 #[serde(default = "default_capture_screenshot_timeout_frames")]
775 timeout_frames: u32,
776 #[serde(default, skip_serializing_if = "Option::is_none")]
777 timeout_ms: Option<u32>,
778 },
779 CaptureLayoutSidecar {
786 #[serde(default, skip_serializing_if = "Option::is_none")]
788 label: Option<String>,
789 #[serde(default, skip_serializing_if = "Option::is_none")]
791 root_label_filter: Option<String>,
792 },
793
794 ClickStable {
801 #[serde(default, skip_serializing_if = "Option::is_none")]
802 window: Option<UiWindowTargetV1>,
803 #[serde(default, skip_serializing_if = "Option::is_none")]
804 pointer_kind: Option<UiPointerKindV1>,
805 target: UiSelectorV1,
806 #[serde(default)]
807 button: UiMouseButtonV1,
808 #[serde(
809 default = "default_click_count",
810 skip_serializing_if = "is_default_click_count"
811 )]
812 click_count: u8,
813 #[serde(default, skip_serializing_if = "Option::is_none")]
814 modifiers: Option<UiKeyModifiersV1>,
815 #[serde(default = "default_click_stable_frames")]
816 stable_frames: u32,
817 #[serde(default = "default_click_stable_max_move_px")]
818 max_move_px: f32,
819 #[serde(default = "default_action_timeout_frames")]
820 timeout_frames: u32,
821 },
822 ClickSelectableTextSpanStable {
829 #[serde(default, skip_serializing_if = "Option::is_none")]
830 window: Option<UiWindowTargetV1>,
831 #[serde(default, skip_serializing_if = "Option::is_none")]
832 pointer_kind: Option<UiPointerKindV1>,
833 target: UiSelectorV1,
834 tag: String,
835 #[serde(default)]
836 button: UiMouseButtonV1,
837 #[serde(
838 default = "default_click_count",
839 skip_serializing_if = "is_default_click_count"
840 )]
841 click_count: u8,
842 #[serde(default, skip_serializing_if = "Option::is_none")]
843 modifiers: Option<UiKeyModifiersV1>,
844 #[serde(default = "default_click_stable_frames")]
845 stable_frames: u32,
846 #[serde(default = "default_click_stable_max_move_px")]
847 max_move_px: f32,
848 #[serde(default = "default_action_timeout_frames")]
849 timeout_frames: u32,
850 },
851 WaitBoundsStable {
857 #[serde(default, skip_serializing_if = "Option::is_none")]
858 window: Option<UiWindowTargetV1>,
859 target: UiSelectorV1,
860 #[serde(default = "default_bounds_stable_frames")]
861 stable_frames: u32,
862 #[serde(default = "default_bounds_stable_max_move_px")]
863 max_move_px: f32,
864 #[serde(default = "default_action_timeout_frames")]
865 timeout_frames: u32,
866 },
867 WaitSemanticsScrollStable {
873 #[serde(default, skip_serializing_if = "Option::is_none")]
874 window: Option<UiWindowTargetV1>,
875 target: UiSelectorV1,
876 field: UiSemanticsScrollFieldV1,
877 #[serde(default = "default_semantics_scroll_stable_frames")]
878 stable_frames: u32,
879 #[serde(default = "default_semantics_scroll_stable_max_delta")]
880 max_delta: f64,
881 #[serde(default = "default_action_timeout_frames")]
882 timeout_frames: u32,
883 },
884 EnsureVisible {
885 #[serde(default, skip_serializing_if = "Option::is_none")]
886 window: Option<UiWindowTargetV1>,
887 target: UiSelectorV1,
888 #[serde(default)]
889 within_window: bool,
890 #[serde(default)]
891 padding_px: f32,
892 #[serde(default = "default_action_timeout_frames")]
893 timeout_frames: u32,
894 },
895 ScrollIntoView {
896 #[serde(default, skip_serializing_if = "Option::is_none")]
897 window: Option<UiWindowTargetV1>,
898 #[serde(default, skip_serializing_if = "Option::is_none")]
899 pointer_kind: Option<UiPointerKindV1>,
900 container: UiSelectorV1,
901 target: UiSelectorV1,
902 #[serde(default)]
903 delta_x: f32,
904 #[serde(default = "default_scroll_delta_y")]
905 delta_y: f32,
906 #[serde(default)]
907 require_fully_within_container: bool,
908 #[serde(default)]
909 require_fully_within_window: bool,
910 #[serde(default)]
911 padding_px: f32,
912 #[serde(default)]
913 padding_insets_px: Option<UiPaddingInsetsV1>,
914 #[serde(default = "default_action_timeout_frames")]
915 timeout_frames: u32,
916 },
917 TypeTextInto {
918 #[serde(default, skip_serializing_if = "Option::is_none")]
919 window: Option<UiWindowTargetV1>,
920 #[serde(default, skip_serializing_if = "Option::is_none")]
921 pointer_kind: Option<UiPointerKindV1>,
922 target: UiSelectorV1,
923 text: String,
924 #[serde(default)]
925 clear_before_type: bool,
926 #[serde(default = "default_action_timeout_frames")]
927 timeout_frames: u32,
928 },
929 SetTextValue {
940 #[serde(default, skip_serializing_if = "Option::is_none")]
941 window: Option<UiWindowTargetV1>,
942 target: UiSelectorV1,
943 text: String,
944 #[serde(default = "default_action_timeout_frames")]
945 timeout_frames: u32,
946 },
947 PasteTextInto {
961 #[serde(default, skip_serializing_if = "Option::is_none")]
962 window: Option<UiWindowTargetV1>,
963 #[serde(default, skip_serializing_if = "Option::is_none")]
964 pointer_kind: Option<UiPointerKindV1>,
965 target: UiSelectorV1,
966 text: String,
967 #[serde(default)]
968 clear_before_paste: bool,
969 #[serde(default = "default_action_timeout_frames")]
970 timeout_frames: u32,
971 },
972 MenuSelect {
973 #[serde(default, skip_serializing_if = "Option::is_none")]
974 window: Option<UiWindowTargetV1>,
975 #[serde(default, skip_serializing_if = "Option::is_none")]
976 pointer_kind: Option<UiPointerKindV1>,
977 menu: UiSelectorV1,
978 item: UiSelectorV1,
979 #[serde(default = "default_action_timeout_frames")]
980 timeout_frames: u32,
981 },
982 MenuSelectPath {
983 #[serde(default, skip_serializing_if = "Option::is_none")]
984 window: Option<UiWindowTargetV1>,
985 #[serde(default, skip_serializing_if = "Option::is_none")]
986 pointer_kind: Option<UiPointerKindV1>,
987 path: Vec<UiSelectorV1>,
988 #[serde(default = "default_action_timeout_frames")]
989 timeout_frames: u32,
990 },
991 DragTo {
992 #[serde(default, skip_serializing_if = "Option::is_none")]
993 window: Option<UiWindowTargetV1>,
994 #[serde(default, skip_serializing_if = "Option::is_none")]
995 pointer_kind: Option<UiPointerKindV1>,
996 from: UiSelectorV1,
997 to: UiSelectorV1,
998 #[serde(default)]
999 button: UiMouseButtonV1,
1000 #[serde(default = "default_drag_steps")]
1001 steps: u32,
1002 #[serde(default = "default_action_timeout_frames")]
1003 timeout_frames: u32,
1004 },
1005 SetSliderValue {
1006 #[serde(default, skip_serializing_if = "Option::is_none")]
1007 window: Option<UiWindowTargetV1>,
1008 #[serde(default, skip_serializing_if = "Option::is_none")]
1009 pointer_kind: Option<UiPointerKindV1>,
1010 target: UiSelectorV1,
1011 value: f32,
1012 #[serde(default = "default_slider_min")]
1013 min: f32,
1014 #[serde(default = "default_slider_max")]
1015 max: f32,
1016 #[serde(default = "default_slider_epsilon")]
1017 epsilon: f32,
1018 #[serde(default = "default_action_timeout_frames")]
1019 timeout_frames: u32,
1020 #[serde(default = "default_drag_steps")]
1021 drag_steps: u32,
1022 },
1023 SetWindowInnerSize {
1024 #[serde(default, skip_serializing_if = "Option::is_none")]
1025 window: Option<UiWindowTargetV1>,
1026 width_px: f32,
1027 height_px: f32,
1028 },
1029 SetWindowStyle {
1043 #[serde(default, skip_serializing_if = "Option::is_none")]
1044 window: Option<UiWindowTargetV1>,
1045 style: UiWindowStylePatchV1,
1046 },
1047 SetWindowInsets {
1048 #[serde(default)]
1049 safe_area_insets: UiInsetsOverrideV1,
1050 #[serde(default)]
1051 occlusion_insets: UiInsetsOverrideV1,
1052 },
1053 SetClipboardForceUnavailable {
1058 enabled: bool,
1059 },
1060 SetClipboardText {
1067 text: String,
1068 },
1069 WaitClipboardWriteResult {
1077 outcome: UiClipboardWriteResultV1,
1078 #[serde(default, skip_serializing_if = "Option::is_none")]
1079 error_kind: Option<UiClipboardAccessErrorKindV1>,
1080 #[serde(default, skip_serializing_if = "Option::is_none")]
1081 message_contains: Option<String>,
1082 #[serde(default = "default_action_timeout_frames")]
1083 timeout_frames: u32,
1084 },
1085 AssertClipboardWriteResult {
1095 outcome: UiClipboardWriteResultV1,
1096 #[serde(default, skip_serializing_if = "Option::is_none")]
1097 error_kind: Option<UiClipboardAccessErrorKindV1>,
1098 #[serde(default, skip_serializing_if = "Option::is_none")]
1099 message_contains: Option<String>,
1100 #[serde(default = "default_action_timeout_frames")]
1101 timeout_frames: u32,
1102 },
1103 AssertClipboardText {
1110 text: String,
1111 #[serde(default = "default_action_timeout_frames")]
1112 timeout_frames: u32,
1113 },
1114 InspectHelpLockBestMatchAndCopySelector {
1126 #[serde(default, skip_serializing_if = "Option::is_none")]
1127 window: Option<UiWindowTargetV1>,
1128 query: String,
1129 #[serde(default = "default_action_timeout_frames")]
1130 timeout_frames: u32,
1131 },
1132 InspectHelpTreeLockBestMatchAndCopySelector {
1140 #[serde(default, skip_serializing_if = "Option::is_none")]
1141 window: Option<UiWindowTargetV1>,
1142 query: String,
1143 #[serde(default = "default_action_timeout_frames")]
1144 timeout_frames: u32,
1145 },
1146 InjectIncomingOpen {
1150 items: Vec<UiIncomingOpenInjectItemV1>,
1151 },
1152 SetWindowOuterPosition {
1157 #[serde(default, skip_serializing_if = "Option::is_none")]
1158 window: Option<UiWindowTargetV1>,
1159 x_px: f32,
1160 y_px: f32,
1161 },
1162 SetCursorScreenPos {
1169 x_px: f32,
1170 y_px: f32,
1171 },
1172 SetCursorInWindow {
1181 #[serde(default, skip_serializing_if = "Option::is_none")]
1182 window: Option<UiWindowTargetV1>,
1183 x_px: f32,
1184 y_px: f32,
1185 },
1186 SetCursorInWindowLogical {
1196 #[serde(default, skip_serializing_if = "Option::is_none")]
1197 window: Option<UiWindowTargetV1>,
1198 x_px: f32,
1199 y_px: f32,
1200 },
1201 SetMouseButtons {
1210 #[serde(default, skip_serializing_if = "Option::is_none")]
1211 window: Option<UiWindowTargetV1>,
1212 #[serde(default, skip_serializing_if = "Option::is_none")]
1213 left: Option<bool>,
1214 #[serde(default, skip_serializing_if = "Option::is_none")]
1215 right: Option<bool>,
1216 #[serde(default, skip_serializing_if = "Option::is_none")]
1217 middle: Option<bool>,
1218 },
1219 RaiseWindow {
1220 #[serde(default, skip_serializing_if = "Option::is_none")]
1221 window: Option<UiWindowTargetV1>,
1222 },
1223 DragPointerUntil {
1228 #[serde(default, skip_serializing_if = "Option::is_none")]
1229 window: Option<UiWindowTargetV1>,
1230 #[serde(default, skip_serializing_if = "Option::is_none")]
1231 pointer_kind: Option<UiPointerKindV1>,
1232 target: UiSelectorV1,
1233 #[serde(default)]
1234 button: UiMouseButtonV1,
1235 #[serde(default = "default_true")]
1236 release_on_success: bool,
1237 delta_x: f32,
1238 delta_y: f32,
1239 #[serde(default = "default_drag_steps")]
1240 steps: u32,
1241 predicate: UiPredicateV1,
1242 #[serde(default = "default_action_timeout_frames")]
1243 timeout_frames: u32,
1244 },
1245}
1246
1247impl From<UiActionStepV1> for UiActionStepV2 {
1248 fn from(value: UiActionStepV1) -> Self {
1249 match value {
1250 UiActionStepV1::Click {
1251 target,
1252 button,
1253 click_count,
1254 } => Self::Click {
1255 window: None,
1256 pointer_kind: None,
1257 target,
1258 button,
1259 click_count,
1260 modifiers: None,
1261 },
1262 UiActionStepV1::ResetDiagnostics => Self::ResetDiagnostics,
1263 UiActionStepV1::MovePointer { window, target } => Self::MovePointer {
1264 window,
1265 pointer_kind: None,
1266 target,
1267 },
1268 UiActionStepV1::DragPointer {
1269 target,
1270 button,
1271 delta_x,
1272 delta_y,
1273 steps,
1274 } => Self::DragPointer {
1275 window: None,
1276 pointer_kind: None,
1277 target,
1278 button,
1279 clamp_to_window_bounds: true,
1280 delta_x,
1281 delta_y,
1282 steps,
1283 },
1284 UiActionStepV1::Wheel {
1285 target,
1286 delta_x,
1287 delta_y,
1288 } => Self::Wheel {
1289 window: None,
1290 pointer_kind: None,
1291 target,
1292 delta_x,
1293 delta_y,
1294 },
1295 UiActionStepV1::PressKey {
1296 key,
1297 modifiers,
1298 repeat,
1299 } => Self::PressKey {
1300 key,
1301 modifiers,
1302 repeat,
1303 },
1304 UiActionStepV1::TypeText { text } => Self::TypeText { text },
1305 UiActionStepV1::WaitFrames { n } => Self::WaitFrames { window: None, n },
1306 UiActionStepV1::WaitUntil {
1307 predicate,
1308 timeout_frames,
1309 } => Self::WaitUntil {
1310 window: None,
1311 predicate,
1312 timeout_frames,
1313 timeout_ms: None,
1314 },
1315 UiActionStepV1::Assert { predicate } => Self::Assert {
1316 window: None,
1317 predicate,
1318 },
1319 UiActionStepV1::CaptureBundle {
1320 label,
1321 max_snapshots,
1322 } => Self::CaptureBundle {
1323 label,
1324 max_snapshots,
1325 },
1326 UiActionStepV1::CaptureScreenshot {
1327 label,
1328 timeout_frames,
1329 } => Self::CaptureScreenshot {
1330 label,
1331 timeout_frames,
1332 timeout_ms: None,
1333 },
1334 }
1335 }
1336}
1337
1338fn default_drag_steps() -> u32 {
1339 8
1340}
1341
1342fn is_default_drag_steps(v: &u32) -> bool {
1343 *v == default_drag_steps()
1344}
1345
1346fn default_wheel_burst_count() -> u32 {
1347 8
1348}
1349
1350fn is_default_wheel_burst_count(v: &u32) -> bool {
1351 *v == default_wheel_burst_count()
1352}
1353
1354fn default_move_frames_per_step() -> u32 {
1355 1
1356}
1357
1358fn default_click_count() -> u8 {
1359 1
1360}
1361
1362fn is_default_click_count(v: &u8) -> bool {
1363 *v == 1
1364}
1365
1366fn default_long_press_duration_ms() -> u64 {
1367 500
1368}
1369
1370fn is_default_long_press_duration_ms(v: &u64) -> bool {
1371 *v == 500
1372}
1373
1374fn default_click_stable_frames() -> u32 {
1375 2
1376}
1377
1378fn default_click_stable_max_move_px() -> f32 {
1379 1.0
1380}
1381
1382fn default_bounds_stable_frames() -> u32 {
1383 2
1384}
1385
1386fn default_bounds_stable_max_move_px() -> f32 {
1387 1.0
1388}
1389
1390fn default_semantics_scroll_stable_frames() -> u32 {
1391 2
1392}
1393
1394fn default_semantics_scroll_stable_max_delta() -> f64 {
1395 1.0
1396}
1397
1398fn default_capture_screenshot_timeout_frames() -> u32 {
1399 300
1400}
1401
1402fn default_action_timeout_frames() -> u32 {
1403 180
1404}
1405
1406fn default_true() -> bool {
1407 true
1408}
1409
1410fn default_scroll_delta_y() -> f32 {
1411 -120.0
1412}
1413
1414fn default_slider_min() -> f32 {
1415 0.0
1416}
1417
1418fn default_slider_max() -> f32 {
1419 100.0
1420}
1421
1422fn default_slider_epsilon() -> f32 {
1423 0.5
1424}
1425
1426#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1427#[serde(rename_all = "snake_case")]
1428pub enum UiMouseButtonV1 {
1429 #[default]
1430 Left,
1431 Right,
1432 Middle,
1433}
1434
1435impl UiMouseButtonV1 {
1436 pub fn from_button(button: fret_core::MouseButton) -> Self {
1437 match button {
1438 fret_core::MouseButton::Left => Self::Left,
1439 fret_core::MouseButton::Right => Self::Right,
1440 fret_core::MouseButton::Middle => Self::Middle,
1441 fret_core::MouseButton::Back
1442 | fret_core::MouseButton::Forward
1443 | fret_core::MouseButton::Other(_) => Self::Left,
1444 }
1445 }
1446}
1447
1448#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1449#[serde(rename_all = "snake_case")]
1450pub enum UiPointerKindV1 {
1451 #[default]
1452 Mouse,
1453 Touch,
1454 Pen,
1455}
1456
1457#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
1458pub struct UiKeyModifiersV1 {
1459 #[serde(default)]
1460 pub shift: bool,
1461 #[serde(default)]
1462 pub ctrl: bool,
1463 #[serde(default)]
1464 pub alt: bool,
1465 #[serde(default)]
1466 pub meta: bool,
1467}
1468
1469impl UiKeyModifiersV1 {
1470 pub fn from_modifiers(modifiers: fret_core::Modifiers) -> Self {
1471 Self {
1472 shift: modifiers.shift,
1473 ctrl: modifiers.ctrl,
1474 alt: modifiers.alt,
1475 meta: modifiers.meta,
1476 }
1477 }
1478}
1479
1480#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1481#[serde(rename_all = "snake_case")]
1482pub enum UiWindowDecorationsRequestV1 {
1483 System,
1484 None,
1485 Server,
1486 Client,
1487}
1488
1489#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1490#[serde(rename_all = "snake_case")]
1491pub enum UiTaskbarVisibilityV1 {
1492 Show,
1493 Hide,
1494}
1495
1496#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1497#[serde(rename_all = "snake_case")]
1498pub enum UiActivationPolicyV1 {
1499 Activates,
1500 NonActivating,
1501}
1502
1503#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1504#[serde(rename_all = "snake_case")]
1505pub enum UiWindowZLevelV1 {
1506 Normal,
1507 AlwaysOnTop,
1508}
1509
1510#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1511#[serde(rename_all = "snake_case")]
1512pub enum UiWindowHitTestRequestV1 {
1513 Normal,
1514 PassthroughAll,
1515 PassthroughRegions,
1516}
1517
1518#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1519#[serde(rename_all = "snake_case")]
1520pub enum UiWindowAppearanceV1 {
1521 Opaque,
1522 CompositedNoBackdrop,
1523 CompositedBackdrop,
1524}
1525
1526#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1527#[serde(rename_all = "snake_case")]
1528pub enum UiWindowBackgroundMaterialRequestV1 {
1529 None,
1530 SystemDefault,
1531 Mica,
1532 Acrylic,
1533 Vibrancy,
1534}
1535
1536#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1537pub struct UiWindowStyleMatchV1 {
1538 #[serde(default, skip_serializing_if = "Option::is_none")]
1539 pub decorations: Option<UiWindowDecorationsRequestV1>,
1540 #[serde(default, skip_serializing_if = "Option::is_none")]
1541 pub resizable: Option<bool>,
1542 #[serde(default, skip_serializing_if = "Option::is_none")]
1543 pub transparent: Option<bool>,
1544 #[serde(default, skip_serializing_if = "Option::is_none")]
1545 pub visual_transparent: Option<bool>,
1546 #[serde(default, skip_serializing_if = "Option::is_none")]
1547 pub appearance: Option<UiWindowAppearanceV1>,
1548 #[serde(default, skip_serializing_if = "Option::is_none")]
1549 pub taskbar: Option<UiTaskbarVisibilityV1>,
1550 #[serde(default, skip_serializing_if = "Option::is_none")]
1551 pub activation: Option<UiActivationPolicyV1>,
1552 #[serde(default, skip_serializing_if = "Option::is_none")]
1553 pub z_level: Option<UiWindowZLevelV1>,
1554 #[serde(default, skip_serializing_if = "Option::is_none")]
1555 pub hit_test: Option<UiWindowHitTestRequestV1>,
1556 #[serde(default, skip_serializing_if = "Option::is_none")]
1557 pub hit_test_regions_fingerprint64: Option<u64>,
1558}
1559
1560#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1561pub struct UiWindowStylePatchV1 {
1562 #[serde(default, skip_serializing_if = "Option::is_none")]
1563 pub taskbar: Option<UiTaskbarVisibilityV1>,
1564 #[serde(default, skip_serializing_if = "Option::is_none")]
1565 pub activation: Option<UiActivationPolicyV1>,
1566 #[serde(default, skip_serializing_if = "Option::is_none")]
1567 pub z_level: Option<UiWindowZLevelV1>,
1568 #[serde(default, skip_serializing_if = "Option::is_none")]
1569 pub decorations: Option<UiWindowDecorationsRequestV1>,
1570 #[serde(default, skip_serializing_if = "Option::is_none")]
1571 pub resizable: Option<bool>,
1572 #[serde(default, skip_serializing_if = "Option::is_none")]
1573 pub transparent: Option<bool>,
1574 #[serde(default, skip_serializing_if = "Option::is_none")]
1575 pub background_material: Option<UiWindowBackgroundMaterialRequestV1>,
1576 #[serde(default, skip_serializing_if = "Option::is_none")]
1577 pub hit_test: Option<UiWindowHitTestPatchV1>,
1578 #[serde(default, skip_serializing_if = "Option::is_none")]
1580 pub opacity_alpha_u8: Option<u8>,
1581}
1582
1583#[derive(Debug, Clone, Serialize, Deserialize)]
1584#[serde(tag = "kind", rename_all = "snake_case")]
1585pub enum UiWindowHitTestPatchV1 {
1586 Normal,
1587 PassthroughAll,
1588 PassthroughRegions {
1589 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1590 regions: Vec<UiWindowHitTestRegionV1>,
1591 },
1592}
1593
1594#[derive(Debug, Clone, Serialize, Deserialize)]
1595#[serde(tag = "kind", rename_all = "snake_case")]
1596pub enum UiWindowHitTestRegionV1 {
1597 Rect {
1598 x: f32,
1599 y: f32,
1600 width: f32,
1601 height: f32,
1602 },
1603 RRect {
1604 x: f32,
1605 y: f32,
1606 width: f32,
1607 height: f32,
1608 radius: f32,
1609 },
1610}
1611
1612#[derive(Debug, Clone, Serialize, Deserialize)]
1613#[serde(tag = "kind", rename_all = "snake_case")]
1614pub enum UiPredicateV1 {
1615 Exists {
1616 target: UiSelectorV1,
1617 },
1618 NotExists {
1619 target: UiSelectorV1,
1620 },
1621 ExistsUnder {
1626 scope: UiSelectorV1,
1627 target: UiSelectorV1,
1628 },
1629 NotExistsUnder {
1633 scope: UiSelectorV1,
1634 target: UiSelectorV1,
1635 },
1636 FocusedDescendantIs {
1643 scope: UiSelectorV1,
1644 target: UiSelectorV1,
1645 },
1646 FocusIs {
1647 target: UiSelectorV1,
1648 },
1649 RoleIs {
1650 target: UiSelectorV1,
1651 role: String,
1652 },
1653 LabelContains {
1655 target: UiSelectorV1,
1656 text: String,
1657 },
1658 LabelLenIs {
1663 target: UiSelectorV1,
1664 len_bytes: u32,
1665 },
1666 LabelLenGe {
1668 target: UiSelectorV1,
1669 min_len_bytes: u32,
1670 },
1671 ValueContains {
1673 target: UiSelectorV1,
1674 text: String,
1675 },
1676 ValueEquals {
1681 target: UiSelectorV1,
1682 text: String,
1683 },
1684 ValueLenIs {
1689 target: UiSelectorV1,
1690 len_bytes: u32,
1691 },
1692 ValueLenGe {
1694 target: UiSelectorV1,
1695 min_len_bytes: u32,
1696 },
1697 PosInSetIs {
1699 target: UiSelectorV1,
1700 pos_in_set: u32,
1701 },
1702 SetSizeIs {
1704 target: UiSelectorV1,
1705 set_size: u32,
1706 },
1707 CheckedIs {
1708 target: UiSelectorV1,
1709 checked: bool,
1710 },
1711 SelectedIs {
1712 target: UiSelectorV1,
1713 selected: bool,
1714 },
1715 SemanticsNumericApproxEq {
1721 target: UiSelectorV1,
1722 field: UiSemanticsNumericFieldV1,
1723 value: f64,
1724 #[serde(default)]
1725 eps: f64,
1726 },
1727 SemanticsScrollIsFinite {
1732 target: UiSelectorV1,
1733 field: UiSemanticsScrollFieldV1,
1734 },
1735 SemanticsScrollApproxEq {
1738 target: UiSelectorV1,
1739 field: UiSemanticsScrollFieldV1,
1740 value: f64,
1741 #[serde(default)]
1742 eps: f64,
1743 },
1744 SemanticsScrollNotApproxEq {
1747 target: UiSelectorV1,
1748 field: UiSemanticsScrollFieldV1,
1749 value: f64,
1750 #[serde(default)]
1751 eps: f64,
1752 },
1753 TextCompositionIs {
1761 target: UiSelectorV1,
1762 composing: bool,
1763 },
1764 ImeCursorAreaIsSome {
1770 is_some: bool,
1771 },
1772 ImeCursorAreaWithinWindow {
1777 #[serde(default)]
1778 padding_px: f32,
1779 #[serde(default, skip_serializing_if = "Option::is_none")]
1781 padding_insets_px: Option<UiPaddingInsetsV1>,
1782 #[serde(default)]
1783 eps_px: f32,
1784 },
1785 ImeCursorAreaMinSize {
1789 #[serde(default)]
1790 min_w_px: f32,
1791 #[serde(default)]
1792 min_h_px: f32,
1793 #[serde(default)]
1794 eps_px: f32,
1795 },
1796 ImeSurroundingTextIsSome {
1802 is_some: bool,
1803 },
1804 ImeSurroundingTextValid,
1809 CheckedIsNone {
1810 target: UiSelectorV1,
1811 },
1812 ActiveItemIs {
1820 container: UiSelectorV1,
1822 item: UiSelectorV1,
1824 },
1825 ActiveItemIsNone {
1830 container: UiSelectorV1,
1833 },
1834 BarrierRoots {
1835 #[serde(default)]
1836 barrier_root: UiOptionalRootStateV1,
1837 #[serde(default)]
1838 focus_barrier_root: UiOptionalRootStateV1,
1839 #[serde(default, skip_serializing_if = "Option::is_none")]
1840 require_equal: Option<bool>,
1841 },
1842 RenderTextMissingGlyphsIs {
1843 missing_glyphs: u64,
1844 },
1845 RenderTextFontTraceCapturedWhenMissingGlyphs,
1851 TextFontStackKeyStable {
1858 stable_frames: u32,
1859 },
1860 FontCatalogPopulated,
1866 SystemFontRescanIdle,
1873 AssetLoadMissingBundleAssetRequestsGe {
1878 min: u64,
1879 },
1880 AssetLoadStaleManifestRequestsGe {
1885 min: u64,
1886 },
1887 AssetLoadUnsupportedFileRequestsGe {
1892 min: u64,
1893 },
1894 AssetLoadUnsupportedUrlRequestsGe {
1896 min: u64,
1897 },
1898 AssetLoadExternalReferenceUnavailableRequestsGe {
1904 min: u64,
1905 },
1906 AssetLoadRevisionChangeRequestsGe {
1911 min: u64,
1912 },
1913 AssetLoadRecentOutcomeSeen {
1926 outcome_kind: String,
1927 },
1928 AssetLoadRecentRevisionTransitionSeen {
1936 transition: String,
1937 },
1938 BundledFontBaselineSourceIs {
1944 source: String,
1945 },
1946 RendererFontEnvironmentRevisionGe {
1948 min: u64,
1949 },
1950 RendererFontSourceLaneSeen {
1957 lane: String,
1958 },
1959 RendererFontSourceAssetKeySeen {
1962 asset_key: String,
1963 },
1964 SvgTextBridgeSelectionMissesGe {
1966 min: u64,
1967 },
1968 SvgTextBridgeMissingGlyphsGe {
1970 min: u64,
1971 },
1972 SvgTextBridgeDiagnosticsCleanIs {
1976 clean: bool,
1977 },
1978 SvgTextBridgeFallbackSeen {
1981 from_family: String,
1982 to_family: String,
1983 },
1984 AssetReloadEpochGe {
1990 min: u64,
1991 },
1992 AssetReloadConfiguredBackendIs {
1998 backend: String,
1999 },
2000 AssetReloadActiveBackendIs {
2006 backend: String,
2007 },
2008 AssetReloadFallbackReasonIs {
2013 reason: String,
2014 },
2015 RunnerAccessibilityActivated,
2021 VisibleInWindow {
2022 target: UiSelectorV1,
2023 },
2024 BoundsWithinWindow {
2025 target: UiSelectorV1,
2026 #[serde(default)]
2027 padding_px: f32,
2028 #[serde(default, skip_serializing_if = "Option::is_none")]
2030 padding_insets_px: Option<UiPaddingInsetsV1>,
2031 #[serde(default)]
2032 eps_px: f32,
2033 },
2034 TextInputImeCursorAreaWithinWindow {
2041 #[serde(default)]
2042 padding_px: f32,
2043 #[serde(default, skip_serializing_if = "Option::is_none")]
2045 padding_insets_px: Option<UiPaddingInsetsV1>,
2046 #[serde(default)]
2047 eps_px: f32,
2048 },
2049 BoundsMinSize {
2050 target: UiSelectorV1,
2051 #[serde(default)]
2052 min_w_px: f32,
2053 #[serde(default)]
2054 min_h_px: f32,
2055 #[serde(default)]
2056 eps_px: f32,
2057 },
2058 BoundsMaxSize {
2059 target: UiSelectorV1,
2060 #[serde(default)]
2061 max_w_px: f32,
2062 #[serde(default)]
2063 max_h_px: f32,
2064 #[serde(default)]
2065 eps_px: f32,
2066 },
2067 BoundsApproxEqual {
2072 a: UiSelectorV1,
2073 b: UiSelectorV1,
2074 #[serde(default)]
2075 eps_px: f32,
2076 },
2077 BoundsCenterApproxEqual {
2083 a: UiSelectorV1,
2084 b: UiSelectorV1,
2085 #[serde(default)]
2086 eps_px: f32,
2087 },
2088 BoundsNonOverlapping {
2089 a: UiSelectorV1,
2090 b: UiSelectorV1,
2091 #[serde(default)]
2092 eps_px: f32,
2093 },
2094 BoundsOverlapping {
2095 a: UiSelectorV1,
2096 b: UiSelectorV1,
2097 #[serde(default)]
2098 eps_px: f32,
2099 },
2100 BoundsOverlappingX {
2101 a: UiSelectorV1,
2102 b: UiSelectorV1,
2103 #[serde(default)]
2104 eps_px: f32,
2105 },
2106 BoundsOverlappingY {
2107 a: UiSelectorV1,
2108 b: UiSelectorV1,
2109 #[serde(default)]
2110 eps_px: f32,
2111 },
2112 EventKindSeen {
2117 event_kind: String,
2118 },
2119 AppSnapshotFieldEquals {
2128 pointer: String,
2129 value: serde_json::Value,
2130 },
2131 KnownWindowCountGe {
2135 n: u32,
2136 },
2137 KnownWindowCountIs {
2142 n: u32,
2143 },
2144 PlatformUiWindowHoverDetectionIs {
2152 quality: String,
2153 },
2154 PlatformWindowReceiverAtCursorIs {
2162 window: UiWindowTargetV1,
2163 },
2164 WindowStyleEffectiveIs {
2169 window: UiWindowTargetV1,
2170 style: UiWindowStyleMatchV1,
2171 },
2172 WindowBackgroundMaterialEffectiveIs {
2177 window: UiWindowTargetV1,
2178 material: UiWindowBackgroundMaterialRequestV1,
2179 },
2180 DockDragCurrentWindowIs {
2183 window: UiWindowTargetV1,
2184 },
2185 DockDragKindIs {
2191 drag_kind: String,
2192 },
2193 DockDragMovingWindowIs {
2199 window: UiWindowTargetV1,
2200 },
2201 DockDragWindowUnderMovingWindowIs {
2208 window: UiWindowTargetV1,
2209 },
2210 DockDragActiveIs {
2212 active: bool,
2213 },
2214 DockDragPayloadGhostVisibleIs {
2220 visible: bool,
2221 },
2222 DockDragTransparentPayloadAppliedIs {
2226 applied: bool,
2227 },
2228 DockDragTransparentPayloadHitTestPassthroughAppliedIs {
2231 applied: bool,
2232 },
2233 DockDragWindowUnderCursorSourceIs {
2250 source: String,
2251 },
2252 DockDragWindowUnderMovingWindowSourceIs {
2265 source: String,
2266 },
2267 DockFloatingDragActiveIs {
2271 active: bool,
2272 },
2273 DockDropPreviewKindIs {
2282 preview_kind: String,
2283 },
2284 DockDropResolveSourceIs {
2301 source: String,
2302 },
2303 DockDropResolvedIsSome {
2308 some: bool,
2309 },
2310 DockDropResolvedZoneIs {
2320 zone: String,
2321 },
2322 DockDropResolvedInsertIndexIs {
2328 index: u32,
2329 },
2330 DockTabStripActiveOverflowIs {
2335 overflow: bool,
2336 },
2337 DockTabStripActiveVisibleIs {
2343 visible: bool,
2344 },
2345 DockTabStripActiveScrollPxGe {
2350 px: f32,
2351 },
2352 DockTabStripActiveScrollPxLe {
2356 px: f32,
2357 },
2358 WorkspaceTabStripActiveOverflowIs {
2363 overflow: bool,
2364 #[serde(default, skip_serializing_if = "Option::is_none")]
2365 pane_id: Option<String>,
2366 },
2367 WorkspaceTabStripActiveVisibleIs {
2373 visible: bool,
2374 #[serde(default, skip_serializing_if = "Option::is_none")]
2375 pane_id: Option<String>,
2376 },
2377 WorkspaceTabStripActiveScrollPxGe {
2382 px: f32,
2383 #[serde(default, skip_serializing_if = "Option::is_none")]
2384 pane_id: Option<String>,
2385 },
2386 WorkspaceTabStripActiveScrollPxLe {
2391 px: f32,
2392 #[serde(default, skip_serializing_if = "Option::is_none")]
2393 pane_id: Option<String>,
2394 },
2395 WorkspaceTabStripDragActiveIs {
2400 active: bool,
2401 #[serde(default, skip_serializing_if = "Option::is_none")]
2402 pane_id: Option<String>,
2403 },
2404 WorkspaceTabStripDragArmedIs {
2410 armed: bool,
2411 #[serde(default, skip_serializing_if = "Option::is_none")]
2412 pane_id: Option<String>,
2413 },
2414 DockGraphCanonicalIs {
2416 canonical: bool,
2417 },
2418 DockGraphHasNestedSameAxisSplitsIs {
2420 has_nested: bool,
2421 },
2422 DockGraphNodeCountLe {
2428 max: u32,
2429 },
2430 DockGraphMaxSplitDepthLe {
2432 max: u32,
2433 },
2434 DockGraphSignatureIs {
2440 signature: String,
2441 },
2442 DockGraphSignatureContains {
2447 needle: String,
2448 },
2449 DockGraphSignatureNotContains {
2452 needle: String,
2453 },
2454 DockGraphSignatureFingerprint64Is {
2456 fingerprint64: u64,
2457 },
2458}
2459
2460#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2461#[serde(rename_all = "snake_case")]
2462pub enum UiSemanticsNumericFieldV1 {
2463 Value,
2464 Min,
2465 Max,
2466 Step,
2467 Jump,
2468}
2469
2470#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2471#[serde(rename_all = "snake_case")]
2472pub enum UiSemanticsScrollFieldV1 {
2473 X,
2474 XMin,
2475 XMax,
2476 Y,
2477 YMin,
2478 YMax,
2479}
2480
2481#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
2482#[serde(rename_all = "snake_case")]
2483pub enum UiOptionalRootStateV1 {
2484 #[default]
2485 Any,
2486 None,
2487 Some,
2488}
2489
2490#[derive(Debug, Clone, Serialize, Deserialize)]
2491#[serde(tag = "kind", rename_all = "snake_case")]
2492pub enum UiSelectorV1 {
2493 RoleAndName {
2494 role: String,
2495 name: String,
2496 #[serde(default, skip_serializing_if = "Option::is_none")]
2497 root_z_index: Option<u32>,
2498 },
2499 RoleAndPath {
2500 role: String,
2501 name: String,
2502 ancestors: Vec<UiRoleAndNameV1>,
2503 #[serde(default, skip_serializing_if = "Option::is_none")]
2504 root_z_index: Option<u32>,
2505 },
2506 TestId {
2507 id: String,
2508 #[serde(default, skip_serializing_if = "Option::is_none")]
2509 root_z_index: Option<u32>,
2510 },
2511 GlobalElementId {
2512 element: u64,
2513 #[serde(default, skip_serializing_if = "Option::is_none")]
2514 root_z_index: Option<u32>,
2515 },
2516 NodeId {
2517 node: u64,
2518 #[serde(default, skip_serializing_if = "Option::is_none")]
2519 root_z_index: Option<u32>,
2520 },
2521}
2522
2523#[derive(Debug, Clone, Serialize, Deserialize)]
2524pub struct UiRoleAndNameV1 {
2525 pub role: String,
2526 pub name: String,
2527}
2528
2529#[derive(Debug, Clone, Serialize, Deserialize)]
2530pub struct UiSemanticsNodeGetV1 {
2531 pub schema_version: u32,
2532 pub window: u64,
2533 pub node_id: u64,
2534}
2535
2536#[derive(Debug, Clone, Serialize, Deserialize)]
2537pub struct UiSemanticsNodeGetAckV1 {
2538 pub schema_version: u32,
2539 pub status: String,
2540 #[serde(default, skip_serializing_if = "Option::is_none")]
2541 pub reason: Option<String>,
2542 pub window: u64,
2543 pub node_id: u64,
2544 #[serde(default, skip_serializing_if = "Option::is_none")]
2545 pub semantics_fingerprint: Option<u64>,
2546 #[serde(default, skip_serializing_if = "Option::is_none")]
2547 pub node: Option<serde_json::Value>,
2548 #[serde(default)]
2549 pub children: Vec<u64>,
2550 #[serde(default, skip_serializing_if = "Option::is_none")]
2551 pub captured_unix_ms: Option<u64>,
2552}
2553
2554#[derive(Debug, Clone, Serialize, Deserialize)]
2555pub struct UiHitTestExplainV1 {
2556 pub schema_version: u32,
2557 pub window: u64,
2558 pub target: UiSelectorV1,
2559}
2560
2561#[derive(Debug, Clone, Serialize, Deserialize)]
2562pub struct UiHitTestExplainAckV1 {
2563 pub schema_version: u32,
2564 pub status: String,
2565 #[serde(default, skip_serializing_if = "Option::is_none")]
2566 pub reason: Option<String>,
2567 pub window: u64,
2568 pub target: UiSelectorV1,
2569 #[serde(default, skip_serializing_if = "Option::is_none")]
2570 pub semantics_fingerprint: Option<u64>,
2571 #[serde(default, skip_serializing_if = "Option::is_none")]
2572 pub hittable: Option<bool>,
2573 #[serde(default, skip_serializing_if = "Option::is_none")]
2574 pub hit_test: Option<UiHitTestTraceEntryV1>,
2575 #[serde(default, skip_serializing_if = "Option::is_none")]
2576 pub captured_unix_ms: Option<u64>,
2577}
2578
2579#[derive(Debug, Clone, Serialize, Deserialize)]
2580pub struct UiInspectConfigV1 {
2581 pub schema_version: u32,
2582 pub enabled: bool,
2583 #[serde(default = "serde_default_true")]
2584 pub consume_clicks: bool,
2585}
2586
2587#[derive(Debug, Clone, Serialize, Deserialize)]
2588pub struct DevtoolsBundleDumpV1 {
2589 pub schema_version: u32,
2590 #[serde(default, skip_serializing_if = "Option::is_none")]
2591 pub label: Option<String>,
2592 #[serde(default, skip_serializing_if = "Option::is_none")]
2598 pub max_snapshots: Option<u32>,
2599}
2600
2601#[derive(Debug, Clone, Serialize, Deserialize)]
2606pub struct DevtoolsAppExitRequestV1 {
2607 pub schema_version: u32,
2608 #[serde(default, skip_serializing_if = "Option::is_none")]
2609 pub reason: Option<String>,
2610 #[serde(default, skip_serializing_if = "Option::is_none")]
2612 pub delay_ms: Option<u64>,
2613}
2614
2615#[derive(Debug, Clone, Serialize, Deserialize)]
2616pub struct DevtoolsBundleDumpedV1 {
2617 pub schema_version: u32,
2618 pub exported_unix_ms: u64,
2619 pub out_dir: String,
2620 pub dir: String,
2621 #[serde(default, skip_serializing_if = "Option::is_none")]
2622 pub bundle: Option<serde_json::Value>,
2623 #[serde(default, skip_serializing_if = "Option::is_none")]
2629 pub bundle_json_chunk: Option<String>,
2630 #[serde(default, skip_serializing_if = "Option::is_none")]
2631 pub bundle_json_chunk_index: Option<u32>,
2632 #[serde(default, skip_serializing_if = "Option::is_none")]
2633 pub bundle_json_chunk_count: Option<u32>,
2634}
2635
2636#[derive(Debug, Clone, Serialize, Deserialize)]
2637pub struct DevtoolsScreenshotRequestV1 {
2638 pub schema_version: u32,
2639 #[serde(default, skip_serializing_if = "Option::is_none")]
2640 pub label: Option<String>,
2641 #[serde(default = "default_capture_screenshot_timeout_frames")]
2642 pub timeout_frames: u32,
2643 #[serde(default, skip_serializing_if = "Option::is_none")]
2644 pub window: Option<u64>,
2645}
2646
2647#[derive(Debug, Clone, Serialize, Deserialize)]
2648pub struct DevtoolsScreenshotResultV1 {
2649 pub schema_version: u32,
2650 pub status: String,
2651 #[serde(default, skip_serializing_if = "Option::is_none")]
2652 pub reason: Option<String>,
2653 pub request_id: String,
2654 pub window: u64,
2655 pub bundle_dir_name: String,
2656 #[serde(default, skip_serializing_if = "Option::is_none")]
2657 pub screenshots_dir: Option<String>,
2658 #[serde(default, skip_serializing_if = "Option::is_none")]
2659 pub entry: Option<serde_json::Value>,
2660}
2661
2662#[derive(Debug, Clone, Serialize, Deserialize)]
2671pub struct DiagScreenshotRequestV1 {
2672 pub schema_version: u32,
2673 pub out_dir: String,
2674 pub bundle_dir_name: String,
2675 #[serde(default, skip_serializing_if = "Option::is_none")]
2676 pub request_id: Option<String>,
2677 #[serde(default)]
2678 pub windows: Vec<DiagScreenshotWindowRequestV1>,
2679}
2680
2681#[derive(Debug, Clone, Serialize, Deserialize)]
2682pub struct DiagScreenshotWindowRequestV1 {
2683 pub window: u64,
2684 pub tick_id: u64,
2685 pub frame_id: u64,
2686 #[serde(default = "serde_default_one_f64")]
2687 pub scale_factor: f64,
2688}
2689
2690#[derive(Debug, Clone, Serialize, Deserialize)]
2691pub struct DiagScreenshotResultFileV1 {
2692 #[serde(default = "default_diag_screenshot_schema_version")]
2693 pub schema_version: u32,
2694 #[serde(default, skip_serializing_if = "Option::is_none")]
2695 pub updated_unix_ms: Option<u64>,
2696 #[serde(default)]
2697 pub completed: Vec<DiagScreenshotResultEntryV1>,
2698}
2699
2700impl Default for DiagScreenshotResultFileV1 {
2701 fn default() -> Self {
2702 Self {
2703 schema_version: default_diag_screenshot_schema_version(),
2704 updated_unix_ms: None,
2705 completed: Vec::new(),
2706 }
2707 }
2708}
2709
2710#[derive(Debug, Clone, Serialize, Deserialize)]
2711pub struct DiagScreenshotResultEntryV1 {
2712 #[serde(default, skip_serializing_if = "Option::is_none")]
2713 pub request_id: Option<String>,
2714 pub bundle_dir_name: String,
2715 pub window: u64,
2716 pub tick_id: u64,
2717 pub frame_id: u64,
2718 pub scale_factor: f32,
2719 pub file: String,
2720 pub width_px: u32,
2721 pub height_px: u32,
2722 pub completed_unix_ms: u64,
2723}
2724
2725#[derive(Debug, Clone, Serialize, Deserialize)]
2726pub struct UiArtifactStatsV1 {
2727 pub schema_version: u32,
2728 #[serde(default, skip_serializing_if = "Option::is_none")]
2729 pub bundle_json_bytes: Option<u64>,
2730 #[serde(default)]
2731 pub window_count: u64,
2732 #[serde(default)]
2733 pub event_count: u64,
2734 #[serde(default)]
2735 pub snapshot_count: u64,
2736 #[serde(default)]
2737 pub max_snapshots: u64,
2738 #[serde(default, skip_serializing_if = "Option::is_none")]
2739 pub dump_max_snapshots: Option<u64>,
2740}
2741
2742#[derive(Debug, Clone, Serialize, Deserialize)]
2743pub struct UiScriptResultV1 {
2744 pub schema_version: u32,
2745 pub run_id: u64,
2746 pub updated_unix_ms: u64,
2747 pub window: Option<u64>,
2748 pub stage: UiScriptStageV1,
2749 pub step_index: Option<u32>,
2750 #[serde(default, skip_serializing_if = "Option::is_none")]
2751 pub reason_code: Option<String>,
2752 pub reason: Option<String>,
2753 #[serde(default, skip_serializing_if = "Option::is_none")]
2754 pub evidence: Option<UiScriptEvidenceV1>,
2755 pub last_bundle_dir: Option<String>,
2756 #[serde(default, skip_serializing_if = "Option::is_none")]
2757 pub last_bundle_artifact: Option<UiArtifactStatsV1>,
2758}
2759
2760#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2761pub struct UiScriptEvidenceV1 {
2762 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2763 pub event_log: Vec<UiScriptEventLogEntryV1>,
2764 #[serde(default, skip_serializing_if = "is_zero_u64")]
2765 pub event_log_dropped: u64,
2766 #[serde(default, skip_serializing_if = "Option::is_none")]
2767 pub capabilities_check: Option<UiCapabilitiesCheckV1>,
2768 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2769 pub selector_resolution_trace: Vec<UiSelectorResolutionTraceEntryV1>,
2770 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2771 pub hit_test_trace: Vec<UiHitTestTraceEntryV1>,
2772 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2773 pub click_stable_trace: Vec<UiClickStableTraceEntryV1>,
2774 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2775 pub bounds_stable_trace: Vec<UiBoundsStableTraceEntryV1>,
2776 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2777 pub focus_trace: Vec<UiFocusTraceEntryV1>,
2778 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2779 pub shortcut_routing_trace: Vec<UiShortcutRoutingTraceEntryV1>,
2780 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2781 pub command_dispatch_trace: Vec<UiCommandDispatchTraceEntryV1>,
2782 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2783 pub overlay_placement_trace: Vec<UiOverlayPlacementTraceEntryV1>,
2784 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2785 pub web_ime_trace: Vec<UiWebImeTraceEntryV1>,
2786 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2787 pub ime_event_trace: Vec<UiImeEventTraceEntryV1>,
2788}
2789
2790#[derive(Debug, Clone, Serialize, Deserialize)]
2791pub struct UiCapabilitiesCheckV1 {
2792 pub schema_version: u32,
2793 pub source: String,
2794 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2795 pub required: Vec<String>,
2796 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2797 pub available: Vec<String>,
2798 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2799 pub missing: Vec<String>,
2800}
2801
2802#[derive(Debug, Clone, Serialize, Deserialize)]
2803pub struct UiScriptEventLogEntryV1 {
2804 pub unix_ms: u64,
2805 pub kind: String,
2806 #[serde(default, skip_serializing_if = "Option::is_none")]
2807 pub step_index: Option<u32>,
2808 #[serde(default, skip_serializing_if = "Option::is_none")]
2809 pub note: Option<String>,
2810 #[serde(default, skip_serializing_if = "Option::is_none")]
2811 pub bundle_dir: Option<String>,
2812 #[serde(default, skip_serializing_if = "Option::is_none")]
2814 pub window: Option<u64>,
2815 #[serde(default, skip_serializing_if = "Option::is_none")]
2817 pub tick_id: Option<u64>,
2818 #[serde(default, skip_serializing_if = "Option::is_none")]
2820 pub frame_id: Option<u64>,
2821 #[serde(default, skip_serializing_if = "Option::is_none")]
2823 pub window_snapshot_seq: Option<u64>,
2824}
2825
2826#[derive(Debug, Clone, Serialize, Deserialize)]
2827pub struct UiSelectorResolutionTraceEntryV1 {
2828 pub step_index: u32,
2829 pub selector: UiSelectorV1,
2830 #[serde(default)]
2831 pub match_count: u32,
2832 #[serde(default, skip_serializing_if = "Option::is_none")]
2833 pub chosen_node_id: Option<u64>,
2834 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2835 pub candidates: Vec<UiSelectorResolutionCandidateV1>,
2836 #[serde(default, skip_serializing_if = "Option::is_none")]
2837 pub note: Option<String>,
2838}
2839
2840#[derive(Debug, Clone, Serialize, Deserialize)]
2841pub struct UiSelectorResolutionCandidateV1 {
2842 pub node_id: u64,
2843 pub role: String,
2844 #[serde(default, skip_serializing_if = "Option::is_none")]
2845 pub name: Option<String>,
2846 #[serde(default, skip_serializing_if = "Option::is_none")]
2847 pub test_id: Option<String>,
2848}
2849
2850#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
2851pub struct UiPointV1 {
2852 pub x_px: f32,
2853 pub y_px: f32,
2854}
2855
2856#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
2857pub struct UiRectV1 {
2858 pub x_px: f32,
2859 pub y_px: f32,
2860 pub w_px: f32,
2861 pub h_px: f32,
2862}
2863
2864#[derive(Debug, Clone, Serialize, Deserialize)]
2865pub struct UiHitTestTraceEntryV1 {
2866 pub step_index: u32,
2867 pub selector: UiSelectorV1,
2868 pub position: UiPointV1,
2869 #[serde(default, skip_serializing_if = "Option::is_none")]
2870 pub intended_node_id: Option<u64>,
2871 #[serde(default, skip_serializing_if = "Option::is_none")]
2872 pub intended_test_id: Option<String>,
2873 #[serde(default, skip_serializing_if = "Option::is_none")]
2874 pub intended_bounds: Option<UiRectV1>,
2875 #[serde(default, skip_serializing_if = "Option::is_none")]
2876 pub hit_node_id: Option<u64>,
2877 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2881 pub hit_node_path: Vec<u64>,
2882 #[serde(default, skip_serializing_if = "Option::is_none")]
2883 pub hit_semantics_node_id: Option<u64>,
2884 #[serde(default, skip_serializing_if = "Option::is_none")]
2885 pub hit_semantics_test_id: Option<String>,
2886 #[serde(default, skip_serializing_if = "Option::is_none")]
2887 pub includes_intended: Option<bool>,
2888 #[serde(default, skip_serializing_if = "Option::is_none")]
2892 pub hit_path_contains_intended: Option<bool>,
2893 #[serde(default, skip_serializing_if = "Option::is_none")]
2906 pub blocking_reason: Option<String>,
2907 #[serde(default, skip_serializing_if = "Option::is_none")]
2909 pub blocking_root: Option<u64>,
2910 #[serde(default, skip_serializing_if = "Option::is_none")]
2912 pub blocking_layer_id: Option<u64>,
2913 #[serde(default, skip_serializing_if = "Option::is_none")]
2917 pub routing_explain: Option<String>,
2918 #[serde(default, skip_serializing_if = "Option::is_none")]
2919 pub barrier_root: Option<u64>,
2920 #[serde(default, skip_serializing_if = "Option::is_none")]
2921 pub focus_barrier_root: Option<u64>,
2922 #[serde(default, skip_serializing_if = "Option::is_none")]
2927 pub pointer_occlusion: Option<String>,
2928 #[serde(default, skip_serializing_if = "Option::is_none")]
2929 pub pointer_occlusion_layer_id: Option<u64>,
2930 #[serde(default, skip_serializing_if = "Option::is_none")]
2935 pub pointer_occlusion_node_id: Option<u64>,
2936 #[serde(default, skip_serializing_if = "Option::is_none")]
2937 pub pointer_occlusion_test_id: Option<String>,
2938 #[serde(default, skip_serializing_if = "Option::is_none")]
2939 pub pointer_occlusion_role: Option<String>,
2940 #[serde(default, skip_serializing_if = "Option::is_none")]
2941 pub pointer_occlusion_bounds: Option<UiRectV1>,
2942 #[serde(default, skip_serializing_if = "Option::is_none")]
2943 pub pointer_capture_active: Option<bool>,
2944 #[serde(default, skip_serializing_if = "Option::is_none")]
2945 pub pointer_capture_layer_id: Option<u64>,
2946 #[serde(default, skip_serializing_if = "Option::is_none")]
2947 pub pointer_capture_multiple_layers: Option<bool>,
2948 #[serde(default, skip_serializing_if = "Option::is_none")]
2950 pub pointer_capture_node_id: Option<u64>,
2951 #[serde(default, skip_serializing_if = "Option::is_none")]
2952 pub pointer_capture_test_id: Option<String>,
2953 #[serde(default, skip_serializing_if = "Option::is_none")]
2954 pub pointer_capture_role: Option<String>,
2955 #[serde(default, skip_serializing_if = "Option::is_none")]
2956 pub pointer_capture_bounds: Option<UiRectV1>,
2957 #[serde(default, skip_serializing_if = "Option::is_none")]
2958 pub pointer_capture_element: Option<u64>,
2959 #[serde(default, skip_serializing_if = "Option::is_none")]
2960 pub pointer_capture_element_path: Option<String>,
2961 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2962 pub scope_roots: Vec<UiHitTestScopeRootEvidenceV1>,
2963 #[serde(default, skip_serializing_if = "Option::is_none")]
2964 pub note: Option<String>,
2965}
2966
2967#[derive(Debug, Clone, Serialize, Deserialize)]
2968pub struct UiClickStableTraceEntryV1 {
2969 pub step_index: u32,
2970 pub stable_required: u32,
2971 pub stable_count: u32,
2972 pub moved_px: f32,
2973 pub max_move_px: f32,
2974 pub remaining_frames: u32,
2975 pub hit_test: UiHitTestTraceEntryV1,
2976}
2977
2978#[derive(Debug, Clone, Serialize, Deserialize)]
2979pub struct UiBoundsStableTraceEntryV1 {
2980 pub step_index: u32,
2981 pub selector: UiSelectorV1,
2982 pub stable_required: u32,
2983 pub stable_count: u32,
2984 pub moved_px: f32,
2985 pub max_move_px: f32,
2986 pub remaining_frames: u32,
2987 #[serde(default, skip_serializing_if = "Option::is_none")]
2988 pub bounds: Option<UiRectV1>,
2989 #[serde(default, skip_serializing_if = "Option::is_none")]
2990 pub note: Option<String>,
2991}
2992
2993#[derive(Debug, Clone, Serialize, Deserialize)]
2994pub struct UiHitTestScopeRootEvidenceV1 {
2995 pub kind: String,
2996 pub root: u64,
2997 #[serde(default, skip_serializing_if = "Option::is_none")]
2998 pub layer_id: Option<u64>,
2999 #[serde(default, skip_serializing_if = "Option::is_none")]
3000 pub pointer_occlusion: Option<String>,
3001 #[serde(default, skip_serializing_if = "Option::is_none")]
3002 pub blocks_underlay_input: Option<bool>,
3003 #[serde(default, skip_serializing_if = "Option::is_none")]
3004 pub hit_testable: Option<bool>,
3005}
3006
3007#[derive(Debug, Clone, Serialize, Deserialize)]
3008pub struct UiFocusTraceEntryV1 {
3009 pub step_index: u32,
3010 #[serde(default, skip_serializing_if = "Option::is_none")]
3011 pub note: Option<String>,
3012 #[serde(default, skip_serializing_if = "Option::is_none")]
3013 pub reason_code: Option<String>,
3014 #[serde(default, skip_serializing_if = "Option::is_none")]
3015 pub text_input_snapshot: Option<UiTextInputSnapshotV1>,
3016 #[serde(default, skip_serializing_if = "Option::is_none")]
3017 pub expected_node_id: Option<u64>,
3018 #[serde(default, skip_serializing_if = "Option::is_none")]
3019 pub expected_test_id: Option<String>,
3020 #[serde(default, skip_serializing_if = "Option::is_none")]
3021 pub modal_barrier_root: Option<u64>,
3022 #[serde(default, skip_serializing_if = "Option::is_none")]
3023 pub focus_barrier_root: Option<u64>,
3024 #[serde(default, skip_serializing_if = "Option::is_none")]
3025 pub pointer_occlusion: Option<String>,
3026 #[serde(default, skip_serializing_if = "Option::is_none")]
3027 pub pointer_occlusion_layer_id: Option<u64>,
3028 #[serde(default, skip_serializing_if = "Option::is_none")]
3029 pub pointer_capture_active: Option<bool>,
3030 #[serde(default, skip_serializing_if = "Option::is_none")]
3031 pub pointer_capture_layer_id: Option<u64>,
3032 #[serde(default, skip_serializing_if = "Option::is_none")]
3033 pub pointer_capture_multiple_layers: Option<bool>,
3034 #[serde(default, skip_serializing_if = "Option::is_none")]
3035 pub focused_element: Option<u64>,
3036 #[serde(default, skip_serializing_if = "Option::is_none")]
3037 pub focused_element_path: Option<String>,
3038 #[serde(default, skip_serializing_if = "Option::is_none")]
3039 pub focused_node_id: Option<u64>,
3040 #[serde(default, skip_serializing_if = "Option::is_none")]
3041 pub focused_test_id: Option<String>,
3042 #[serde(default, skip_serializing_if = "Option::is_none")]
3043 pub focused_role: Option<String>,
3044 #[serde(default, skip_serializing_if = "Option::is_none")]
3045 pub matches_expected: Option<bool>,
3046}
3047
3048#[derive(Debug, Clone, Serialize, Deserialize)]
3049pub struct UiTextInputSnapshotV1 {
3050 #[serde(default)]
3051 pub focus_is_text_input: bool,
3052 #[serde(default)]
3053 pub is_composing: bool,
3054 #[serde(default)]
3055 pub text_len_utf16: u32,
3056 #[serde(default, skip_serializing_if = "Option::is_none")]
3057 pub selection_utf16: Option<(u32, u32)>,
3058 #[serde(default, skip_serializing_if = "Option::is_none")]
3059 pub marked_utf16: Option<(u32, u32)>,
3060 #[serde(default, skip_serializing_if = "Option::is_none")]
3061 pub ime_cursor_area: Option<UiRectV1>,
3062 #[serde(default, skip_serializing_if = "Option::is_none")]
3067 pub ime_surrounding_text_len_bytes: Option<u32>,
3068 #[serde(default, skip_serializing_if = "Option::is_none")]
3069 pub ime_surrounding_cursor_bytes: Option<u32>,
3070 #[serde(default, skip_serializing_if = "Option::is_none")]
3071 pub ime_surrounding_anchor_bytes: Option<u32>,
3072}
3073
3074#[derive(Debug, Clone, Serialize, Deserialize)]
3075pub struct UiShortcutRoutingTraceEntryV1 {
3076 pub step_index: u32,
3077 #[serde(default, skip_serializing_if = "Option::is_none")]
3078 pub note: Option<String>,
3079 #[serde(default)]
3080 pub frame_id: u64,
3081 pub phase: String,
3082 #[serde(default)]
3083 pub deferred: bool,
3084 #[serde(default)]
3085 pub focus_is_text_input: bool,
3086 #[serde(default)]
3087 pub ime_composing: bool,
3088 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3089 pub key_contexts: Vec<String>,
3090 pub key: String,
3091 pub modifiers: UiKeyModifiersV1,
3092 pub repeat: bool,
3093 pub outcome: String,
3094 #[serde(default, skip_serializing_if = "Option::is_none")]
3095 pub command: Option<String>,
3096 #[serde(default, skip_serializing_if = "Option::is_none")]
3097 pub command_enabled: Option<bool>,
3098 #[serde(default, skip_serializing_if = "Option::is_none")]
3099 pub pending_sequence_len: Option<u32>,
3100}
3101
3102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3103#[serde(rename_all = "snake_case")]
3104pub enum UiOverlayPlacementTraceKindV1 {
3105 AnchoredPanel,
3106 PlacedRect,
3107}
3108
3109#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3110pub struct UiShortcutRoutingTraceQueryV1 {
3111 #[serde(default, skip_serializing_if = "Option::is_none")]
3112 pub phase: Option<String>,
3113 #[serde(default, skip_serializing_if = "Option::is_none")]
3114 pub outcome: Option<String>,
3115 #[serde(default, skip_serializing_if = "Option::is_none")]
3116 pub key: Option<String>,
3117 #[serde(default, skip_serializing_if = "Option::is_none")]
3118 pub command: Option<String>,
3119 #[serde(default, skip_serializing_if = "Option::is_none")]
3120 pub ime_composing: Option<bool>,
3121 #[serde(default, skip_serializing_if = "Option::is_none")]
3122 pub focus_is_text_input: Option<bool>,
3123 #[serde(default, skip_serializing_if = "Option::is_none")]
3124 pub key_context: Option<String>,
3125}
3126
3127#[derive(Debug, Clone, Serialize, Deserialize)]
3128pub struct UiCommandDispatchTraceEntryV1 {
3129 pub step_index: u32,
3130 pub frame_id: u64,
3131 pub command: String,
3132 pub handled: bool,
3133 #[serde(default, skip_serializing_if = "Option::is_none")]
3137 pub handled_by_scope: Option<String>,
3138 #[serde(default)]
3140 pub handled_by_driver: bool,
3141 #[serde(default)]
3142 pub stopped: bool,
3143 #[serde(default)]
3144 pub source_kind: String,
3145 #[serde(default, skip_serializing_if = "Option::is_none")]
3146 pub source_element: Option<u64>,
3147 #[serde(default, skip_serializing_if = "Option::is_none")]
3158 pub source_test_id: Option<String>,
3159 #[serde(default, skip_serializing_if = "Option::is_none")]
3160 pub handled_by_element: Option<u64>,
3161 #[serde(default, skip_serializing_if = "Option::is_none")]
3163 pub handled_by_test_id: Option<String>,
3164 #[serde(default)]
3165 pub started_from_focus: bool,
3166 #[serde(default)]
3167 pub used_default_root_fallback: bool,
3168}
3169
3170#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3171pub struct UiCommandDispatchTraceQueryV1 {
3172 #[serde(default, skip_serializing_if = "Option::is_none")]
3173 pub command: Option<String>,
3174 #[serde(default, skip_serializing_if = "Option::is_none")]
3175 pub source_kind: Option<String>,
3176 #[serde(default, skip_serializing_if = "Option::is_none")]
3177 pub source_test_id: Option<String>,
3178 #[serde(default, skip_serializing_if = "Option::is_none")]
3179 pub handled: Option<bool>,
3180 #[serde(default, skip_serializing_if = "Option::is_none")]
3181 pub handled_by_scope: Option<String>,
3182 #[serde(default, skip_serializing_if = "Option::is_none")]
3183 pub handled_by_driver: Option<bool>,
3184 #[serde(default, skip_serializing_if = "Option::is_none")]
3185 pub handled_by_test_id: Option<String>,
3186 #[serde(default, skip_serializing_if = "Option::is_none")]
3187 pub started_from_focus: Option<bool>,
3188 #[serde(default, skip_serializing_if = "Option::is_none")]
3189 pub used_default_root_fallback: Option<bool>,
3190}
3191
3192#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3193#[serde(rename_all = "snake_case")]
3194pub enum UiLayoutDirectionV1 {
3195 Ltr,
3196 Rtl,
3197}
3198
3199#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3200#[serde(rename_all = "snake_case")]
3201pub enum UiOverlaySideV1 {
3202 Top,
3203 Bottom,
3204 Left,
3205 Right,
3206}
3207
3208#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3209#[serde(rename_all = "snake_case")]
3210pub enum UiOverlayAlignV1 {
3211 Start,
3212 Center,
3213 End,
3214}
3215
3216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3217#[serde(rename_all = "snake_case")]
3218pub enum UiOverlayStickyModeV1 {
3219 Partial,
3220 Always,
3221}
3222
3223#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3224pub struct UiEdgesV1 {
3225 pub top_px: f32,
3226 pub right_px: f32,
3227 pub bottom_px: f32,
3228 pub left_px: f32,
3229}
3230
3231#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3232pub struct UiSizeV1 {
3233 pub w_px: f32,
3234 pub h_px: f32,
3235}
3236
3237#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3238pub struct UiOverlayOffsetV1 {
3239 pub main_axis_px: f32,
3240 pub cross_axis_px: f32,
3241 #[serde(default, skip_serializing_if = "Option::is_none")]
3242 pub alignment_axis_px: Option<f32>,
3243}
3244
3245#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3246pub struct UiOverlayShiftV1 {
3247 pub main_axis: bool,
3248 pub cross_axis: bool,
3249}
3250
3251#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3252pub struct UiOverlayArrowLayoutV1 {
3253 pub side: UiOverlaySideV1,
3254 pub offset_px: f32,
3255 pub alignment_offset_px: f32,
3256 pub center_offset_px: f32,
3257}
3258
3259#[derive(Debug, Clone, Serialize, Deserialize)]
3260#[serde(tag = "kind", rename_all = "snake_case")]
3261pub enum UiOverlayPlacementTraceEntryV1 {
3262 AnchoredPanel {
3263 step_index: u32,
3264 #[serde(default, skip_serializing_if = "Option::is_none")]
3265 note: Option<String>,
3266 #[serde(default)]
3267 frame_id: u64,
3268 #[serde(default, skip_serializing_if = "Option::is_none")]
3269 overlay_root_name: Option<String>,
3270 #[serde(default, skip_serializing_if = "Option::is_none")]
3271 anchor_element: Option<u64>,
3272 #[serde(default, skip_serializing_if = "Option::is_none")]
3273 anchor_test_id: Option<String>,
3274 #[serde(default, skip_serializing_if = "Option::is_none")]
3275 content_element: Option<u64>,
3276 #[serde(default, skip_serializing_if = "Option::is_none")]
3277 content_test_id: Option<String>,
3278
3279 outer_input: UiRectV1,
3280 outer_collision: UiRectV1,
3281 anchor: UiRectV1,
3282 desired: UiSizeV1,
3283 side_offset_px: f32,
3284 preferred_side: UiOverlaySideV1,
3285 align: UiOverlayAlignV1,
3286 direction: UiLayoutDirectionV1,
3287 sticky: UiOverlayStickyModeV1,
3288 offset: UiOverlayOffsetV1,
3289 shift: UiOverlayShiftV1,
3290 collision_padding: UiEdgesV1,
3291 #[serde(default, skip_serializing_if = "Option::is_none")]
3292 collision_boundary: Option<UiRectV1>,
3293 gap_px: f32,
3294
3295 preferred_rect: UiRectV1,
3296 flipped_rect: UiRectV1,
3297 #[serde(default)]
3298 preferred_fits_without_main_clamp: bool,
3299 #[serde(default)]
3300 flipped_fits_without_main_clamp: bool,
3301 #[serde(default)]
3302 preferred_available_main_px: f32,
3303 #[serde(default)]
3304 flipped_available_main_px: f32,
3305 chosen_side: UiOverlaySideV1,
3306 chosen_rect: UiRectV1,
3307 rect_after_shift: UiRectV1,
3308 shift_delta: UiPointV1,
3309 final_rect: UiRectV1,
3310 #[serde(default, skip_serializing_if = "Option::is_none")]
3311 arrow: Option<UiOverlayArrowLayoutV1>,
3312 },
3313 PlacedRect {
3314 step_index: u32,
3315 #[serde(default, skip_serializing_if = "Option::is_none")]
3316 note: Option<String>,
3317 #[serde(default)]
3318 frame_id: u64,
3319 #[serde(default, skip_serializing_if = "Option::is_none")]
3320 overlay_root_name: Option<String>,
3321 #[serde(default, skip_serializing_if = "Option::is_none")]
3322 anchor_element: Option<u64>,
3323 #[serde(default, skip_serializing_if = "Option::is_none")]
3324 anchor_test_id: Option<String>,
3325 #[serde(default, skip_serializing_if = "Option::is_none")]
3326 content_element: Option<u64>,
3327 #[serde(default, skip_serializing_if = "Option::is_none")]
3328 content_test_id: Option<String>,
3329 outer: UiRectV1,
3330 anchor: UiRectV1,
3331 placed: UiRectV1,
3332 #[serde(default, skip_serializing_if = "Option::is_none")]
3333 side: Option<UiOverlaySideV1>,
3334 },
3335}
3336
3337#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3338pub struct UiOverlayPlacementTraceQueryV1 {
3339 #[serde(default, skip_serializing_if = "Option::is_none")]
3340 pub kind: Option<UiOverlayPlacementTraceKindV1>,
3341 #[serde(default, skip_serializing_if = "Option::is_none")]
3342 pub overlay_root_name: Option<String>,
3343 #[serde(default, skip_serializing_if = "Option::is_none")]
3344 pub anchor_test_id: Option<String>,
3345 #[serde(default, skip_serializing_if = "Option::is_none")]
3346 pub content_test_id: Option<String>,
3347 #[serde(default, skip_serializing_if = "Option::is_none")]
3348 pub preferred_side: Option<UiOverlaySideV1>,
3349 #[serde(default, skip_serializing_if = "Option::is_none")]
3350 pub chosen_side: Option<UiOverlaySideV1>,
3351 #[serde(default, skip_serializing_if = "Option::is_none")]
3354 pub flipped: Option<bool>,
3355 #[serde(default, skip_serializing_if = "Option::is_none")]
3356 pub align: Option<UiOverlayAlignV1>,
3357 #[serde(default, skip_serializing_if = "Option::is_none")]
3358 pub sticky: Option<UiOverlayStickyModeV1>,
3359}
3360
3361#[derive(Debug, Clone, Serialize, Deserialize)]
3365pub struct UiWebImeTraceEntryV1 {
3366 pub step_index: u32,
3367 #[serde(default, skip_serializing_if = "Option::is_none")]
3368 pub note: Option<String>,
3369
3370 #[serde(default)]
3371 pub enabled: bool,
3372 #[serde(default)]
3373 pub composing: bool,
3374 #[serde(default)]
3375 pub suppress_next_input: bool,
3376
3377 #[serde(default, skip_serializing_if = "Option::is_none")]
3378 pub textarea_has_focus: Option<bool>,
3379 #[serde(default, skip_serializing_if = "Option::is_none")]
3380 pub active_element_tag: Option<String>,
3381
3382 #[serde(default, skip_serializing_if = "Option::is_none")]
3383 pub position_mode: Option<String>,
3384 #[serde(default, skip_serializing_if = "Option::is_none")]
3385 pub mount_kind: Option<String>,
3386 #[serde(default, skip_serializing_if = "Option::is_none")]
3387 pub device_pixel_ratio: Option<f64>,
3388
3389 #[serde(default, skip_serializing_if = "Option::is_none")]
3390 pub textarea_selection_start_utf16: Option<u32>,
3391 #[serde(default, skip_serializing_if = "Option::is_none")]
3392 pub textarea_selection_end_utf16: Option<u32>,
3393
3394 #[serde(default, skip_serializing_if = "Option::is_none")]
3395 pub last_cursor_area: Option<UiRectV1>,
3396 #[serde(default, skip_serializing_if = "Option::is_none")]
3397 pub last_cursor_anchor_px: Option<(f32, f32)>,
3398
3399 #[serde(default, skip_serializing_if = "Option::is_none")]
3400 pub last_input_type: Option<String>,
3401
3402 #[serde(default, skip_serializing_if = "Option::is_none")]
3403 pub last_preedit_len: Option<u32>,
3404 #[serde(default, skip_serializing_if = "Option::is_none")]
3405 pub last_preedit_cursor_utf16: Option<(u32, u32)>,
3406 #[serde(default, skip_serializing_if = "Option::is_none")]
3407 pub last_commit_len: Option<u32>,
3408
3409 #[serde(default)]
3410 pub beforeinput_seen: u64,
3411 #[serde(default)]
3412 pub input_seen: u64,
3413 #[serde(default)]
3414 pub suppressed_input_seen: u64,
3415 #[serde(default)]
3416 pub composition_start_seen: u64,
3417 #[serde(default)]
3418 pub composition_update_seen: u64,
3419 #[serde(default)]
3420 pub composition_end_seen: u64,
3421 #[serde(default)]
3422 pub cursor_area_set_seen: u64,
3423}
3424
3425#[derive(Debug, Clone, Serialize, Deserialize)]
3426pub struct UiImeEventTraceEntryV1 {
3427 pub step_index: u32,
3428 #[serde(default, skip_serializing_if = "Option::is_none")]
3429 pub note: Option<String>,
3430 pub kind: String,
3431 #[serde(default, skip_serializing_if = "Option::is_none")]
3432 pub preedit_len: Option<u32>,
3433 #[serde(default, skip_serializing_if = "Option::is_none")]
3434 pub preedit_cursor: Option<(u32, u32)>,
3435 #[serde(default, skip_serializing_if = "Option::is_none")]
3436 pub commit_len: Option<u32>,
3437 #[serde(default, skip_serializing_if = "Option::is_none")]
3438 pub delete_surrounding: Option<(u32, u32)>,
3439}
3440
3441#[derive(Debug, Clone, Serialize, Deserialize)]
3442#[serde(rename_all = "snake_case")]
3443pub enum UiScriptStageV1 {
3444 Queued,
3445 Running,
3446 Passed,
3447 Failed,
3448}
3449
3450fn serde_default_true() -> bool {
3451 true
3452}
3453
3454fn serde_default_one_f64() -> f64 {
3455 1.0
3456}
3457
3458fn default_diag_screenshot_schema_version() -> u32 {
3459 1
3460}
3461
3462fn is_zero_u64(v: &u64) -> bool {
3463 *v == 0
3464}
3465
3466#[cfg(test)]
3467mod tests {
3468 use super::*;
3469
3470 #[test]
3471 fn devtools_app_exit_request_serializes_minimally() {
3472 let value = serde_json::to_value(DevtoolsAppExitRequestV1 {
3473 schema_version: 1,
3474 reason: None,
3475 delay_ms: None,
3476 })
3477 .unwrap();
3478 assert_eq!(value, serde_json::json!({ "schema_version": 1 }));
3479 }
3480
3481 #[test]
3482 fn predicate_runner_accessibility_activated_serializes_and_deserializes() {
3483 let value = serde_json::to_value(UiPredicateV1::RunnerAccessibilityActivated).unwrap();
3484 assert_eq!(
3485 value,
3486 serde_json::json!({ "kind": "runner_accessibility_activated" })
3487 );
3488
3489 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3490 assert!(matches!(
3491 roundtrip,
3492 UiPredicateV1::RunnerAccessibilityActivated
3493 ));
3494 }
3495
3496 #[test]
3497 fn predicate_app_snapshot_field_equals_serializes_minimally() {
3498 let value = serde_json::to_value(UiPredicateV1::AppSnapshotFieldEquals {
3499 pointer: "/shell/settings_open".to_string(),
3500 value: serde_json::json!(true),
3501 })
3502 .unwrap();
3503
3504 assert_eq!(
3505 value,
3506 serde_json::json!({
3507 "kind": "app_snapshot_field_equals",
3508 "pointer": "/shell/settings_open",
3509 "value": true,
3510 })
3511 );
3512
3513 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3514 assert!(matches!(
3515 roundtrip,
3516 UiPredicateV1::AppSnapshotFieldEquals { .. }
3517 ));
3518 }
3519
3520 #[test]
3521 fn predicate_bounds_approx_equal_serializes_and_deserializes() {
3522 let value = serde_json::to_value(UiPredicateV1::BoundsApproxEqual {
3523 a: UiSelectorV1::TestId {
3524 id: "a".to_string(),
3525 root_z_index: None,
3526 },
3527 b: UiSelectorV1::TestId {
3528 id: "b".to_string(),
3529 root_z_index: None,
3530 },
3531 eps_px: 1.0,
3532 })
3533 .unwrap();
3534
3535 assert_eq!(
3536 value,
3537 serde_json::json!({
3538 "kind": "bounds_approx_equal",
3539 "a": { "kind": "test_id", "id": "a" },
3540 "b": { "kind": "test_id", "id": "b" },
3541 "eps_px": 1.0
3542 })
3543 );
3544
3545 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3546 assert!(matches!(roundtrip, UiPredicateV1::BoundsApproxEqual { .. }));
3547 }
3548
3549 #[test]
3550 fn predicate_bounds_center_approx_equal_serializes_and_deserializes() {
3551 let value = serde_json::to_value(UiPredicateV1::BoundsCenterApproxEqual {
3552 a: UiSelectorV1::TestId {
3553 id: "a".to_string(),
3554 root_z_index: None,
3555 },
3556 b: UiSelectorV1::TestId {
3557 id: "b".to_string(),
3558 root_z_index: None,
3559 },
3560 eps_px: 1.0,
3561 })
3562 .unwrap();
3563
3564 assert_eq!(
3565 value,
3566 serde_json::json!({
3567 "kind": "bounds_center_approx_equal",
3568 "a": { "kind": "test_id", "id": "a" },
3569 "b": { "kind": "test_id", "id": "b" },
3570 "eps_px": 1.0
3571 })
3572 );
3573
3574 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3575 assert!(matches!(
3576 roundtrip,
3577 UiPredicateV1::BoundsCenterApproxEqual { .. }
3578 ));
3579 }
3580
3581 #[test]
3582 fn predicate_exists_under_serializes_minimally() {
3583 let value = serde_json::to_value(UiPredicateV1::ExistsUnder {
3584 scope: UiSelectorV1::TestId {
3585 id: "scope".to_string(),
3586 root_z_index: None,
3587 },
3588 target: UiSelectorV1::TestId {
3589 id: "target".to_string(),
3590 root_z_index: None,
3591 },
3592 })
3593 .unwrap();
3594
3595 assert_eq!(
3596 value,
3597 serde_json::json!({
3598 "kind": "exists_under",
3599 "scope": { "kind": "test_id", "id": "scope" },
3600 "target": { "kind": "test_id", "id": "target" },
3601 })
3602 );
3603
3604 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3605 assert!(matches!(roundtrip, UiPredicateV1::ExistsUnder { .. }));
3606 }
3607
3608 #[test]
3609 fn predicate_value_equals_serializes_minimally() {
3610 let value = serde_json::to_value(UiPredicateV1::ValueEquals {
3611 target: UiSelectorV1::TestId {
3612 id: "name".to_string(),
3613 root_z_index: None,
3614 },
3615 text: "Alice".to_string(),
3616 })
3617 .unwrap();
3618
3619 assert_eq!(
3620 value,
3621 serde_json::json!({
3622 "kind": "value_equals",
3623 "target": { "kind": "test_id", "id": "name" },
3624 "text": "Alice",
3625 })
3626 );
3627
3628 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3629 assert!(matches!(roundtrip, UiPredicateV1::ValueEquals { .. }));
3630 }
3631
3632 #[test]
3633 fn predicate_focused_descendant_is_serializes_minimally() {
3634 let value = serde_json::to_value(UiPredicateV1::FocusedDescendantIs {
3635 scope: UiSelectorV1::TestId {
3636 id: "dialog".to_string(),
3637 root_z_index: None,
3638 },
3639 target: UiSelectorV1::TestId {
3640 id: "close".to_string(),
3641 root_z_index: None,
3642 },
3643 })
3644 .unwrap();
3645
3646 assert_eq!(
3647 value,
3648 serde_json::json!({
3649 "kind": "focused_descendant_is",
3650 "scope": { "kind": "test_id", "id": "dialog" },
3651 "target": { "kind": "test_id", "id": "close" },
3652 })
3653 );
3654
3655 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3656 assert!(matches!(
3657 roundtrip,
3658 UiPredicateV1::FocusedDescendantIs { .. }
3659 ));
3660 }
3661
3662 #[test]
3663 fn predicate_dock_tab_strip_scroll_predicates_serialize_and_deserialize() {
3664 let value =
3665 serde_json::to_value(UiPredicateV1::DockTabStripActiveScrollPxGe { px: 12.0 }).unwrap();
3666 assert_eq!(
3667 value,
3668 serde_json::json!({
3669 "kind": "dock_tab_strip_active_scroll_px_ge",
3670 "px": 12.0
3671 })
3672 );
3673 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3674 assert!(matches!(
3675 roundtrip,
3676 UiPredicateV1::DockTabStripActiveScrollPxGe { .. }
3677 ));
3678
3679 let value =
3680 serde_json::to_value(UiPredicateV1::DockTabStripActiveScrollPxLe { px: 0.0 }).unwrap();
3681 assert_eq!(
3682 value,
3683 serde_json::json!({
3684 "kind": "dock_tab_strip_active_scroll_px_le",
3685 "px": 0.0
3686 })
3687 );
3688 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3689 assert!(matches!(
3690 roundtrip,
3691 UiPredicateV1::DockTabStripActiveScrollPxLe { .. }
3692 ));
3693 }
3694
3695 #[test]
3696 fn predicate_workspace_tab_strip_scroll_predicates_serialize_and_deserialize() {
3697 let value = serde_json::to_value(UiPredicateV1::WorkspaceTabStripActiveScrollPxGe {
3698 px: 12.0,
3699 pane_id: None,
3700 })
3701 .unwrap();
3702 assert_eq!(
3703 value,
3704 serde_json::json!({
3705 "kind": "workspace_tab_strip_active_scroll_px_ge",
3706 "px": 12.0
3707 })
3708 );
3709 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3710 assert!(matches!(
3711 roundtrip,
3712 UiPredicateV1::WorkspaceTabStripActiveScrollPxGe { .. }
3713 ));
3714
3715 let value = serde_json::to_value(UiPredicateV1::WorkspaceTabStripActiveScrollPxLe {
3716 px: 0.0,
3717 pane_id: Some("pane-a".to_string()),
3718 })
3719 .unwrap();
3720 assert_eq!(
3721 value,
3722 serde_json::json!({
3723 "kind": "workspace_tab_strip_active_scroll_px_le",
3724 "px": 0.0,
3725 "pane_id": "pane-a",
3726 })
3727 );
3728 let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
3729 assert!(matches!(
3730 roundtrip,
3731 UiPredicateV1::WorkspaceTabStripActiveScrollPxLe { .. }
3732 ));
3733 }
3734
3735 #[test]
3736 fn diag_screenshot_request_round_trips_and_defaults_scale_factor() {
3737 let json = serde_json::json!({
3738 "schema_version": 1,
3739 "out_dir": "target/fret-diag",
3740 "bundle_dir_name": "1700000-bundle",
3741 "request_id": "req-1",
3742 "windows": [{
3743 "window": 123,
3744 "tick_id": 1,
3745 "frame_id": 2
3746 }]
3747 });
3748 let parsed: DiagScreenshotRequestV1 = serde_json::from_value(json).unwrap();
3749 assert_eq!(parsed.schema_version, 1);
3750 assert_eq!(parsed.windows.len(), 1);
3751 assert_eq!(parsed.windows[0].scale_factor, 1.0);
3752
3753 let value = serde_json::to_value(parsed).unwrap();
3754 assert_eq!(value["schema_version"].as_u64(), Some(1));
3755 }
3756
3757 #[test]
3758 fn diag_screenshot_result_defaults_schema_version_to_1() {
3759 let value = serde_json::json!({
3760 "updated_unix_ms": 1700000,
3761 "completed": [],
3762 });
3763 let parsed: DiagScreenshotResultFileV1 = serde_json::from_value(value).unwrap();
3764 assert_eq!(parsed.schema_version, 1);
3765 assert_eq!(DiagScreenshotResultFileV1::default().schema_version, 1);
3766 }
3767
3768 #[test]
3769 fn click_step_pointer_kind_round_trips_and_omits_none() {
3770 let step = UiActionStepV2::Click {
3771 window: None,
3772 pointer_kind: None,
3773 target: UiSelectorV1::TestId {
3774 id: "a".to_string(),
3775 root_z_index: None,
3776 },
3777 button: UiMouseButtonV1::Left,
3778 click_count: 1,
3779 modifiers: None,
3780 };
3781 let value = serde_json::to_value(step.clone()).unwrap();
3782 assert_eq!(
3783 value,
3784 serde_json::json!({
3785 "type": "click",
3786 "target": {"kind":"test_id","id":"a"},
3787 "button": "left"
3788 })
3789 );
3790
3791 let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
3792 "type": "click",
3793 "pointer_kind": "touch",
3794 "target": {"kind":"test_id","id":"a"},
3795 "button": "left",
3796 "click_count": 1
3797 }))
3798 .unwrap();
3799 assert!(matches!(
3800 parsed,
3801 UiActionStepV2::Click {
3802 pointer_kind: Some(UiPointerKindV1::Touch),
3803 ..
3804 }
3805 ));
3806 }
3807
3808 #[test]
3809 fn tap_step_pointer_kind_round_trips_and_omits_none() {
3810 let step = UiActionStepV2::Tap {
3811 window: None,
3812 pointer_kind: None,
3813 target: UiSelectorV1::TestId {
3814 id: "a".to_string(),
3815 root_z_index: None,
3816 },
3817 modifiers: None,
3818 };
3819 let value = serde_json::to_value(step.clone()).unwrap();
3820 assert_eq!(
3821 value,
3822 serde_json::json!({
3823 "type": "tap",
3824 "target": {"kind":"test_id","id":"a"}
3825 })
3826 );
3827
3828 let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
3829 "type": "tap",
3830 "pointer_kind": "pen",
3831 "target": {"kind":"test_id","id":"a"}
3832 }))
3833 .unwrap();
3834 assert!(matches!(
3835 parsed,
3836 UiActionStepV2::Tap {
3837 pointer_kind: Some(UiPointerKindV1::Pen),
3838 ..
3839 }
3840 ));
3841 }
3842
3843 #[test]
3844 fn long_press_step_round_trips_and_omits_defaults() {
3845 let step = UiActionStepV2::LongPress {
3846 window: None,
3847 pointer_kind: None,
3848 target: UiSelectorV1::TestId {
3849 id: "a".to_string(),
3850 root_z_index: None,
3851 },
3852 duration_ms: default_long_press_duration_ms(),
3853 modifiers: None,
3854 };
3855 let value = serde_json::to_value(step.clone()).unwrap();
3856 assert_eq!(
3857 value,
3858 serde_json::json!({
3859 "type": "long_press",
3860 "target": {"kind":"test_id","id":"a"}
3861 })
3862 );
3863
3864 let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
3865 "type": "long_press",
3866 "pointer_kind": "pen",
3867 "target": {"kind":"test_id","id":"a"},
3868 "duration_ms": 125
3869 }))
3870 .unwrap();
3871 assert!(matches!(
3872 parsed,
3873 UiActionStepV2::LongPress {
3874 pointer_kind: Some(UiPointerKindV1::Pen),
3875 duration_ms: 125,
3876 ..
3877 }
3878 ));
3879 }
3880
3881 #[test]
3882 fn swipe_step_round_trips_and_omits_defaults() {
3883 let step = UiActionStepV2::Swipe {
3884 window: None,
3885 pointer_kind: None,
3886 target: UiSelectorV1::TestId {
3887 id: "a".to_string(),
3888 root_z_index: None,
3889 },
3890 delta_x: 12.0,
3891 delta_y: -8.0,
3892 steps: default_drag_steps(),
3893 modifiers: None,
3894 };
3895 let value = serde_json::to_value(step.clone()).unwrap();
3896 assert_eq!(
3897 value,
3898 serde_json::json!({
3899 "type": "swipe",
3900 "target": {"kind":"test_id","id":"a"},
3901 "delta_x": 12.0,
3902 "delta_y": -8.0
3903 })
3904 );
3905
3906 let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
3907 "type": "swipe",
3908 "pointer_kind": "pen",
3909 "target": {"kind":"test_id","id":"a"},
3910 "delta_x": 1.0,
3911 "delta_y": 2.0,
3912 "steps": 3
3913 }))
3914 .unwrap();
3915 assert!(matches!(
3916 parsed,
3917 UiActionStepV2::Swipe {
3918 pointer_kind: Some(UiPointerKindV1::Pen),
3919 steps: 3,
3920 ..
3921 }
3922 ));
3923 }
3924
3925 #[test]
3926 fn step_activate_deserializes_with_defaults() {
3927 let value = serde_json::json!({
3928 "type": "activate",
3929 "target": { "kind": "test_id", "id": "trigger" }
3930 });
3931
3932 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
3933 match step {
3934 UiActionStepV2::Activate { window, target } => {
3935 assert!(window.is_none());
3936 assert!(matches!(target, UiSelectorV1::TestId { .. }));
3937 }
3938 _ => panic!("expected activate"),
3939 }
3940 }
3941
3942 #[test]
3943 fn step_focus_deserializes_with_defaults() {
3944 let value = serde_json::json!({
3945 "type": "focus",
3946 "target": { "kind": "test_id", "id": "trigger" }
3947 });
3948
3949 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
3950 match step {
3951 UiActionStepV2::Focus { window, target } => {
3952 assert!(window.is_none());
3953 assert!(matches!(target, UiSelectorV1::TestId { .. }));
3954 }
3955 _ => panic!("expected focus"),
3956 }
3957 }
3958
3959 #[test]
3960 fn hit_test_explain_request_round_trips() {
3961 let value = serde_json::json!({
3962 "schema_version": 1,
3963 "window": 7,
3964 "target": { "kind": "test_id", "id": "trigger" }
3965 });
3966 let req: UiHitTestExplainV1 = serde_json::from_value(value.clone()).unwrap();
3967 assert_eq!(req.window, 7);
3968 assert_eq!(serde_json::to_value(req).unwrap(), value);
3969 }
3970
3971 #[test]
3972 fn step_paste_text_into_deserializes_with_defaults() {
3973 let value = serde_json::json!({
3974 "type": "paste_text_into",
3975 "target": { "kind": "test_id", "id": "field" },
3976 "text": "Hello"
3977 });
3978
3979 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
3980 match step {
3981 UiActionStepV2::PasteTextInto {
3982 window,
3983 pointer_kind,
3984 target,
3985 text,
3986 clear_before_paste,
3987 timeout_frames,
3988 } => {
3989 assert!(window.is_none());
3990 assert!(pointer_kind.is_none());
3991 assert!(matches!(target, UiSelectorV1::TestId { .. }));
3992 assert_eq!(text, "Hello");
3993 assert!(!clear_before_paste);
3994 assert_eq!(timeout_frames, default_action_timeout_frames());
3995 }
3996 _ => panic!("expected paste_text_into"),
3997 }
3998 }
3999
4000 #[test]
4001 fn step_set_text_value_deserializes_with_defaults() {
4002 let value = serde_json::json!({
4003 "type": "set_text_value",
4004 "target": { "kind": "test_id", "id": "field" },
4005 "text": "#112233"
4006 });
4007
4008 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4009 match step {
4010 UiActionStepV2::SetTextValue {
4011 window,
4012 target,
4013 text,
4014 timeout_frames,
4015 } => {
4016 assert!(window.is_none());
4017 assert!(matches!(target, UiSelectorV1::TestId { .. }));
4018 assert_eq!(text, "#112233");
4019 assert_eq!(timeout_frames, default_action_timeout_frames());
4020 }
4021 _ => panic!("expected set_text_value"),
4022 }
4023 }
4024
4025 #[test]
4026 fn step_wait_clipboard_write_result_deserializes_with_defaults() {
4027 let value = serde_json::json!({
4028 "type": "wait_clipboard_write_result",
4029 "outcome": "success"
4030 });
4031
4032 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4033 match step {
4034 UiActionStepV2::WaitClipboardWriteResult {
4035 outcome,
4036 error_kind,
4037 message_contains,
4038 timeout_frames,
4039 } => {
4040 assert_eq!(outcome, UiClipboardWriteResultV1::Success);
4041 assert!(error_kind.is_none());
4042 assert!(message_contains.is_none());
4043 assert_eq!(timeout_frames, default_action_timeout_frames());
4044 }
4045 _ => panic!("expected wait_clipboard_write_result"),
4046 }
4047 }
4048
4049 #[test]
4050 fn step_assert_clipboard_write_result_deserializes_with_failure_details() {
4051 let value = serde_json::json!({
4052 "type": "assert_clipboard_write_result",
4053 "outcome": "failure",
4054 "error_kind": "unavailable",
4055 "message_contains": "forced clipboard unavailable",
4056 "timeout_frames": 90
4057 });
4058
4059 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4060 match step {
4061 UiActionStepV2::AssertClipboardWriteResult {
4062 outcome,
4063 error_kind,
4064 message_contains,
4065 timeout_frames,
4066 } => {
4067 assert_eq!(outcome, UiClipboardWriteResultV1::Failure);
4068 assert_eq!(error_kind, Some(UiClipboardAccessErrorKindV1::Unavailable));
4069 assert_eq!(
4070 message_contains.as_deref(),
4071 Some("forced clipboard unavailable")
4072 );
4073 assert_eq!(timeout_frames, 90);
4074 }
4075 _ => panic!("expected assert_clipboard_write_result"),
4076 }
4077 }
4078
4079 #[test]
4080 fn step_inspect_help_lock_best_match_and_copy_selector_deserializes_with_defaults() {
4081 let value = serde_json::json!({
4082 "type": "inspect_help_lock_best_match_and_copy_selector",
4083 "query": "ui-gallery-nav-search"
4084 });
4085
4086 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4087 match step {
4088 UiActionStepV2::InspectHelpLockBestMatchAndCopySelector {
4089 window,
4090 query,
4091 timeout_frames,
4092 } => {
4093 assert!(window.is_none());
4094 assert_eq!(query, "ui-gallery-nav-search");
4095 assert_eq!(timeout_frames, default_action_timeout_frames());
4096 }
4097 _ => panic!("expected inspect_help_lock_best_match_and_copy_selector"),
4098 }
4099 }
4100
4101 #[test]
4102 fn step_inspect_help_tree_lock_best_match_and_copy_selector_deserializes_with_defaults() {
4103 let value = serde_json::json!({
4104 "type": "inspect_help_tree_lock_best_match_and_copy_selector",
4105 "query": "ui-gallery-nav-search"
4106 });
4107
4108 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4109 match step {
4110 UiActionStepV2::InspectHelpTreeLockBestMatchAndCopySelector {
4111 window,
4112 query,
4113 timeout_frames,
4114 } => {
4115 assert!(window.is_none());
4116 assert_eq!(query, "ui-gallery-nav-search");
4117 assert_eq!(timeout_frames, default_action_timeout_frames());
4118 }
4119 _ => panic!("expected inspect_help_tree_lock_best_match_and_copy_selector"),
4120 }
4121 }
4122
4123 #[test]
4124 fn step_wait_until_deserializes_with_default_timeout_frames() {
4125 let value = serde_json::json!({
4126 "type": "wait_until",
4127 "predicate": {
4128 "kind": "exists",
4129 "target": { "kind": "test_id", "id": "ui-gallery-nav-search" }
4130 }
4131 });
4132
4133 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4134 match step {
4135 UiActionStepV2::WaitUntil {
4136 window,
4137 predicate,
4138 timeout_frames,
4139 timeout_ms,
4140 } => {
4141 assert!(window.is_none());
4142 assert!(
4143 matches!(predicate, UiPredicateV1::Exists { .. }),
4144 "expected exists predicate"
4145 );
4146 assert_eq!(timeout_frames, default_action_timeout_frames());
4147 assert!(timeout_ms.is_none());
4148 }
4149 _ => panic!("expected wait_until"),
4150 }
4151 }
4152
4153 #[test]
4154 fn step_wait_semantics_scroll_stable_deserializes_with_defaults() {
4155 let value = serde_json::json!({
4156 "type": "wait_semantics_scroll_stable",
4157 "target": { "kind": "test_id", "id": "ui-gallery-content-viewport" },
4158 "field": "y_max"
4159 });
4160
4161 let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
4162 match step {
4163 UiActionStepV2::WaitSemanticsScrollStable {
4164 window,
4165 target,
4166 field,
4167 stable_frames,
4168 max_delta,
4169 timeout_frames,
4170 } => {
4171 assert!(window.is_none());
4172 assert!(matches!(target, UiSelectorV1::TestId { .. }));
4173 assert_eq!(field, UiSemanticsScrollFieldV1::YMax);
4174 assert_eq!(stable_frames, default_semantics_scroll_stable_frames());
4175 assert_eq!(max_delta, default_semantics_scroll_stable_max_delta());
4176 assert_eq!(timeout_frames, default_action_timeout_frames());
4177 }
4178 _ => panic!("expected wait_semantics_scroll_stable"),
4179 }
4180 }
4181}