playwright_cdp/cdp/
messages.rs1use serde::{Deserialize, Serialize};
13use serde_json::Value;
14
15#[derive(Debug, Serialize)]
17pub struct CdpRequest {
18 pub id: u32,
19 pub method: String,
20 #[serde(skip_serializing_if = "Value::is_null", default)]
21 pub params: Value,
22 #[serde(skip_serializing_if = "Option::is_none", rename = "sessionId")]
23 pub session_id: Option<String>,
24}
25
26#[derive(Debug, Deserialize)]
28pub struct CdpErrorBody {
29 pub code: i64,
30 pub message: String,
31 #[serde(default)]
32 pub data: Option<Value>,
33}
34
35#[derive(Debug, Deserialize)]
37pub struct CdpResponse {
38 pub id: u32,
39 #[serde(default)]
40 pub result: Option<Value>,
41 #[serde(default)]
42 pub error: Option<CdpErrorBody>,
43}
44
45#[derive(Debug, Clone, Deserialize)]
47pub struct CdpEvent {
48 pub method: String,
49 #[serde(default)]
50 pub params: Value,
51 #[serde(default, rename = "sessionId")]
52 pub session_id: Option<String>,
53}
54
55#[derive(Debug, Deserialize)]
60#[serde(untagged)]
61pub enum Message {
62 Response(CdpResponse),
63 Event(CdpEvent),
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn request_omits_empty_params_and_missing_session() {
72 let req = CdpRequest {
73 id: 1,
74 method: "Target.setAutoAttach".into(),
75 params: serde_json::json!({"autoAttach": true}),
76 session_id: None,
77 };
78 let s = serde_json::to_string(&req).unwrap();
79 assert!(!s.contains("sessionId"), "sessionId should be absent: {s}");
80 assert!(s.contains("\"params\""));
81 }
82
83 #[test]
84 fn request_serializes_session_id_when_present() {
85 let req = CdpRequest {
86 id: 2,
87 method: "Page.navigate".into(),
88 params: serde_json::json!({}),
89 session_id: Some("ABC".into()),
90 };
91 let s = serde_json::to_string(&req).unwrap();
92 assert!(s.contains("\"sessionId\":\"ABC\""), "{s}");
93 }
94
95 #[test]
96 fn demux_response_with_result() {
97 let v = serde_json::json!({ "id": 5, "result": { "ok": true } });
98 let m: Message = serde_json::from_value(v).unwrap();
99 match m {
100 Message::Response(r) => {
101 assert_eq!(r.id, 5);
102 assert!(r.error.is_none());
103 }
104 _ => panic!("expected Response"),
105 }
106 }
107
108 #[test]
109 fn demux_response_with_error() {
110 let v = serde_json::json!({
111 "id": 6,
112 "error": { "code": -32000, "message": "boom", "data": "ctx" }
113 });
114 let m: Message = serde_json::from_value(v).unwrap();
115 match m {
116 Message::Response(r) => {
117 let e = r.error.expect("error body");
118 assert_eq!(e.code, -32000);
119 assert_eq!(e.message, "boom");
120 assert_eq!(e.data, Some(serde_json::json!("ctx")));
121 assert!(r.result.is_none());
122 }
123 _ => panic!("expected Response"),
124 }
125 }
126
127 #[test]
128 fn demux_event_no_session() {
129 let v = serde_json::json!({ "method": "Target.attachedToTarget", "params": { "x": 1 } });
130 let m: Message = serde_json::from_value(v).unwrap();
131 match m {
132 Message::Event(e) => {
133 assert_eq!(e.method, "Target.attachedToTarget");
134 assert!(e.session_id.is_none());
135 }
136 _ => panic!("expected Event"),
137 }
138 }
139
140 #[test]
141 fn demux_event_with_session() {
142 let v = serde_json::json!({
143 "method": "Page.lifecycleEvent",
144 "params": { "name": "load" },
145 "sessionId": "S1"
146 });
147 let m: Message = serde_json::from_value(v).unwrap();
148 match m {
149 Message::Event(e) => {
150 assert_eq!(e.method, "Page.lifecycleEvent");
151 assert_eq!(e.session_id.as_deref(), Some("S1"));
152 }
153 _ => panic!("expected Event"),
154 }
155 }
156}