use serde::{Deserialize, Serialize};
pub mod builder;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagTransportMessageV1 {
pub schema_version: u32,
pub r#type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_id: Option<u64>,
#[serde(default)]
pub payload: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsHelloV1 {
pub client_kind: String,
pub client_version: String,
#[serde(default)]
pub capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsHelloAckV1 {
pub server_version: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub server_capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsSessionDescriptorV1 {
pub session_id: String,
pub client_kind: String,
pub client_version: String,
#[serde(default)]
pub capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsSessionListV1 {
pub sessions: Vec<DevtoolsSessionDescriptorV1>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsSessionAddedV1 {
pub session: DevtoolsSessionDescriptorV1,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsSessionRemovedV1 {
pub session_id: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiScriptMetaV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_capabilities: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub target_hints: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiImeEventV1 {
Enabled,
Disabled,
Commit {
text: String,
},
Preedit {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
cursor_bytes: Option<(u32, u32)>,
},
DeleteSurrounding {
before_bytes: u32,
after_bytes: u32,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiActionScriptV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub meta: Option<UiScriptMetaV1>,
pub steps: Vec<UiActionStepV1>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum UiActionStepV1 {
Click {
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(
default = "default_click_count",
skip_serializing_if = "is_default_click_count"
)]
click_count: u8,
},
ResetDiagnostics,
MovePointer {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
},
DragPointer {
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
delta_x: f32,
delta_y: f32,
#[serde(default = "default_drag_steps")]
steps: u32,
},
Wheel {
target: UiSelectorV1,
#[serde(default)]
delta_x: f32,
#[serde(default)]
delta_y: f32,
},
PressKey {
key: String,
#[serde(default)]
modifiers: UiKeyModifiersV1,
#[serde(default)]
repeat: bool,
},
TypeText {
text: String,
},
WaitFrames {
n: u32,
},
WaitUntil {
predicate: UiPredicateV1,
timeout_frames: u32,
},
Assert {
predicate: UiPredicateV1,
},
CaptureBundle {
label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_snapshots: Option<u32>,
},
CaptureScreenshot {
label: Option<String>,
#[serde(default = "default_capture_screenshot_timeout_frames")]
timeout_frames: u32,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiActionScriptV2 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub meta: Option<UiScriptMetaV1>,
pub steps: Vec<UiActionStepV2>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilesystemCapabilitiesHintsV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allow_script_schema_v1: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub write_bundle_schema2: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilesystemCapabilitiesV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub capabilities: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runner_kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runner_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hints: Option<FilesystemCapabilitiesHintsV1>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiDiagnosticsConfigPathsV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ready_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exit_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshot_request_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshot_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshot_result_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshot_result_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_result_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_result_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_result_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_result_trigger_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inspect_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inspect_trigger_path: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiDiagnosticsConfigFileV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub out_dir: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub paths: Option<UiDiagnosticsConfigPathsV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allow_script_schema_v1: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_keepalive: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_auto_dump: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_auto_dump: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_events: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_snapshots: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_dump_max_snapshots: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub capture_semantics: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_semantics_nodes: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub semantics_test_ids_only: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshots_enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshot_on_dump: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub write_bundle_json: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub write_bundle_schema2: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub redact_text: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_debug_string_bytes: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_gating_trace_entries: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub isolate_external_pointer_input_while_script_running: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub isolate_external_keyboard_input_while_script_running: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frame_clock_fixed_delta_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub devtools_embed_bundle: Option<bool>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiPaddingInsetsV1 {
pub left_px: f32,
pub top_px: f32,
pub right_px: f32,
pub bottom_px: f32,
}
impl UiPaddingInsetsV1 {
pub fn uniform(padding_px: f32) -> Self {
let p = padding_px.max(0.0);
Self {
left_px: p,
top_px: p,
right_px: p,
bottom_px: p,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiWindowTargetV1 {
Current,
FirstSeen,
FirstSeenOther,
LastSeen,
LastSeenOther,
WindowFfi { window: u64 },
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiInsetsOverrideV1 {
#[default]
NoChange,
Clear,
Set {
insets_px: UiPaddingInsetsV1,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiIncomingOpenInjectItemV1 {
FileUtf8 {
name: String,
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
media_type: Option<String>,
},
Text {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
media_type: Option<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiClipboardWriteResultV1 {
Success,
Failure,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiClipboardAccessErrorKindV1 {
Unavailable,
PermissionDenied,
UserActivationRequired,
Unsupported,
BackendError,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum UiActionStepV2 {
Click {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(
default = "default_click_count",
skip_serializing_if = "is_default_click_count"
)]
click_count: u8,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
Tap {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
LongPress {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(
default = "default_long_press_duration_ms",
skip_serializing_if = "is_default_long_press_duration_ms"
)]
duration_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
Swipe {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
delta_x: f32,
delta_y: f32,
#[serde(
default = "default_drag_steps",
skip_serializing_if = "is_default_drag_steps"
)]
steps: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
Pinch {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
delta: f32,
#[serde(
default = "default_drag_steps",
skip_serializing_if = "is_default_drag_steps"
)]
steps: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
ResetDiagnostics,
SetBaseRef {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
},
ClearBaseRef,
Activate {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
},
Focus {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
},
MovePointer {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
},
PointerDown {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
},
DragPointer {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(default = "default_true")]
clamp_to_window_bounds: bool,
delta_x: f32,
delta_y: f32,
#[serde(default = "default_drag_steps")]
steps: u32,
},
PointerMove {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
delta_x: f32,
delta_y: f32,
#[serde(default = "default_drag_steps")]
steps: u32,
},
PointerUp {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
button: Option<UiMouseButtonV1>,
},
PointerCancel {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
},
MovePointerSweep {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
delta_x: f32,
delta_y: f32,
#[serde(default = "default_drag_steps")]
steps: u32,
#[serde(default = "default_move_frames_per_step")]
frames_per_step: u32,
},
Wheel {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
delta_x: f32,
#[serde(default)]
delta_y: f32,
},
WheelBurst {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
delta_x: f32,
#[serde(default)]
delta_y: f32,
#[serde(
default = "default_wheel_burst_count",
skip_serializing_if = "is_default_wheel_burst_count"
)]
count: u32,
},
PressKey {
key: String,
#[serde(default)]
modifiers: UiKeyModifiersV1,
#[serde(default)]
repeat: bool,
},
PressShortcut {
shortcut: String,
#[serde(default)]
repeat: bool,
},
TypeText {
text: String,
},
Ime {
event: UiImeEventV1,
},
WaitFrames {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
n: u32,
},
WaitMs {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
n_ms: u32,
},
WaitUntil {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
predicate: UiPredicateV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_ms: Option<u32>,
},
WaitShortcutRoutingTrace {
query: UiShortcutRoutingTraceQueryV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_ms: Option<u32>,
},
WaitCommandDispatchTrace {
query: UiCommandDispatchTraceQueryV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_ms: Option<u32>,
},
WaitOverlayPlacementTrace {
query: UiOverlayPlacementTraceQueryV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_ms: Option<u32>,
},
Assert {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
predicate: UiPredicateV1,
},
CaptureBundle {
label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_snapshots: Option<u32>,
},
CaptureScreenshot {
label: Option<String>,
#[serde(default = "default_capture_screenshot_timeout_frames")]
timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_ms: Option<u32>,
},
CaptureLayoutSidecar {
#[serde(default, skip_serializing_if = "Option::is_none")]
label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_label_filter: Option<String>,
},
ClickStable {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(
default = "default_click_count",
skip_serializing_if = "is_default_click_count"
)]
click_count: u8,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
#[serde(default = "default_click_stable_frames")]
stable_frames: u32,
#[serde(default = "default_click_stable_max_move_px")]
max_move_px: f32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
ClickSelectableTextSpanStable {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
tag: String,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(
default = "default_click_count",
skip_serializing_if = "is_default_click_count"
)]
click_count: u8,
#[serde(default, skip_serializing_if = "Option::is_none")]
modifiers: Option<UiKeyModifiersV1>,
#[serde(default = "default_click_stable_frames")]
stable_frames: u32,
#[serde(default = "default_click_stable_max_move_px")]
max_move_px: f32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
WaitBoundsStable {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
#[serde(default = "default_bounds_stable_frames")]
stable_frames: u32,
#[serde(default = "default_bounds_stable_max_move_px")]
max_move_px: f32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
WaitSemanticsScrollStable {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
field: UiSemanticsScrollFieldV1,
#[serde(default = "default_semantics_scroll_stable_frames")]
stable_frames: u32,
#[serde(default = "default_semantics_scroll_stable_max_delta")]
max_delta: f64,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
EnsureVisible {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
#[serde(default)]
within_window: bool,
#[serde(default)]
padding_px: f32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
ScrollIntoView {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
container: UiSelectorV1,
target: UiSelectorV1,
#[serde(default)]
delta_x: f32,
#[serde(default = "default_scroll_delta_y")]
delta_y: f32,
#[serde(default)]
require_fully_within_container: bool,
#[serde(default)]
require_fully_within_window: bool,
#[serde(default)]
padding_px: f32,
#[serde(default)]
padding_insets_px: Option<UiPaddingInsetsV1>,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
TypeTextInto {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
text: String,
#[serde(default)]
clear_before_type: bool,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
SetTextValue {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
target: UiSelectorV1,
text: String,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
PasteTextInto {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
text: String,
#[serde(default)]
clear_before_paste: bool,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
MenuSelect {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
menu: UiSelectorV1,
item: UiSelectorV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
MenuSelectPath {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
path: Vec<UiSelectorV1>,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
DragTo {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
from: UiSelectorV1,
to: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(default = "default_drag_steps")]
steps: u32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
SetSliderValue {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
value: f32,
#[serde(default = "default_slider_min")]
min: f32,
#[serde(default = "default_slider_max")]
max: f32,
#[serde(default = "default_slider_epsilon")]
epsilon: f32,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
#[serde(default = "default_drag_steps")]
drag_steps: u32,
},
SetWindowInnerSize {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
width_px: f32,
height_px: f32,
},
SetWindowStyle {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
style: UiWindowStylePatchV1,
},
SetWindowInsets {
#[serde(default)]
safe_area_insets: UiInsetsOverrideV1,
#[serde(default)]
occlusion_insets: UiInsetsOverrideV1,
},
SetClipboardForceUnavailable {
enabled: bool,
},
SetClipboardText {
text: String,
},
WaitClipboardWriteResult {
outcome: UiClipboardWriteResultV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
error_kind: Option<UiClipboardAccessErrorKindV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
message_contains: Option<String>,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
AssertClipboardWriteResult {
outcome: UiClipboardWriteResultV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
error_kind: Option<UiClipboardAccessErrorKindV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
message_contains: Option<String>,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
AssertClipboardText {
text: String,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
InspectHelpLockBestMatchAndCopySelector {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
query: String,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
InspectHelpTreeLockBestMatchAndCopySelector {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
query: String,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
InjectIncomingOpen {
items: Vec<UiIncomingOpenInjectItemV1>,
},
SetWindowOuterPosition {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
x_px: f32,
y_px: f32,
},
SetCursorScreenPos {
x_px: f32,
y_px: f32,
},
SetCursorInWindow {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
x_px: f32,
y_px: f32,
},
SetCursorInWindowLogical {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
x_px: f32,
y_px: f32,
},
SetMouseButtons {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
left: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
right: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
middle: Option<bool>,
},
RaiseWindow {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
},
DragPointerUntil {
#[serde(default, skip_serializing_if = "Option::is_none")]
window: Option<UiWindowTargetV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pointer_kind: Option<UiPointerKindV1>,
target: UiSelectorV1,
#[serde(default)]
button: UiMouseButtonV1,
#[serde(default = "default_true")]
release_on_success: bool,
delta_x: f32,
delta_y: f32,
#[serde(default = "default_drag_steps")]
steps: u32,
predicate: UiPredicateV1,
#[serde(default = "default_action_timeout_frames")]
timeout_frames: u32,
},
}
impl From<UiActionStepV1> for UiActionStepV2 {
fn from(value: UiActionStepV1) -> Self {
match value {
UiActionStepV1::Click {
target,
button,
click_count,
} => Self::Click {
window: None,
pointer_kind: None,
target,
button,
click_count,
modifiers: None,
},
UiActionStepV1::ResetDiagnostics => Self::ResetDiagnostics,
UiActionStepV1::MovePointer { window, target } => Self::MovePointer {
window,
pointer_kind: None,
target,
},
UiActionStepV1::DragPointer {
target,
button,
delta_x,
delta_y,
steps,
} => Self::DragPointer {
window: None,
pointer_kind: None,
target,
button,
clamp_to_window_bounds: true,
delta_x,
delta_y,
steps,
},
UiActionStepV1::Wheel {
target,
delta_x,
delta_y,
} => Self::Wheel {
window: None,
pointer_kind: None,
target,
delta_x,
delta_y,
},
UiActionStepV1::PressKey {
key,
modifiers,
repeat,
} => Self::PressKey {
key,
modifiers,
repeat,
},
UiActionStepV1::TypeText { text } => Self::TypeText { text },
UiActionStepV1::WaitFrames { n } => Self::WaitFrames { window: None, n },
UiActionStepV1::WaitUntil {
predicate,
timeout_frames,
} => Self::WaitUntil {
window: None,
predicate,
timeout_frames,
timeout_ms: None,
},
UiActionStepV1::Assert { predicate } => Self::Assert {
window: None,
predicate,
},
UiActionStepV1::CaptureBundle {
label,
max_snapshots,
} => Self::CaptureBundle {
label,
max_snapshots,
},
UiActionStepV1::CaptureScreenshot {
label,
timeout_frames,
} => Self::CaptureScreenshot {
label,
timeout_frames,
timeout_ms: None,
},
}
}
}
fn default_drag_steps() -> u32 {
8
}
fn is_default_drag_steps(v: &u32) -> bool {
*v == default_drag_steps()
}
fn default_wheel_burst_count() -> u32 {
8
}
fn is_default_wheel_burst_count(v: &u32) -> bool {
*v == default_wheel_burst_count()
}
fn default_move_frames_per_step() -> u32 {
1
}
fn default_click_count() -> u8 {
1
}
fn is_default_click_count(v: &u8) -> bool {
*v == 1
}
fn default_long_press_duration_ms() -> u64 {
500
}
fn is_default_long_press_duration_ms(v: &u64) -> bool {
*v == 500
}
fn default_click_stable_frames() -> u32 {
2
}
fn default_click_stable_max_move_px() -> f32 {
1.0
}
fn default_bounds_stable_frames() -> u32 {
2
}
fn default_bounds_stable_max_move_px() -> f32 {
1.0
}
fn default_semantics_scroll_stable_frames() -> u32 {
2
}
fn default_semantics_scroll_stable_max_delta() -> f64 {
1.0
}
fn default_capture_screenshot_timeout_frames() -> u32 {
300
}
fn default_action_timeout_frames() -> u32 {
180
}
fn default_true() -> bool {
true
}
fn default_scroll_delta_y() -> f32 {
-120.0
}
fn default_slider_min() -> f32 {
0.0
}
fn default_slider_max() -> f32 {
100.0
}
fn default_slider_epsilon() -> f32 {
0.5
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiMouseButtonV1 {
#[default]
Left,
Right,
Middle,
}
impl UiMouseButtonV1 {
pub fn from_button(button: fret_core::MouseButton) -> Self {
match button {
fret_core::MouseButton::Left => Self::Left,
fret_core::MouseButton::Right => Self::Right,
fret_core::MouseButton::Middle => Self::Middle,
fret_core::MouseButton::Back
| fret_core::MouseButton::Forward
| fret_core::MouseButton::Other(_) => Self::Left,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiPointerKindV1 {
#[default]
Mouse,
Touch,
Pen,
}
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
pub struct UiKeyModifiersV1 {
#[serde(default)]
pub shift: bool,
#[serde(default)]
pub ctrl: bool,
#[serde(default)]
pub alt: bool,
#[serde(default)]
pub meta: bool,
}
impl UiKeyModifiersV1 {
pub fn from_modifiers(modifiers: fret_core::Modifiers) -> Self {
Self {
shift: modifiers.shift,
ctrl: modifiers.ctrl,
alt: modifiers.alt,
meta: modifiers.meta,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiWindowDecorationsRequestV1 {
System,
None,
Server,
Client,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiTaskbarVisibilityV1 {
Show,
Hide,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiActivationPolicyV1 {
Activates,
NonActivating,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiWindowZLevelV1 {
Normal,
AlwaysOnTop,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiWindowHitTestRequestV1 {
Normal,
PassthroughAll,
PassthroughRegions,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiWindowAppearanceV1 {
Opaque,
CompositedNoBackdrop,
CompositedBackdrop,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiWindowBackgroundMaterialRequestV1 {
None,
SystemDefault,
Mica,
Acrylic,
Vibrancy,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiWindowStyleMatchV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorations: Option<UiWindowDecorationsRequestV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resizable: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transparent: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub visual_transparent: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub appearance: Option<UiWindowAppearanceV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub taskbar: Option<UiTaskbarVisibilityV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub activation: Option<UiActivationPolicyV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub z_level: Option<UiWindowZLevelV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_test: Option<UiWindowHitTestRequestV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_test_regions_fingerprint64: Option<u64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiWindowStylePatchV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub taskbar: Option<UiTaskbarVisibilityV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub activation: Option<UiActivationPolicyV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub z_level: Option<UiWindowZLevelV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorations: Option<UiWindowDecorationsRequestV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resizable: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transparent: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub background_material: Option<UiWindowBackgroundMaterialRequestV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_test: Option<UiWindowHitTestPatchV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub opacity_alpha_u8: Option<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiWindowHitTestPatchV1 {
Normal,
PassthroughAll,
PassthroughRegions {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
regions: Vec<UiWindowHitTestRegionV1>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiWindowHitTestRegionV1 {
Rect {
x: f32,
y: f32,
width: f32,
height: f32,
},
RRect {
x: f32,
y: f32,
width: f32,
height: f32,
radius: f32,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiPredicateV1 {
Exists {
target: UiSelectorV1,
},
NotExists {
target: UiSelectorV1,
},
ExistsUnder {
scope: UiSelectorV1,
target: UiSelectorV1,
},
NotExistsUnder {
scope: UiSelectorV1,
target: UiSelectorV1,
},
FocusedDescendantIs {
scope: UiSelectorV1,
target: UiSelectorV1,
},
FocusIs {
target: UiSelectorV1,
},
RoleIs {
target: UiSelectorV1,
role: String,
},
LabelContains {
target: UiSelectorV1,
text: String,
},
LabelLenIs {
target: UiSelectorV1,
len_bytes: u32,
},
LabelLenGe {
target: UiSelectorV1,
min_len_bytes: u32,
},
ValueContains {
target: UiSelectorV1,
text: String,
},
ValueEquals {
target: UiSelectorV1,
text: String,
},
ValueLenIs {
target: UiSelectorV1,
len_bytes: u32,
},
ValueLenGe {
target: UiSelectorV1,
min_len_bytes: u32,
},
PosInSetIs {
target: UiSelectorV1,
pos_in_set: u32,
},
SetSizeIs {
target: UiSelectorV1,
set_size: u32,
},
CheckedIs {
target: UiSelectorV1,
checked: bool,
},
SelectedIs {
target: UiSelectorV1,
selected: bool,
},
SemanticsNumericApproxEq {
target: UiSelectorV1,
field: UiSemanticsNumericFieldV1,
value: f64,
#[serde(default)]
eps: f64,
},
SemanticsScrollIsFinite {
target: UiSelectorV1,
field: UiSemanticsScrollFieldV1,
},
SemanticsScrollApproxEq {
target: UiSelectorV1,
field: UiSemanticsScrollFieldV1,
value: f64,
#[serde(default)]
eps: f64,
},
SemanticsScrollNotApproxEq {
target: UiSelectorV1,
field: UiSemanticsScrollFieldV1,
value: f64,
#[serde(default)]
eps: f64,
},
TextCompositionIs {
target: UiSelectorV1,
composing: bool,
},
ImeCursorAreaIsSome {
is_some: bool,
},
ImeCursorAreaWithinWindow {
#[serde(default)]
padding_px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
padding_insets_px: Option<UiPaddingInsetsV1>,
#[serde(default)]
eps_px: f32,
},
ImeCursorAreaMinSize {
#[serde(default)]
min_w_px: f32,
#[serde(default)]
min_h_px: f32,
#[serde(default)]
eps_px: f32,
},
ImeSurroundingTextIsSome {
is_some: bool,
},
ImeSurroundingTextValid,
CheckedIsNone {
target: UiSelectorV1,
},
ActiveItemIs {
container: UiSelectorV1,
item: UiSelectorV1,
},
ActiveItemIsNone {
container: UiSelectorV1,
},
BarrierRoots {
#[serde(default)]
barrier_root: UiOptionalRootStateV1,
#[serde(default)]
focus_barrier_root: UiOptionalRootStateV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
require_equal: Option<bool>,
},
RenderTextMissingGlyphsIs {
missing_glyphs: u64,
},
RenderTextFontTraceCapturedWhenMissingGlyphs,
TextFontStackKeyStable {
stable_frames: u32,
},
FontCatalogPopulated,
SystemFontRescanIdle,
AssetLoadMissingBundleAssetRequestsGe {
min: u64,
},
AssetLoadStaleManifestRequestsGe {
min: u64,
},
AssetLoadUnsupportedFileRequestsGe {
min: u64,
},
AssetLoadUnsupportedUrlRequestsGe {
min: u64,
},
AssetLoadExternalReferenceUnavailableRequestsGe {
min: u64,
},
AssetLoadRevisionChangeRequestsGe {
min: u64,
},
AssetLoadRecentOutcomeSeen {
outcome_kind: String,
},
AssetLoadRecentRevisionTransitionSeen {
transition: String,
},
BundledFontBaselineSourceIs {
source: String,
},
RendererFontEnvironmentRevisionGe {
min: u64,
},
RendererFontSourceLaneSeen {
lane: String,
},
RendererFontSourceAssetKeySeen {
asset_key: String,
},
SvgTextBridgeSelectionMissesGe {
min: u64,
},
SvgTextBridgeMissingGlyphsGe {
min: u64,
},
SvgTextBridgeDiagnosticsCleanIs {
clean: bool,
},
SvgTextBridgeFallbackSeen {
from_family: String,
to_family: String,
},
AssetReloadEpochGe {
min: u64,
},
AssetReloadConfiguredBackendIs {
backend: String,
},
AssetReloadActiveBackendIs {
backend: String,
},
AssetReloadFallbackReasonIs {
reason: String,
},
RunnerAccessibilityActivated,
VisibleInWindow {
target: UiSelectorV1,
},
BoundsWithinWindow {
target: UiSelectorV1,
#[serde(default)]
padding_px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
padding_insets_px: Option<UiPaddingInsetsV1>,
#[serde(default)]
eps_px: f32,
},
TextInputImeCursorAreaWithinWindow {
#[serde(default)]
padding_px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
padding_insets_px: Option<UiPaddingInsetsV1>,
#[serde(default)]
eps_px: f32,
},
BoundsMinSize {
target: UiSelectorV1,
#[serde(default)]
min_w_px: f32,
#[serde(default)]
min_h_px: f32,
#[serde(default)]
eps_px: f32,
},
BoundsMaxSize {
target: UiSelectorV1,
#[serde(default)]
max_w_px: f32,
#[serde(default)]
max_h_px: f32,
#[serde(default)]
eps_px: f32,
},
BoundsApproxEqual {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
BoundsCenterApproxEqual {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
BoundsNonOverlapping {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
BoundsOverlapping {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
BoundsOverlappingX {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
BoundsOverlappingY {
a: UiSelectorV1,
b: UiSelectorV1,
#[serde(default)]
eps_px: f32,
},
EventKindSeen {
event_kind: String,
},
AppSnapshotFieldEquals {
pointer: String,
value: serde_json::Value,
},
KnownWindowCountGe {
n: u32,
},
KnownWindowCountIs {
n: u32,
},
PlatformUiWindowHoverDetectionIs {
quality: String,
},
PlatformWindowReceiverAtCursorIs {
window: UiWindowTargetV1,
},
WindowStyleEffectiveIs {
window: UiWindowTargetV1,
style: UiWindowStyleMatchV1,
},
WindowBackgroundMaterialEffectiveIs {
window: UiWindowTargetV1,
material: UiWindowBackgroundMaterialRequestV1,
},
DockDragCurrentWindowIs {
window: UiWindowTargetV1,
},
DockDragKindIs {
drag_kind: String,
},
DockDragMovingWindowIs {
window: UiWindowTargetV1,
},
DockDragWindowUnderMovingWindowIs {
window: UiWindowTargetV1,
},
DockDragActiveIs {
active: bool,
},
DockDragPayloadGhostVisibleIs {
visible: bool,
},
DockDragTransparentPayloadAppliedIs {
applied: bool,
},
DockDragTransparentPayloadHitTestPassthroughAppliedIs {
applied: bool,
},
DockDragWindowUnderCursorSourceIs {
source: String,
},
DockDragWindowUnderMovingWindowSourceIs {
source: String,
},
DockFloatingDragActiveIs {
active: bool,
},
DockDropPreviewKindIs {
preview_kind: String,
},
DockDropResolveSourceIs {
source: String,
},
DockDropResolvedIsSome {
some: bool,
},
DockDropResolvedZoneIs {
zone: String,
},
DockDropResolvedInsertIndexIs {
index: u32,
},
DockTabStripActiveOverflowIs {
overflow: bool,
},
DockTabStripActiveVisibleIs {
visible: bool,
},
DockTabStripActiveScrollPxGe {
px: f32,
},
DockTabStripActiveScrollPxLe {
px: f32,
},
WorkspaceTabStripActiveOverflowIs {
overflow: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
WorkspaceTabStripActiveVisibleIs {
visible: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
WorkspaceTabStripActiveScrollPxGe {
px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
WorkspaceTabStripActiveScrollPxLe {
px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
WorkspaceTabStripDragActiveIs {
active: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
WorkspaceTabStripDragArmedIs {
armed: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pane_id: Option<String>,
},
DockGraphCanonicalIs {
canonical: bool,
},
DockGraphHasNestedSameAxisSplitsIs {
has_nested: bool,
},
DockGraphNodeCountLe {
max: u32,
},
DockGraphMaxSplitDepthLe {
max: u32,
},
DockGraphSignatureIs {
signature: String,
},
DockGraphSignatureContains {
needle: String,
},
DockGraphSignatureNotContains {
needle: String,
},
DockGraphSignatureFingerprint64Is {
fingerprint64: u64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiSemanticsNumericFieldV1 {
Value,
Min,
Max,
Step,
Jump,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiSemanticsScrollFieldV1 {
X,
XMin,
XMax,
Y,
YMin,
YMax,
}
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiOptionalRootStateV1 {
#[default]
Any,
None,
Some,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiSelectorV1 {
RoleAndName {
role: String,
name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_z_index: Option<u32>,
},
RoleAndPath {
role: String,
name: String,
ancestors: Vec<UiRoleAndNameV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_z_index: Option<u32>,
},
TestId {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_z_index: Option<u32>,
},
GlobalElementId {
element: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_z_index: Option<u32>,
},
NodeId {
node: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
root_z_index: Option<u32>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiRoleAndNameV1 {
pub role: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiSemanticsNodeGetV1 {
pub schema_version: u32,
pub window: u64,
pub node_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiSemanticsNodeGetAckV1 {
pub schema_version: u32,
pub status: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub window: u64,
pub node_id: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub semantics_fingerprint: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub node: Option<serde_json::Value>,
#[serde(default)]
pub children: Vec<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub captured_unix_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiHitTestExplainV1 {
pub schema_version: u32,
pub window: u64,
pub target: UiSelectorV1,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiHitTestExplainAckV1 {
pub schema_version: u32,
pub status: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub window: u64,
pub target: UiSelectorV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub semantics_fingerprint: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hittable: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_test: Option<UiHitTestTraceEntryV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub captured_unix_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiInspectConfigV1 {
pub schema_version: u32,
pub enabled: bool,
#[serde(default = "serde_default_true")]
pub consume_clicks: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsBundleDumpV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_snapshots: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsAppExitRequestV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delay_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsBundleDumpedV1 {
pub schema_version: u32,
pub exported_unix_ms: u64,
pub out_dir: String,
pub dir: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_json_chunk: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_json_chunk_index: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_json_chunk_count: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsScreenshotRequestV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default = "default_capture_screenshot_timeout_frames")]
pub timeout_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevtoolsScreenshotResultV1 {
pub schema_version: u32,
pub status: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub request_id: String,
pub window: u64,
pub bundle_dir_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screenshots_dir: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagScreenshotRequestV1 {
pub schema_version: u32,
pub out_dir: String,
pub bundle_dir_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(default)]
pub windows: Vec<DiagScreenshotWindowRequestV1>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagScreenshotWindowRequestV1 {
pub window: u64,
pub tick_id: u64,
pub frame_id: u64,
#[serde(default = "serde_default_one_f64")]
pub scale_factor: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagScreenshotResultFileV1 {
#[serde(default = "default_diag_screenshot_schema_version")]
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_unix_ms: Option<u64>,
#[serde(default)]
pub completed: Vec<DiagScreenshotResultEntryV1>,
}
impl Default for DiagScreenshotResultFileV1 {
fn default() -> Self {
Self {
schema_version: default_diag_screenshot_schema_version(),
updated_unix_ms: None,
completed: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagScreenshotResultEntryV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
pub bundle_dir_name: String,
pub window: u64,
pub tick_id: u64,
pub frame_id: u64,
pub scale_factor: f32,
pub file: String,
pub width_px: u32,
pub height_px: u32,
pub completed_unix_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiArtifactStatsV1 {
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_json_bytes: Option<u64>,
#[serde(default)]
pub window_count: u64,
#[serde(default)]
pub event_count: u64,
#[serde(default)]
pub snapshot_count: u64,
#[serde(default)]
pub max_snapshots: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dump_max_snapshots: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiScriptResultV1 {
pub schema_version: u32,
pub run_id: u64,
pub updated_unix_ms: u64,
pub window: Option<u64>,
pub stage: UiScriptStageV1,
pub step_index: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason_code: Option<String>,
pub reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evidence: Option<UiScriptEvidenceV1>,
pub last_bundle_dir: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_bundle_artifact: Option<UiArtifactStatsV1>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiScriptEvidenceV1 {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub event_log: Vec<UiScriptEventLogEntryV1>,
#[serde(default, skip_serializing_if = "is_zero_u64")]
pub event_log_dropped: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub capabilities_check: Option<UiCapabilitiesCheckV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub selector_resolution_trace: Vec<UiSelectorResolutionTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hit_test_trace: Vec<UiHitTestTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub click_stable_trace: Vec<UiClickStableTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub bounds_stable_trace: Vec<UiBoundsStableTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub focus_trace: Vec<UiFocusTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub shortcut_routing_trace: Vec<UiShortcutRoutingTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub command_dispatch_trace: Vec<UiCommandDispatchTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub overlay_placement_trace: Vec<UiOverlayPlacementTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub web_ime_trace: Vec<UiWebImeTraceEntryV1>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ime_event_trace: Vec<UiImeEventTraceEntryV1>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiCapabilitiesCheckV1 {
pub schema_version: u32,
pub source: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub available: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub missing: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiScriptEventLogEntryV1 {
pub unix_ms: u64,
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub step_index: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bundle_dir: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tick_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frame_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window_snapshot_seq: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiSelectorResolutionTraceEntryV1 {
pub step_index: u32,
pub selector: UiSelectorV1,
#[serde(default)]
pub match_count: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chosen_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub candidates: Vec<UiSelectorResolutionCandidateV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiSelectorResolutionCandidateV1 {
pub node_id: u64,
pub role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub test_id: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiPointV1 {
pub x_px: f32,
pub y_px: f32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiRectV1 {
pub x_px: f32,
pub y_px: f32,
pub w_px: f32,
pub h_px: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiHitTestTraceEntryV1 {
pub step_index: u32,
pub selector: UiSelectorV1,
pub position: UiPointV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intended_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intended_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intended_bounds: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hit_node_path: Vec<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_semantics_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_semantics_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub includes_intended: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_path_contains_intended: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blocking_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blocking_root: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blocking_layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub routing_explain: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub barrier_root: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focus_barrier_root: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_bounds: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_active: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_multiple_layers: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_bounds: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_element_path: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub scope_roots: Vec<UiHitTestScopeRootEvidenceV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiClickStableTraceEntryV1 {
pub step_index: u32,
pub stable_required: u32,
pub stable_count: u32,
pub moved_px: f32,
pub max_move_px: f32,
pub remaining_frames: u32,
pub hit_test: UiHitTestTraceEntryV1,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiBoundsStableTraceEntryV1 {
pub step_index: u32,
pub selector: UiSelectorV1,
pub stable_required: u32,
pub stable_count: u32,
pub moved_px: f32,
pub max_move_px: f32,
pub remaining_frames: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bounds: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiHitTestScopeRootEvidenceV1 {
pub kind: String,
pub root: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blocks_underlay_input: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hit_testable: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiFocusTraceEntryV1 {
pub step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason_code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text_input_snapshot: Option<UiTextInputSnapshotV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modal_barrier_root: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focus_barrier_root: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_occlusion_layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_active: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_layer_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pointer_capture_multiple_layers: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focused_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focused_element_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focused_node_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focused_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focused_role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub matches_expected: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiTextInputSnapshotV1 {
#[serde(default)]
pub focus_is_text_input: bool,
#[serde(default)]
pub is_composing: bool,
#[serde(default)]
pub text_len_utf16: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection_utf16: Option<(u32, u32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub marked_utf16: Option<(u32, u32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ime_cursor_area: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ime_surrounding_text_len_bytes: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ime_surrounding_cursor_bytes: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ime_surrounding_anchor_bytes: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiShortcutRoutingTraceEntryV1 {
pub step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default)]
pub frame_id: u64,
pub phase: String,
#[serde(default)]
pub deferred: bool,
#[serde(default)]
pub focus_is_text_input: bool,
#[serde(default)]
pub ime_composing: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub key_contexts: Vec<String>,
pub key: String,
pub modifiers: UiKeyModifiersV1,
pub repeat: bool,
pub outcome: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command_enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pending_sequence_len: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiOverlayPlacementTraceKindV1 {
AnchoredPanel,
PlacedRect,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UiShortcutRoutingTraceQueryV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub phase: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ime_composing: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub focus_is_text_input: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiCommandDispatchTraceEntryV1 {
pub step_index: u32,
pub frame_id: u64,
pub command: String,
pub handled: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_scope: Option<String>,
#[serde(default)]
pub handled_by_driver: bool,
#[serde(default)]
pub stopped: bool,
#[serde(default)]
pub source_kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_test_id: Option<String>,
#[serde(default)]
pub started_from_focus: bool,
#[serde(default)]
pub used_default_root_fallback: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UiCommandDispatchTraceQueryV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_scope: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_driver: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled_by_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_from_focus: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub used_default_root_fallback: Option<bool>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiLayoutDirectionV1 {
Ltr,
Rtl,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiOverlaySideV1 {
Top,
Bottom,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiOverlayAlignV1 {
Start,
Center,
End,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiOverlayStickyModeV1 {
Partial,
Always,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiEdgesV1 {
pub top_px: f32,
pub right_px: f32,
pub bottom_px: f32,
pub left_px: f32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiSizeV1 {
pub w_px: f32,
pub h_px: f32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiOverlayOffsetV1 {
pub main_axis_px: f32,
pub cross_axis_px: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alignment_axis_px: Option<f32>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiOverlayShiftV1 {
pub main_axis: bool,
pub cross_axis: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct UiOverlayArrowLayoutV1 {
pub side: UiOverlaySideV1,
pub offset_px: f32,
pub alignment_offset_px: f32,
pub center_offset_px: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum UiOverlayPlacementTraceEntryV1 {
AnchoredPanel {
step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
note: Option<String>,
#[serde(default)]
frame_id: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
overlay_root_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
anchor_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
anchor_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
content_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
content_test_id: Option<String>,
outer_input: UiRectV1,
outer_collision: UiRectV1,
anchor: UiRectV1,
desired: UiSizeV1,
side_offset_px: f32,
preferred_side: UiOverlaySideV1,
align: UiOverlayAlignV1,
direction: UiLayoutDirectionV1,
sticky: UiOverlayStickyModeV1,
offset: UiOverlayOffsetV1,
shift: UiOverlayShiftV1,
collision_padding: UiEdgesV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
collision_boundary: Option<UiRectV1>,
gap_px: f32,
preferred_rect: UiRectV1,
flipped_rect: UiRectV1,
#[serde(default)]
preferred_fits_without_main_clamp: bool,
#[serde(default)]
flipped_fits_without_main_clamp: bool,
#[serde(default)]
preferred_available_main_px: f32,
#[serde(default)]
flipped_available_main_px: f32,
chosen_side: UiOverlaySideV1,
chosen_rect: UiRectV1,
rect_after_shift: UiRectV1,
shift_delta: UiPointV1,
final_rect: UiRectV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
arrow: Option<UiOverlayArrowLayoutV1>,
},
PlacedRect {
step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
note: Option<String>,
#[serde(default)]
frame_id: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
overlay_root_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
anchor_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
anchor_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
content_element: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
content_test_id: Option<String>,
outer: UiRectV1,
anchor: UiRectV1,
placed: UiRectV1,
#[serde(default, skip_serializing_if = "Option::is_none")]
side: Option<UiOverlaySideV1>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UiOverlayPlacementTraceQueryV1 {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<UiOverlayPlacementTraceKindV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub overlay_root_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub anchor_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_test_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preferred_side: Option<UiOverlaySideV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chosen_side: Option<UiOverlaySideV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub flipped: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub align: Option<UiOverlayAlignV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sticky: Option<UiOverlayStickyModeV1>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiWebImeTraceEntryV1 {
pub step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub composing: bool,
#[serde(default)]
pub suppress_next_input: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub textarea_has_focus: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active_element_tag: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position_mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mount_kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_pixel_ratio: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub textarea_selection_start_utf16: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub textarea_selection_end_utf16: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_cursor_area: Option<UiRectV1>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_cursor_anchor_px: Option<(f32, f32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_input_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_preedit_len: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_preedit_cursor_utf16: Option<(u32, u32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_commit_len: Option<u32>,
#[serde(default)]
pub beforeinput_seen: u64,
#[serde(default)]
pub input_seen: u64,
#[serde(default)]
pub suppressed_input_seen: u64,
#[serde(default)]
pub composition_start_seen: u64,
#[serde(default)]
pub composition_update_seen: u64,
#[serde(default)]
pub composition_end_seen: u64,
#[serde(default)]
pub cursor_area_set_seen: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiImeEventTraceEntryV1 {
pub step_index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preedit_len: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preedit_cursor: Option<(u32, u32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub commit_len: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delete_surrounding: Option<(u32, u32)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UiScriptStageV1 {
Queued,
Running,
Passed,
Failed,
}
fn serde_default_true() -> bool {
true
}
fn serde_default_one_f64() -> f64 {
1.0
}
fn default_diag_screenshot_schema_version() -> u32 {
1
}
fn is_zero_u64(v: &u64) -> bool {
*v == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn devtools_app_exit_request_serializes_minimally() {
let value = serde_json::to_value(DevtoolsAppExitRequestV1 {
schema_version: 1,
reason: None,
delay_ms: None,
})
.unwrap();
assert_eq!(value, serde_json::json!({ "schema_version": 1 }));
}
#[test]
fn predicate_runner_accessibility_activated_serializes_and_deserializes() {
let value = serde_json::to_value(UiPredicateV1::RunnerAccessibilityActivated).unwrap();
assert_eq!(
value,
serde_json::json!({ "kind": "runner_accessibility_activated" })
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::RunnerAccessibilityActivated
));
}
#[test]
fn predicate_app_snapshot_field_equals_serializes_minimally() {
let value = serde_json::to_value(UiPredicateV1::AppSnapshotFieldEquals {
pointer: "/shell/settings_open".to_string(),
value: serde_json::json!(true),
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "app_snapshot_field_equals",
"pointer": "/shell/settings_open",
"value": true,
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::AppSnapshotFieldEquals { .. }
));
}
#[test]
fn predicate_bounds_approx_equal_serializes_and_deserializes() {
let value = serde_json::to_value(UiPredicateV1::BoundsApproxEqual {
a: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
b: UiSelectorV1::TestId {
id: "b".to_string(),
root_z_index: None,
},
eps_px: 1.0,
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "bounds_approx_equal",
"a": { "kind": "test_id", "id": "a" },
"b": { "kind": "test_id", "id": "b" },
"eps_px": 1.0
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(roundtrip, UiPredicateV1::BoundsApproxEqual { .. }));
}
#[test]
fn predicate_bounds_center_approx_equal_serializes_and_deserializes() {
let value = serde_json::to_value(UiPredicateV1::BoundsCenterApproxEqual {
a: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
b: UiSelectorV1::TestId {
id: "b".to_string(),
root_z_index: None,
},
eps_px: 1.0,
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "bounds_center_approx_equal",
"a": { "kind": "test_id", "id": "a" },
"b": { "kind": "test_id", "id": "b" },
"eps_px": 1.0
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::BoundsCenterApproxEqual { .. }
));
}
#[test]
fn predicate_exists_under_serializes_minimally() {
let value = serde_json::to_value(UiPredicateV1::ExistsUnder {
scope: UiSelectorV1::TestId {
id: "scope".to_string(),
root_z_index: None,
},
target: UiSelectorV1::TestId {
id: "target".to_string(),
root_z_index: None,
},
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "exists_under",
"scope": { "kind": "test_id", "id": "scope" },
"target": { "kind": "test_id", "id": "target" },
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(roundtrip, UiPredicateV1::ExistsUnder { .. }));
}
#[test]
fn predicate_value_equals_serializes_minimally() {
let value = serde_json::to_value(UiPredicateV1::ValueEquals {
target: UiSelectorV1::TestId {
id: "name".to_string(),
root_z_index: None,
},
text: "Alice".to_string(),
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "value_equals",
"target": { "kind": "test_id", "id": "name" },
"text": "Alice",
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(roundtrip, UiPredicateV1::ValueEquals { .. }));
}
#[test]
fn predicate_focused_descendant_is_serializes_minimally() {
let value = serde_json::to_value(UiPredicateV1::FocusedDescendantIs {
scope: UiSelectorV1::TestId {
id: "dialog".to_string(),
root_z_index: None,
},
target: UiSelectorV1::TestId {
id: "close".to_string(),
root_z_index: None,
},
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "focused_descendant_is",
"scope": { "kind": "test_id", "id": "dialog" },
"target": { "kind": "test_id", "id": "close" },
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::FocusedDescendantIs { .. }
));
}
#[test]
fn predicate_dock_tab_strip_scroll_predicates_serialize_and_deserialize() {
let value =
serde_json::to_value(UiPredicateV1::DockTabStripActiveScrollPxGe { px: 12.0 }).unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "dock_tab_strip_active_scroll_px_ge",
"px": 12.0
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::DockTabStripActiveScrollPxGe { .. }
));
let value =
serde_json::to_value(UiPredicateV1::DockTabStripActiveScrollPxLe { px: 0.0 }).unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "dock_tab_strip_active_scroll_px_le",
"px": 0.0
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::DockTabStripActiveScrollPxLe { .. }
));
}
#[test]
fn predicate_workspace_tab_strip_scroll_predicates_serialize_and_deserialize() {
let value = serde_json::to_value(UiPredicateV1::WorkspaceTabStripActiveScrollPxGe {
px: 12.0,
pane_id: None,
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "workspace_tab_strip_active_scroll_px_ge",
"px": 12.0
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::WorkspaceTabStripActiveScrollPxGe { .. }
));
let value = serde_json::to_value(UiPredicateV1::WorkspaceTabStripActiveScrollPxLe {
px: 0.0,
pane_id: Some("pane-a".to_string()),
})
.unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "workspace_tab_strip_active_scroll_px_le",
"px": 0.0,
"pane_id": "pane-a",
})
);
let roundtrip: UiPredicateV1 = serde_json::from_value(value).unwrap();
assert!(matches!(
roundtrip,
UiPredicateV1::WorkspaceTabStripActiveScrollPxLe { .. }
));
}
#[test]
fn diag_screenshot_request_round_trips_and_defaults_scale_factor() {
let json = serde_json::json!({
"schema_version": 1,
"out_dir": "target/fret-diag",
"bundle_dir_name": "1700000-bundle",
"request_id": "req-1",
"windows": [{
"window": 123,
"tick_id": 1,
"frame_id": 2
}]
});
let parsed: DiagScreenshotRequestV1 = serde_json::from_value(json).unwrap();
assert_eq!(parsed.schema_version, 1);
assert_eq!(parsed.windows.len(), 1);
assert_eq!(parsed.windows[0].scale_factor, 1.0);
let value = serde_json::to_value(parsed).unwrap();
assert_eq!(value["schema_version"].as_u64(), Some(1));
}
#[test]
fn diag_screenshot_result_defaults_schema_version_to_1() {
let value = serde_json::json!({
"updated_unix_ms": 1700000,
"completed": [],
});
let parsed: DiagScreenshotResultFileV1 = serde_json::from_value(value).unwrap();
assert_eq!(parsed.schema_version, 1);
assert_eq!(DiagScreenshotResultFileV1::default().schema_version, 1);
}
#[test]
fn click_step_pointer_kind_round_trips_and_omits_none() {
let step = UiActionStepV2::Click {
window: None,
pointer_kind: None,
target: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
button: UiMouseButtonV1::Left,
click_count: 1,
modifiers: None,
};
let value = serde_json::to_value(step.clone()).unwrap();
assert_eq!(
value,
serde_json::json!({
"type": "click",
"target": {"kind":"test_id","id":"a"},
"button": "left"
})
);
let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
"type": "click",
"pointer_kind": "touch",
"target": {"kind":"test_id","id":"a"},
"button": "left",
"click_count": 1
}))
.unwrap();
assert!(matches!(
parsed,
UiActionStepV2::Click {
pointer_kind: Some(UiPointerKindV1::Touch),
..
}
));
}
#[test]
fn tap_step_pointer_kind_round_trips_and_omits_none() {
let step = UiActionStepV2::Tap {
window: None,
pointer_kind: None,
target: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
modifiers: None,
};
let value = serde_json::to_value(step.clone()).unwrap();
assert_eq!(
value,
serde_json::json!({
"type": "tap",
"target": {"kind":"test_id","id":"a"}
})
);
let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
"type": "tap",
"pointer_kind": "pen",
"target": {"kind":"test_id","id":"a"}
}))
.unwrap();
assert!(matches!(
parsed,
UiActionStepV2::Tap {
pointer_kind: Some(UiPointerKindV1::Pen),
..
}
));
}
#[test]
fn long_press_step_round_trips_and_omits_defaults() {
let step = UiActionStepV2::LongPress {
window: None,
pointer_kind: None,
target: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
duration_ms: default_long_press_duration_ms(),
modifiers: None,
};
let value = serde_json::to_value(step.clone()).unwrap();
assert_eq!(
value,
serde_json::json!({
"type": "long_press",
"target": {"kind":"test_id","id":"a"}
})
);
let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
"type": "long_press",
"pointer_kind": "pen",
"target": {"kind":"test_id","id":"a"},
"duration_ms": 125
}))
.unwrap();
assert!(matches!(
parsed,
UiActionStepV2::LongPress {
pointer_kind: Some(UiPointerKindV1::Pen),
duration_ms: 125,
..
}
));
}
#[test]
fn swipe_step_round_trips_and_omits_defaults() {
let step = UiActionStepV2::Swipe {
window: None,
pointer_kind: None,
target: UiSelectorV1::TestId {
id: "a".to_string(),
root_z_index: None,
},
delta_x: 12.0,
delta_y: -8.0,
steps: default_drag_steps(),
modifiers: None,
};
let value = serde_json::to_value(step.clone()).unwrap();
assert_eq!(
value,
serde_json::json!({
"type": "swipe",
"target": {"kind":"test_id","id":"a"},
"delta_x": 12.0,
"delta_y": -8.0
})
);
let parsed: UiActionStepV2 = serde_json::from_value(serde_json::json!({
"type": "swipe",
"pointer_kind": "pen",
"target": {"kind":"test_id","id":"a"},
"delta_x": 1.0,
"delta_y": 2.0,
"steps": 3
}))
.unwrap();
assert!(matches!(
parsed,
UiActionStepV2::Swipe {
pointer_kind: Some(UiPointerKindV1::Pen),
steps: 3,
..
}
));
}
#[test]
fn step_activate_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "activate",
"target": { "kind": "test_id", "id": "trigger" }
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::Activate { window, target } => {
assert!(window.is_none());
assert!(matches!(target, UiSelectorV1::TestId { .. }));
}
_ => panic!("expected activate"),
}
}
#[test]
fn step_focus_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "focus",
"target": { "kind": "test_id", "id": "trigger" }
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::Focus { window, target } => {
assert!(window.is_none());
assert!(matches!(target, UiSelectorV1::TestId { .. }));
}
_ => panic!("expected focus"),
}
}
#[test]
fn hit_test_explain_request_round_trips() {
let value = serde_json::json!({
"schema_version": 1,
"window": 7,
"target": { "kind": "test_id", "id": "trigger" }
});
let req: UiHitTestExplainV1 = serde_json::from_value(value.clone()).unwrap();
assert_eq!(req.window, 7);
assert_eq!(serde_json::to_value(req).unwrap(), value);
}
#[test]
fn step_paste_text_into_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "paste_text_into",
"target": { "kind": "test_id", "id": "field" },
"text": "Hello"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::PasteTextInto {
window,
pointer_kind,
target,
text,
clear_before_paste,
timeout_frames,
} => {
assert!(window.is_none());
assert!(pointer_kind.is_none());
assert!(matches!(target, UiSelectorV1::TestId { .. }));
assert_eq!(text, "Hello");
assert!(!clear_before_paste);
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected paste_text_into"),
}
}
#[test]
fn step_set_text_value_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "set_text_value",
"target": { "kind": "test_id", "id": "field" },
"text": "#112233"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::SetTextValue {
window,
target,
text,
timeout_frames,
} => {
assert!(window.is_none());
assert!(matches!(target, UiSelectorV1::TestId { .. }));
assert_eq!(text, "#112233");
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected set_text_value"),
}
}
#[test]
fn step_wait_clipboard_write_result_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "wait_clipboard_write_result",
"outcome": "success"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::WaitClipboardWriteResult {
outcome,
error_kind,
message_contains,
timeout_frames,
} => {
assert_eq!(outcome, UiClipboardWriteResultV1::Success);
assert!(error_kind.is_none());
assert!(message_contains.is_none());
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected wait_clipboard_write_result"),
}
}
#[test]
fn step_assert_clipboard_write_result_deserializes_with_failure_details() {
let value = serde_json::json!({
"type": "assert_clipboard_write_result",
"outcome": "failure",
"error_kind": "unavailable",
"message_contains": "forced clipboard unavailable",
"timeout_frames": 90
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::AssertClipboardWriteResult {
outcome,
error_kind,
message_contains,
timeout_frames,
} => {
assert_eq!(outcome, UiClipboardWriteResultV1::Failure);
assert_eq!(error_kind, Some(UiClipboardAccessErrorKindV1::Unavailable));
assert_eq!(
message_contains.as_deref(),
Some("forced clipboard unavailable")
);
assert_eq!(timeout_frames, 90);
}
_ => panic!("expected assert_clipboard_write_result"),
}
}
#[test]
fn step_inspect_help_lock_best_match_and_copy_selector_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "inspect_help_lock_best_match_and_copy_selector",
"query": "ui-gallery-nav-search"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::InspectHelpLockBestMatchAndCopySelector {
window,
query,
timeout_frames,
} => {
assert!(window.is_none());
assert_eq!(query, "ui-gallery-nav-search");
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected inspect_help_lock_best_match_and_copy_selector"),
}
}
#[test]
fn step_inspect_help_tree_lock_best_match_and_copy_selector_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "inspect_help_tree_lock_best_match_and_copy_selector",
"query": "ui-gallery-nav-search"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::InspectHelpTreeLockBestMatchAndCopySelector {
window,
query,
timeout_frames,
} => {
assert!(window.is_none());
assert_eq!(query, "ui-gallery-nav-search");
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected inspect_help_tree_lock_best_match_and_copy_selector"),
}
}
#[test]
fn step_wait_until_deserializes_with_default_timeout_frames() {
let value = serde_json::json!({
"type": "wait_until",
"predicate": {
"kind": "exists",
"target": { "kind": "test_id", "id": "ui-gallery-nav-search" }
}
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::WaitUntil {
window,
predicate,
timeout_frames,
timeout_ms,
} => {
assert!(window.is_none());
assert!(
matches!(predicate, UiPredicateV1::Exists { .. }),
"expected exists predicate"
);
assert_eq!(timeout_frames, default_action_timeout_frames());
assert!(timeout_ms.is_none());
}
_ => panic!("expected wait_until"),
}
}
#[test]
fn step_wait_semantics_scroll_stable_deserializes_with_defaults() {
let value = serde_json::json!({
"type": "wait_semantics_scroll_stable",
"target": { "kind": "test_id", "id": "ui-gallery-content-viewport" },
"field": "y_max"
});
let step: UiActionStepV2 = serde_json::from_value(value).unwrap();
match step {
UiActionStepV2::WaitSemanticsScrollStable {
window,
target,
field,
stable_frames,
max_delta,
timeout_frames,
} => {
assert!(window.is_none());
assert!(matches!(target, UiSelectorV1::TestId { .. }));
assert_eq!(field, UiSemanticsScrollFieldV1::YMax);
assert_eq!(stable_frames, default_semantics_scroll_stable_frames());
assert_eq!(max_delta, default_semantics_scroll_stable_max_delta());
assert_eq!(timeout_frames, default_action_timeout_frames());
}
_ => panic!("expected wait_semantics_scroll_stable"),
}
}
}