1mod action;
2mod device;
3mod event;
4#[cfg(feature = "fixtures")]
5pub mod fixtures;
6mod handshake;
7mod manual;
8mod methods;
9mod rpc;
10#[cfg(feature = "schema")]
11pub mod schema;
12mod stream;
13mod ui;
14mod wire_integer;
15
16pub use action::{
17 ActionCall, ActionDefinition, ActionProtection, ActionResult, DeviceExecuteParams,
18 RecordedActionCall,
19};
20pub use device::{
21 AssetRef, DeviceId, DeviceInfo, DeviceSelectParams, DeviceSelectResult, DevicesListResult,
22 Observation, Platform, ScreenshotOmissionReason, Viewport,
23};
24pub use event::{
25 ActionOutcome, ErrorInfo, EventId, EventSequence, MAX_VERDICT_EVIDENCE_REFERENCES,
26 MAX_VERDICT_SUMMARY_LENGTH, MediaFrame, MediaStreamId, MediaStreamInfo, MediaStreamKind,
27 SessionExport, SessionId, SessionInfo, SessionOutcome, SessionState, TestEvent,
28 TestEventPayload, Verdict, VerdictStatus, VerdictValidationError,
29};
30pub use handshake::{
31 FeatureNegotiationError, FeatureOffer, FeatureSelection, HelloParams, HelloResult,
32 PROTOCOL_VERSION, PeerInfo, ProtocolIncompatibilityReason, ProtocolNegotiationError,
33 ProtocolOffer, ProtocolRange, ProtocolSelection, ProtocolVersion, TransportInfo, feature,
34 negotiate_features, negotiate_protocol, supported_protocol_offer,
35};
36pub use manual::{
37 MANUAL_RECORDING_VERSION, ManualActionArguments, ManualActionStep, ManualRecording,
38};
39pub use methods::{
40 DeviceCapabilitiesResult, DeviceConnectResult, DeviceDisconnectResult, DeviceExecuteResult,
41 DeviceObserveResult, EventsClearResult, EventsListParams, EventsListResult,
42 MAX_EVENTS_LIST_PAGE_SIZE, MediaStreamCaptureParams, MediaStreamCaptureResult,
43 MediaStreamEndParams, MediaStreamEndResult, MediaStreamStartParams, MediaStreamStartResult,
44 SessionCurrentResult, SessionEndParams, SessionEndResult, SessionExportParams,
45 SessionExportResult, SessionStartResult, SessionTargetParams, SessionsListResult,
46 SystemDescribeResult, UiSnapshotGetParams, UiSnapshotGetResult, VerdictRecordParams,
47 VerdictRecordResult,
48};
49pub use rpc::{
50 JsonRpcVersion, MAX_SAFE_INTEGER_ID, RequestCancelParams, RequestCancelResult,
51 RequestCancelStatus, RequestTimeoutMs, RpcError, RpcId, RpcParams, RpcRequest, RpcResponse,
52};
53pub use stream::{
54 EventStreamCursor, EventStreamEndpoint, EventStreamEpoch, EventStreamOriginPolicy,
55 EventSubscriptionId, EventsStreamEventMethod, EventsStreamEventNotification,
56 EventsStreamEventParams, EventsStreamOpenParams, EventsStreamOpenResult,
57 EventsStreamTerminalMethod, EventsStreamTerminalNotification, EventsStreamTerminalParams,
58 EventsStreamTermination, EventsSubscribeParams, EventsSubscribeResult, RpcServerMessage,
59 RpcServerNotification,
60};
61pub use ui::{
62 ActionExecution, CLEAR_ELEMENT_ACTION, ClearElementArguments, ClearElementResult,
63 CoordinateFallbackReason, ElementActionOutput, ElementSelector, ElementTarget,
64 FIND_ELEMENT_ACTION, FindElementArguments, FindElementResult, MAX_UI_IDENTIFIER_LENGTH,
65 MAX_UI_ROLE_LENGTH, MAX_UI_SNAPSHOT_BYTES, MAX_UI_SNAPSHOT_NODES, MAX_UI_TEXT_LENGTH,
66 SEMANTIC_ACTION_NAMES, SET_ELEMENT_VALUE_ACTION, SetElementValueArguments,
67 SetElementValueResult, TAP_ELEMENT_ACTION, TapElementArguments, TapElementResult, TextMatch,
68 TextMatchMode, UI_SNAPSHOT_FORMAT_VERSION, UI_SNAPSHOT_MEDIA_TYPE, UiContextKind, UiContextRef,
69 UiContextSelector, UiContractError, UiNode, UiNodeRef, UiRect, UiSnapshot,
70 UiSnapshotOmissionReason, UiSnapshotRef, WAIT_FOR_ELEMENT_ACTION, WaitForElementArguments,
71 WaitForElementCondition, WaitForElementResult, is_semantic_action_name,
72};
73pub use wire_integer::{MAX_SAFE_INTEGER, json_integer_as_i32, json_integer_as_u32};
74
75#[cfg(test)]
76mod tests {
77 use serde_json::json;
78 use uuid::Uuid;
79
80 use super::{
81 ActionCall, EventId, EventSequence, HelloParams, HelloResult, JsonRpcVersion, RpcId,
82 RpcParams, RpcRequest, RpcResponse, SessionId, TestEvent, TestEventPayload,
83 };
84
85 #[test]
86 fn action_call_uses_camel_case_json() {
87 let call = ActionCall {
88 id: Uuid::nil(),
89 name: "tap".to_owned(),
90 arguments: json!({ "x": 10, "y": 20 }),
91 };
92
93 let value = serde_json::to_value(call).expect("serialize action call");
94 assert_eq!(value["id"], Uuid::nil().to_string());
95 assert_eq!(value["arguments"]["x"], 10);
96 }
97
98 #[test]
99 fn rpc_request_round_trips() {
100 let request = RpcRequest {
101 jsonrpc: JsonRpcVersion::V2,
102 id: RpcId::Number(42),
103 method: "device.observe".to_owned(),
104 timeout_ms: None,
105 params: Some(RpcParams::Object(Default::default())),
106 };
107
108 let serialized = serde_json::to_string(&request).expect("serialize request");
109 let restored: RpcRequest = serde_json::from_str(&serialized).expect("restore request");
110 assert_eq!(restored, request);
111 }
112
113 #[test]
114 fn event_type_is_explicit() {
115 let event = TestEvent {
116 event_id: EventId::from(Uuid::nil()),
117 session_id: SessionId::from(Uuid::nil()),
118 sequence: EventSequence::FIRST,
119 request_id: None,
120 device_id: None,
121 at_ms: 1,
122 payload: TestEventPayload::SessionStarted,
123 };
124
125 let value = serde_json::to_value(event).expect("serialize event");
126 assert_eq!(value["payload"]["type"], "sessionStarted");
127 assert_eq!(value["atMs"], 1);
128 }
129
130 #[test]
131 fn system_hello_golden_fixtures_round_trip() {
132 let request_value: serde_json::Value =
133 serde_json::from_str(include_str!("../fixtures/system-hello-v1.request.json"))
134 .expect("read hello request fixture");
135 let request: RpcRequest = serde_json::from_value(request_value.clone())
136 .expect("deserialize hello request fixture");
137 let hello_params: HelloParams =
138 serde_json::from_value(request.params.clone().expect("hello params").into_value())
139 .expect("deserialize typed hello params");
140 assert_eq!(
141 serde_json::to_value(hello_params).expect("serialize typed hello params"),
142 request_value["params"]
143 );
144 assert_eq!(
145 serde_json::to_value(request).expect("serialize hello request fixture"),
146 request_value
147 );
148
149 let response_value: serde_json::Value =
150 serde_json::from_str(include_str!("../fixtures/system-hello-v1.response.json"))
151 .expect("read hello response fixture");
152 let response: RpcResponse = serde_json::from_value(response_value.clone())
153 .expect("deserialize hello response fixture");
154 let hello_result: HelloResult =
155 serde_json::from_value(response.result().expect("hello result").clone())
156 .expect("deserialize typed hello result");
157 assert_eq!(
158 serde_json::to_value(hello_result).expect("serialize typed hello result"),
159 response_value["result"]
160 );
161 assert_eq!(
162 serde_json::to_value(response).expect("serialize hello response fixture"),
163 response_value
164 );
165 }
166}