1use crate::actions::ActionSequence;
6use crate::capabilities::{
7 BrowserCapabilities, Capabilities, CapabilitiesMatching, SpecNewSessionParameters,
8};
9use crate::common::{
10 CredentialParameters, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, MAX_SAFE_INTEGER,
11};
12use crate::error::{ErrorStatus, WebDriverError, WebDriverResult};
13use crate::httpapi::{Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute};
14use crate::Parameters;
15use serde::de::{self, Deserialize, Deserializer};
16use serde_json::Value;
17
18#[derive(Debug, PartialEq)]
19pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
20 NewSession(NewSessionParameters),
21 DeleteSession,
22 Get(GetParameters),
23 GetCurrentUrl,
24 GoBack,
25 GoForward,
26 Refresh,
27 GetTitle,
28 GetPageSource,
29 GetWindowHandle,
30 GetWindowHandles,
31 NewWindow(NewWindowParameters),
32 CloseWindow,
33 GetWindowRect,
34 SetWindowRect(WindowRectParameters),
35 MinimizeWindow,
36 MaximizeWindow,
37 FullscreenWindow,
38 SwitchToWindow(SwitchToWindowParameters),
39 SwitchToFrame(SwitchToFrameParameters),
40 SwitchToParentFrame,
41 FindElement(LocatorParameters),
42 FindElements(LocatorParameters),
43 FindElementElement(WebElement, LocatorParameters),
44 FindElementElements(WebElement, LocatorParameters),
45 FindShadowRootElement(ShadowRoot, LocatorParameters),
46 FindShadowRootElements(ShadowRoot, LocatorParameters),
47 GetActiveElement,
48 GetComputedLabel(WebElement),
49 GetComputedRole(WebElement),
50 GetShadowRoot(WebElement),
51 IsDisplayed(WebElement),
52 IsSelected(WebElement),
53 GetElementAttribute(WebElement, String),
54 GetElementProperty(WebElement, String),
55 GetCSSValue(WebElement, String),
56 GetElementText(WebElement),
57 GetElementTagName(WebElement),
58 GetElementRect(WebElement),
59 IsEnabled(WebElement),
60 ExecuteScript(JavascriptCommandParameters),
61 ExecuteAsyncScript(JavascriptCommandParameters),
62 GetCookies,
63 GetNamedCookie(String),
64 AddCookie(AddCookieParameters),
65 DeleteCookies,
66 DeleteCookie(String),
67 GetTimeouts,
68 SetTimeouts(TimeoutsParameters),
69 ElementClick(WebElement),
70 ElementClear(WebElement),
71 ElementSendKeys(WebElement, SendKeysParameters),
72 PerformActions(ActionsParameters),
73 ReleaseActions,
74 DismissAlert,
75 AcceptAlert,
76 GetAlertText,
77 SendAlertText(SendKeysParameters),
78 TakeScreenshot,
79 TakeElementScreenshot(WebElement),
80 Print(PrintParameters),
81 SetPermission(SetPermissionParameters),
82 Status,
83 Extension(T),
84 WebAuthnAddVirtualAuthenticator(AuthenticatorParameters),
85 WebAuthnRemoveVirtualAuthenticator,
86 WebAuthnAddCredential(CredentialParameters),
87 WebAuthnGetCredentials,
88 WebAuthnRemoveCredential,
89 WebAuthnRemoveAllCredentials,
90 WebAuthnSetUserVerified(UserVerificationParameters),
91}
92
93pub trait WebDriverExtensionCommand: Clone + Send {
94 fn parameters_json(&self) -> Option<Value>;
95}
96
97#[derive(Clone, Debug)]
98pub struct VoidWebDriverExtensionCommand;
99
100impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
101 fn parameters_json(&self) -> Option<Value> {
102 panic!("No extensions implemented");
103 }
104}
105
106#[derive(Debug, PartialEq)]
107pub struct WebDriverMessage<U: WebDriverExtensionRoute = VoidWebDriverExtensionRoute> {
108 pub session_id: Option<String>,
109 pub command: WebDriverCommand<U::Command>,
110}
111
112impl<U: WebDriverExtensionRoute> WebDriverMessage<U> {
113 pub fn new(
114 session_id: Option<String>,
115 command: WebDriverCommand<U::Command>,
116 ) -> WebDriverMessage<U> {
117 WebDriverMessage {
118 session_id,
119 command,
120 }
121 }
122
123 pub fn from_http(
124 match_type: Route<U>,
125 params: &Parameters,
126 raw_body: &str,
127 requires_body: bool,
128 ) -> WebDriverResult<WebDriverMessage<U>> {
129 let session_id = WebDriverMessage::<U>::get_session_id(params);
130 let body_data = WebDriverMessage::<U>::decode_body(raw_body, requires_body)?;
131 let command = match match_type {
132 Route::NewSession => WebDriverCommand::NewSession(serde_json::from_str(raw_body)?),
133 Route::DeleteSession => WebDriverCommand::DeleteSession,
134 Route::Get => WebDriverCommand::Get(serde_json::from_str(raw_body)?),
135 Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
136 Route::GoBack => WebDriverCommand::GoBack,
137 Route::GoForward => WebDriverCommand::GoForward,
138 Route::Refresh => WebDriverCommand::Refresh,
139 Route::GetTitle => WebDriverCommand::GetTitle,
140 Route::GetPageSource => WebDriverCommand::GetPageSource,
141 Route::GetWindowHandle => WebDriverCommand::GetWindowHandle,
142 Route::GetWindowHandles => WebDriverCommand::GetWindowHandles,
143 Route::NewWindow => WebDriverCommand::NewWindow(serde_json::from_str(raw_body)?),
144 Route::CloseWindow => WebDriverCommand::CloseWindow,
145 Route::GetTimeouts => WebDriverCommand::GetTimeouts,
146 Route::SetTimeouts => WebDriverCommand::SetTimeouts(serde_json::from_str(raw_body)?),
147 Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => {
148 WebDriverCommand::GetWindowRect
149 }
150 Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
151 WebDriverCommand::SetWindowRect(serde_json::from_str(raw_body)?)
152 }
153 Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
154 Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
155 Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
156 Route::SwitchToWindow => {
157 WebDriverCommand::SwitchToWindow(serde_json::from_str(raw_body)?)
158 }
159 Route::SwitchToFrame => {
160 WebDriverCommand::SwitchToFrame(serde_json::from_str(raw_body)?)
161 }
162 Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
163 Route::FindElement => WebDriverCommand::FindElement(serde_json::from_str(raw_body)?),
164 Route::FindElements => WebDriverCommand::FindElements(serde_json::from_str(raw_body)?),
165 Route::FindElementElement => {
166 let element_id = try_opt!(
167 params.get("elementId"),
168 ErrorStatus::InvalidArgument,
169 "Missing elementId parameter"
170 );
171 let element = WebElement(element_id.as_str().into());
172 WebDriverCommand::FindElementElement(element, serde_json::from_str(raw_body)?)
173 }
174 Route::FindElementElements => {
175 let element_id = try_opt!(
176 params.get("elementId"),
177 ErrorStatus::InvalidArgument,
178 "Missing elementId parameter"
179 );
180 let element = WebElement(element_id.as_str().into());
181 WebDriverCommand::FindElementElements(element, serde_json::from_str(raw_body)?)
182 }
183 Route::FindShadowRootElement => {
184 let shadow_id = try_opt!(
185 params.get("shadowId"),
186 ErrorStatus::InvalidArgument,
187 "Missing shadowId parameter"
188 );
189 let shadow_root = ShadowRoot(shadow_id.as_str().into());
190 WebDriverCommand::FindShadowRootElement(
191 shadow_root,
192 serde_json::from_str(raw_body)?,
193 )
194 }
195 Route::FindShadowRootElements => {
196 let shadow_id = try_opt!(
197 params.get("shadowId"),
198 ErrorStatus::InvalidArgument,
199 "Missing shadowId parameter"
200 );
201 let shadow_root = ShadowRoot(shadow_id.as_str().into());
202 WebDriverCommand::FindShadowRootElements(
203 shadow_root,
204 serde_json::from_str(raw_body)?,
205 )
206 }
207 Route::GetActiveElement => WebDriverCommand::GetActiveElement,
208 Route::GetShadowRoot => {
209 let element_id = try_opt!(
210 params.get("elementId"),
211 ErrorStatus::InvalidArgument,
212 "Missing elementId parameter"
213 );
214 let element = WebElement(element_id.as_str().into());
215 WebDriverCommand::GetShadowRoot(element)
216 }
217 Route::GetComputedLabel => {
218 let element_id = try_opt!(
219 params.get("elementId"),
220 ErrorStatus::InvalidArgument,
221 "Missing elementId parameter"
222 );
223 let element = WebElement(element_id.as_str().into());
224 WebDriverCommand::GetComputedLabel(element)
225 }
226 Route::GetComputedRole => {
227 let element_id = try_opt!(
228 params.get("elementId"),
229 ErrorStatus::InvalidArgument,
230 "Missing elementId parameter"
231 );
232 let element = WebElement(element_id.as_str().into());
233 WebDriverCommand::GetComputedRole(element)
234 }
235 Route::IsDisplayed => {
236 let element_id = try_opt!(
237 params.get("elementId"),
238 ErrorStatus::InvalidArgument,
239 "Missing elementId parameter"
240 );
241 let element = WebElement(element_id.as_str().into());
242 WebDriverCommand::IsDisplayed(element)
243 }
244 Route::IsSelected => {
245 let element_id = try_opt!(
246 params.get("elementId"),
247 ErrorStatus::InvalidArgument,
248 "Missing elementId parameter"
249 );
250 let element = WebElement(element_id.as_str().into());
251 WebDriverCommand::IsSelected(element)
252 }
253 Route::GetElementAttribute => {
254 let element_id = try_opt!(
255 params.get("elementId"),
256 ErrorStatus::InvalidArgument,
257 "Missing elementId parameter"
258 );
259 let element = WebElement(element_id.as_str().into());
260 let attr = try_opt!(
261 params.get("name"),
262 ErrorStatus::InvalidArgument,
263 "Missing name parameter"
264 )
265 .as_str();
266 WebDriverCommand::GetElementAttribute(element, attr.into())
267 }
268 Route::GetElementProperty => {
269 let element_id = try_opt!(
270 params.get("elementId"),
271 ErrorStatus::InvalidArgument,
272 "Missing elementId parameter"
273 );
274 let element = WebElement(element_id.as_str().into());
275 let property = try_opt!(
276 params.get("name"),
277 ErrorStatus::InvalidArgument,
278 "Missing name parameter"
279 )
280 .as_str();
281 WebDriverCommand::GetElementProperty(element, property.into())
282 }
283 Route::GetCSSValue => {
284 let element_id = try_opt!(
285 params.get("elementId"),
286 ErrorStatus::InvalidArgument,
287 "Missing elementId parameter"
288 );
289 let element = WebElement(element_id.as_str().into());
290 let property = try_opt!(
291 params.get("propertyName"),
292 ErrorStatus::InvalidArgument,
293 "Missing propertyName parameter"
294 )
295 .as_str();
296 WebDriverCommand::GetCSSValue(element, property.into())
297 }
298 Route::GetElementText => {
299 let element_id = try_opt!(
300 params.get("elementId"),
301 ErrorStatus::InvalidArgument,
302 "Missing elementId parameter"
303 );
304 let element = WebElement(element_id.as_str().into());
305 WebDriverCommand::GetElementText(element)
306 }
307 Route::GetElementTagName => {
308 let element_id = try_opt!(
309 params.get("elementId"),
310 ErrorStatus::InvalidArgument,
311 "Missing elementId parameter"
312 );
313 let element = WebElement(element_id.as_str().into());
314 WebDriverCommand::GetElementTagName(element)
315 }
316 Route::GetElementRect => {
317 let element_id = try_opt!(
318 params.get("elementId"),
319 ErrorStatus::InvalidArgument,
320 "Missing elementId parameter"
321 );
322 let element = WebElement(element_id.as_str().into());
323 WebDriverCommand::GetElementRect(element)
324 }
325 Route::IsEnabled => {
326 let element_id = try_opt!(
327 params.get("elementId"),
328 ErrorStatus::InvalidArgument,
329 "Missing elementId parameter"
330 );
331 let element = WebElement(element_id.as_str().into());
332 WebDriverCommand::IsEnabled(element)
333 }
334 Route::ElementClick => {
335 let element_id = try_opt!(
336 params.get("elementId"),
337 ErrorStatus::InvalidArgument,
338 "Missing elementId parameter"
339 );
340 let element = WebElement(element_id.as_str().into());
341 WebDriverCommand::ElementClick(element)
342 }
343 Route::ElementClear => {
344 let element_id = try_opt!(
345 params.get("elementId"),
346 ErrorStatus::InvalidArgument,
347 "Missing elementId parameter"
348 );
349 let element = WebElement(element_id.as_str().into());
350 WebDriverCommand::ElementClear(element)
351 }
352 Route::ElementSendKeys => {
353 let element_id = try_opt!(
354 params.get("elementId"),
355 ErrorStatus::InvalidArgument,
356 "Missing elementId parameter"
357 );
358 let element = WebElement(element_id.as_str().into());
359 WebDriverCommand::ElementSendKeys(element, serde_json::from_str(raw_body)?)
360 }
361 Route::ExecuteScript => {
362 WebDriverCommand::ExecuteScript(serde_json::from_str(raw_body)?)
363 }
364 Route::ExecuteAsyncScript => {
365 WebDriverCommand::ExecuteAsyncScript(serde_json::from_str(raw_body)?)
366 }
367 Route::GetCookies => WebDriverCommand::GetCookies,
368 Route::GetNamedCookie => {
369 let name = try_opt!(
370 params.get("name"),
371 ErrorStatus::InvalidArgument,
372 "Missing 'name' parameter"
373 )
374 .as_str()
375 .into();
376 WebDriverCommand::GetNamedCookie(name)
377 }
378 Route::AddCookie => WebDriverCommand::AddCookie(serde_json::from_str(raw_body)?),
379 Route::DeleteCookies => WebDriverCommand::DeleteCookies,
380 Route::DeleteCookie => {
381 let name = try_opt!(
382 params.get("name"),
383 ErrorStatus::InvalidArgument,
384 "Missing name parameter"
385 )
386 .as_str()
387 .into();
388 WebDriverCommand::DeleteCookie(name)
389 }
390 Route::PerformActions => {
391 WebDriverCommand::PerformActions(serde_json::from_str(raw_body)?)
392 }
393 Route::ReleaseActions => WebDriverCommand::ReleaseActions,
394 Route::DismissAlert => WebDriverCommand::DismissAlert,
395 Route::AcceptAlert => WebDriverCommand::AcceptAlert,
396 Route::GetAlertText => WebDriverCommand::GetAlertText,
397 Route::SendAlertText => {
398 WebDriverCommand::SendAlertText(serde_json::from_str(raw_body)?)
399 }
400 Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
401 Route::TakeElementScreenshot => {
402 let element_id = try_opt!(
403 params.get("elementId"),
404 ErrorStatus::InvalidArgument,
405 "Missing elementId parameter"
406 );
407 let element = WebElement(element_id.as_str().into());
408 WebDriverCommand::TakeElementScreenshot(element)
409 }
410 Route::Print => WebDriverCommand::Print(serde_json::from_str(raw_body)?),
411 Route::SetPermission => {
412 WebDriverCommand::SetPermission(serde_json::from_str(raw_body)?)
413 }
414 Route::Status => WebDriverCommand::Status,
415 Route::Extension(ref extension) => extension.command(params, &body_data)?,
416 Route::WebAuthnAddVirtualAuthenticator => {
417 WebDriverCommand::WebAuthnAddVirtualAuthenticator(serde_json::from_str(raw_body)?)
418 }
419 Route::WebAuthnRemoveVirtualAuthenticator => {
420 WebDriverCommand::WebAuthnRemoveVirtualAuthenticator
421 }
422 Route::WebAuthnAddCredential => {
423 WebDriverCommand::WebAuthnAddCredential(serde_json::from_str(raw_body)?)
424 }
425 Route::WebAuthnGetCredentials => WebDriverCommand::WebAuthnGetCredentials,
426 Route::WebAuthnRemoveCredential => WebDriverCommand::WebAuthnRemoveCredential,
427 Route::WebAuthnRemoveAllCredentials => WebDriverCommand::WebAuthnRemoveAllCredentials,
428 Route::WebAuthnSetUserVerified => {
429 WebDriverCommand::WebAuthnSetUserVerified(serde_json::from_str(raw_body)?)
430 }
431 };
432 Ok(WebDriverMessage::new(session_id, command))
433 }
434
435 fn get_session_id(params: &Parameters) -> Option<String> {
436 params.get("sessionId").cloned()
437 }
438
439 fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Value> {
440 if requires_body {
441 match serde_json::from_str(body) {
442 Ok(x @ Value::Object(_)) => Ok(x),
443 Ok(_) => Err(WebDriverError::new(
444 ErrorStatus::InvalidArgument,
445 "Body was not a JSON Object",
446 )),
447 Err(e) => {
448 if e.is_io() {
449 Err(WebDriverError::new(
450 ErrorStatus::InvalidArgument,
451 format!("I/O error whilst decoding body: {}", e),
452 ))
453 } else {
454 let msg = format!("Failed to decode request as JSON: {}", body);
455 let stack = format!("Syntax error at :{}:{}", e.line(), e.column());
456 Err(WebDriverError::new_with_data(
457 ErrorStatus::InvalidArgument,
458 msg,
459 None,
460 Some(stack),
461 ))
462 }
463 }
464 }
465 } else {
466 Ok(Value::Null)
467 }
468 }
469}
470
471#[derive(Debug, PartialEq, Serialize, Deserialize)]
472pub struct ActionsParameters {
473 pub actions: Vec<ActionSequence>,
474}
475
476#[derive(Debug, PartialEq, Serialize, Deserialize)]
477#[serde(remote = "Self")]
478pub struct AddCookieParameters {
479 pub name: String,
480 pub value: String,
481 pub path: Option<String>,
482 pub domain: Option<String>,
483 #[serde(default)]
484 pub secure: bool,
485 #[serde(default)]
486 pub httpOnly: bool,
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub expiry: Option<Date>,
489 pub sameSite: Option<String>,
490}
491
492impl<'de> Deserialize<'de> for AddCookieParameters {
493 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
494 where
495 D: Deserializer<'de>,
496 {
497 #[derive(Deserialize)]
498 struct Wrapper {
499 #[serde(with = "AddCookieParameters")]
500 cookie: AddCookieParameters,
501 }
502
503 Wrapper::deserialize(deserializer).map(|wrapper| wrapper.cookie)
504 }
505}
506
507#[derive(Debug, PartialEq, Serialize, Deserialize)]
508pub struct GetParameters {
509 pub url: String,
510}
511
512#[derive(Debug, PartialEq, Serialize, Deserialize)]
513pub struct GetNamedCookieParameters {
514 pub name: Option<String>,
515}
516
517#[derive(Debug, PartialEq, Serialize, Deserialize)]
518pub struct JavascriptCommandParameters {
519 pub script: String,
520 pub args: Option<Vec<Value>>,
521}
522
523#[derive(Debug, PartialEq, Serialize, Deserialize)]
524pub struct LocatorParameters {
525 pub using: LocatorStrategy,
526 pub value: String,
527}
528
529#[derive(Debug, PartialEq, Serialize)]
530pub struct NewSessionParameters {
531 pub capabilities: SpecNewSessionParameters,
532}
533
534impl<'de> Deserialize<'de> for NewSessionParameters {
537 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
538 where
539 D: Deserializer<'de>,
540 {
541 let value = serde_json::Value::deserialize(deserializer)?;
542 let caps = value
543 .get("capabilities")
544 .ok_or(de::Error::missing_field("capabilities"))?;
545 if !caps.is_object() {
546 return Err(de::Error::custom("capabilities must be objects"));
547 }
548 let capabilities =
549 SpecNewSessionParameters::deserialize(caps).map_err(de::Error::custom)?;
550 Ok(NewSessionParameters { capabilities })
551 }
552}
553
554impl CapabilitiesMatching for NewSessionParameters {
555 fn match_browser<T: BrowserCapabilities>(
556 &self,
557 browser_capabilities: &mut T,
558 ) -> WebDriverResult<Option<Capabilities>> {
559 self.capabilities.match_browser(browser_capabilities)
560 }
561}
562
563#[derive(Debug, PartialEq, Serialize, Deserialize)]
564pub struct NewWindowParameters {
565 #[serde(rename = "type")]
566 pub type_hint: Option<String>,
567}
568
569#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
570#[serde(untagged)]
571pub enum PrintPageRange {
572 Integer(u64),
573 Range(String),
574}
575
576#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
577#[serde(default, rename_all = "camelCase")]
578pub struct PrintParameters {
579 pub orientation: PrintOrientation,
580 #[serde(deserialize_with = "deserialize_to_print_scale_f64")]
581 pub scale: f64,
582 pub background: bool,
583 pub page: PrintPage,
584 pub margin: PrintMargins,
585 pub page_ranges: Vec<PrintPageRange>,
586 pub shrink_to_fit: bool,
587}
588
589impl Default for PrintParameters {
590 fn default() -> Self {
591 PrintParameters {
592 orientation: PrintOrientation::default(),
593 scale: 1.0,
594 background: false,
595 page: PrintPage::default(),
596 margin: PrintMargins::default(),
597 page_ranges: Vec::new(),
598 shrink_to_fit: true,
599 }
600 }
601}
602
603#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
604#[serde(rename_all = "lowercase")]
605pub enum PrintOrientation {
606 Landscape,
607 #[default]
608 Portrait,
609}
610
611#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
612#[serde(default)]
613pub struct PrintPage {
614 #[serde(deserialize_with = "deserialize_to_positive_f64")]
615 pub width: f64,
616 #[serde(deserialize_with = "deserialize_to_positive_f64")]
617 pub height: f64,
618}
619
620impl Default for PrintPage {
621 fn default() -> Self {
622 PrintPage {
623 width: 21.59,
624 height: 27.94,
625 }
626 }
627}
628
629#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
630#[serde(default)]
631pub struct PrintMargins {
632 #[serde(deserialize_with = "deserialize_to_positive_f64")]
633 pub top: f64,
634 #[serde(deserialize_with = "deserialize_to_positive_f64")]
635 pub bottom: f64,
636 #[serde(deserialize_with = "deserialize_to_positive_f64")]
637 pub left: f64,
638 #[serde(deserialize_with = "deserialize_to_positive_f64")]
639 pub right: f64,
640}
641
642impl Default for PrintMargins {
643 fn default() -> Self {
644 PrintMargins {
645 top: 1.0,
646 bottom: 1.0,
647 left: 1.0,
648 right: 1.0,
649 }
650 }
651}
652
653#[derive(Debug, PartialEq, Serialize, Deserialize)]
654pub struct SetPermissionParameters {
655 pub descriptor: SetPermissionDescriptor,
656 pub state: SetPermissionState,
657}
658
659#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
660pub struct SetPermissionDescriptor {
661 pub name: String,
662}
663
664#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
665#[serde(rename_all = "lowercase")]
666pub enum SetPermissionState {
667 Denied,
668 Granted,
669 Prompt,
670}
671
672#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
673pub enum WebAuthnProtocol {
674 #[serde(rename = "ctap1/u2f")]
675 Ctap1U2f,
676 #[serde(rename = "ctap2")]
677 Ctap2,
678 #[serde(rename = "ctap2_1")]
679 Ctap2_1,
680}
681
682#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
683#[serde(rename_all = "kebab-case")]
684pub enum AuthenticatorTransport {
685 Usb,
686 Nfc,
687 Ble,
688 SmartCard,
689 Hybrid,
690 Internal,
691}
692
693fn default_as_true() -> bool {
694 true
695}
696
697#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
698#[serde(rename_all = "camelCase")]
699pub struct AuthenticatorParameters {
700 pub protocol: WebAuthnProtocol,
701 pub transport: AuthenticatorTransport,
702 #[serde(default)]
703 pub has_resident_key: bool,
704 #[serde(default)]
705 pub has_user_verification: bool,
706 #[serde(default = "default_as_true")]
707 pub is_user_consenting: bool,
708 #[serde(default)]
709 pub is_user_verified: bool,
710}
711
712#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
713pub struct UserVerificationParameters {
714 #[serde(rename = "isUserVerified")]
715 pub is_user_verified: bool,
716}
717
718fn deserialize_to_positive_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
719where
720 D: Deserializer<'de>,
721{
722 let val = f64::deserialize(deserializer)?;
723 if val < 0.0 {
724 return Err(de::Error::custom(format!("{} is negative", val)));
725 };
726 Ok(val)
727}
728
729fn deserialize_to_print_scale_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
730where
731 D: Deserializer<'de>,
732{
733 let val = f64::deserialize(deserializer)?;
734 if !(0.1..=2.0).contains(&val) {
735 return Err(de::Error::custom(format!("{} is outside range 0.1-2", val)));
736 };
737 Ok(val)
738}
739
740#[derive(Debug, PartialEq, Serialize, Deserialize)]
741pub struct SendKeysParameters {
742 pub text: String,
743}
744
745#[derive(Debug, PartialEq, Serialize, Deserialize)]
746pub struct SwitchToFrameParameters {
747 pub id: FrameId,
748}
749
750#[derive(Debug, PartialEq, Serialize, Deserialize)]
751pub struct SwitchToWindowParameters {
752 pub handle: String,
753}
754
755#[derive(Debug, PartialEq, Serialize, Deserialize)]
756pub struct TakeScreenshotParameters {
757 pub element: Option<WebElement>,
758}
759
760#[derive(Debug, PartialEq, Serialize, Deserialize)]
761pub struct TimeoutsParameters {
762 #[serde(
763 default,
764 skip_serializing_if = "Option::is_none",
765 deserialize_with = "deserialize_to_u64"
766 )]
767 pub implicit: Option<u64>,
768 #[serde(
769 default,
770 rename = "pageLoad",
771 skip_serializing_if = "Option::is_none",
772 deserialize_with = "deserialize_to_u64"
773 )]
774 pub page_load: Option<u64>,
775 #[serde(
776 default,
777 skip_serializing_if = "Option::is_none",
778 deserialize_with = "deserialize_to_nullable_u64"
779 )]
780 #[allow(clippy::option_option)]
781 pub script: Option<Option<u64>>,
782}
783
784#[allow(clippy::option_option)]
785fn deserialize_to_nullable_u64<'de, D>(deserializer: D) -> Result<Option<Option<u64>>, D::Error>
786where
787 D: Deserializer<'de>,
788{
789 let opt: Option<f64> = Option::deserialize(deserializer)?;
790 let value = match opt {
791 Some(n) => {
792 if n < 0.0 || n.fract() != 0.0 {
793 return Err(de::Error::custom(format!(
794 "{} is not a positive Integer",
795 n
796 )));
797 }
798 if (n as u64) > MAX_SAFE_INTEGER {
799 return Err(de::Error::custom(format!(
800 "{} is greater than maximum safe integer",
801 n
802 )));
803 }
804 Some(Some(n as u64))
805 }
806 None => Some(None),
807 };
808
809 Ok(value)
810}
811
812fn deserialize_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
813where
814 D: Deserializer<'de>,
815{
816 let opt: Option<f64> = Option::deserialize(deserializer)?;
817 let value = match opt {
818 Some(n) => {
819 if n < 0.0 || n.fract() != 0.0 {
820 return Err(de::Error::custom(format!(
821 "{} is not a positive Integer",
822 n
823 )));
824 }
825 if (n as u64) > MAX_SAFE_INTEGER {
826 return Err(de::Error::custom(format!(
827 "{} is greater than maximum safe integer",
828 n
829 )));
830 }
831 Some(n as u64)
832 }
833 None => return Err(de::Error::custom("null is not a positive integer")),
834 };
835
836 Ok(value)
837}
838
839#[derive(Debug, PartialEq, Serialize, Deserialize)]
850pub struct WindowRectParameters {
851 #[serde(
852 default,
853 skip_serializing_if = "Option::is_none",
854 deserialize_with = "deserialize_to_i32"
855 )]
856 pub x: Option<i32>,
857 #[serde(
858 default,
859 skip_serializing_if = "Option::is_none",
860 deserialize_with = "deserialize_to_i32"
861 )]
862 pub y: Option<i32>,
863 #[serde(
864 default,
865 skip_serializing_if = "Option::is_none",
866 deserialize_with = "deserialize_to_positive_i32"
867 )]
868 pub width: Option<i32>,
869 #[serde(
870 default,
871 skip_serializing_if = "Option::is_none",
872 deserialize_with = "deserialize_to_positive_i32"
873 )]
874 pub height: Option<i32>,
875}
876
877fn deserialize_to_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
878where
879 D: Deserializer<'de>,
880{
881 let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
882 let value = match opt {
883 Some(n) => {
884 if n < i64::from(i32::MIN) || n > i64::from(i32::MAX) {
885 return Err(de::Error::custom(format!("'{}' is larger than i32", n)));
886 }
887 Some(n as i32)
888 }
889 None => None,
890 };
891
892 Ok(value)
893}
894
895fn deserialize_to_positive_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
896where
897 D: Deserializer<'de>,
898{
899 let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
900 let value = match opt {
901 Some(n) => {
902 if n < 0 || n > i64::from(i32::MAX) {
903 return Err(de::Error::custom(format!("'{}' is outside of i32", n)));
904 }
905 Some(n as i32)
906 }
907 None => None,
908 };
909
910 Ok(value)
911}
912
913#[cfg(test)]
914mod tests {
915 use super::*;
916 use crate::capabilities::SpecNewSessionParameters;
917 use crate::common::ELEMENT_KEY;
918 use crate::test::assert_de;
919 use serde_json::{self, json};
920
921 #[test]
922 fn test_json_actions_parameters_missing_actions_field() {
923 assert!(serde_json::from_value::<ActionsParameters>(json!({})).is_err());
924 }
925
926 #[test]
927 fn test_json_actions_parameters_invalid() {
928 assert!(serde_json::from_value::<ActionsParameters>(json!({ "actions": null })).is_err());
929 }
930
931 #[test]
932 fn test_json_action_parameters_empty_list() {
933 assert_de(
934 &ActionsParameters { actions: vec![] },
935 json!({"actions": []}),
936 );
937 }
938
939 #[test]
940 fn test_json_action_parameters_with_unknown_field() {
941 assert_de(
942 &ActionsParameters { actions: vec![] },
943 json!({"actions": [], "foo": "bar"}),
944 );
945 }
946
947 #[test]
948 fn test_json_add_cookie_parameters_with_values() {
949 let json = json!({"cookie": {
950 "name": "foo",
951 "value": "bar",
952 "path": "/",
953 "domain": "foo.bar",
954 "expiry": 123,
955 "secure": true,
956 "httpOnly": false,
957 "sameSite": "Lax",
958 }});
959 let cookie = AddCookieParameters {
960 name: "foo".into(),
961 value: "bar".into(),
962 path: Some("/".into()),
963 domain: Some("foo.bar".into()),
964 expiry: Some(Date(123)),
965 secure: true,
966 httpOnly: false,
967 sameSite: Some("Lax".into()),
968 };
969
970 assert_de(&cookie, json);
971 }
972
973 #[test]
974 fn test_json_add_cookie_parameters_with_optional_null_fields() {
975 let json = json!({"cookie": {
976 "name": "foo",
977 "value": "bar",
978 "path": null,
979 "domain": null,
980 "expiry": null,
981 "secure": true,
982 "httpOnly": false,
983 "sameSite": null,
984 }});
985 let cookie = AddCookieParameters {
986 name: "foo".into(),
987 value: "bar".into(),
988 path: None,
989 domain: None,
990 expiry: None,
991 secure: true,
992 httpOnly: false,
993 sameSite: None,
994 };
995
996 assert_de(&cookie, json);
997 }
998
999 #[test]
1000 fn test_json_add_cookie_parameters_without_optional_fields() {
1001 let json = json!({"cookie": {
1002 "name": "foo",
1003 "value": "bar",
1004 "secure": true,
1005 "httpOnly": false,
1006 }});
1007 let cookie = AddCookieParameters {
1008 name: "foo".into(),
1009 value: "bar".into(),
1010 path: None,
1011 domain: None,
1012 expiry: None,
1013 secure: true,
1014 httpOnly: false,
1015 sameSite: None,
1016 };
1017
1018 assert_de(&cookie, json);
1019 }
1020
1021 #[test]
1022 fn test_json_add_cookie_parameters_with_invalid_cookie_field() {
1023 assert!(serde_json::from_value::<AddCookieParameters>(json!({"name": "foo"})).is_err());
1024 }
1025
1026 #[test]
1027 fn test_json_add_cookie_parameters_with_unknown_field() {
1028 let json = json!({"cookie": {
1029 "name": "foo",
1030 "value": "bar",
1031 "secure": true,
1032 "httpOnly": false,
1033 "foo": "bar",
1034 }, "baz": "bah"});
1035 let cookie = AddCookieParameters {
1036 name: "foo".into(),
1037 value: "bar".into(),
1038 path: None,
1039 domain: None,
1040 expiry: None,
1041 secure: true,
1042 httpOnly: false,
1043 sameSite: None,
1044 };
1045
1046 assert_de(&cookie, json);
1047 }
1048
1049 #[test]
1050 fn test_json_get_parameters_with_url() {
1051 assert_de(
1052 &GetParameters {
1053 url: "foo.bar".into(),
1054 },
1055 json!({"url": "foo.bar"}),
1056 );
1057 }
1058
1059 #[test]
1060 fn test_json_get_parameters_with_invalid_url_value() {
1061 assert!(serde_json::from_value::<GetParameters>(json!({"url": 3})).is_err());
1062 }
1063
1064 #[test]
1065 fn test_json_get_parameters_with_invalid_url_field() {
1066 assert!(serde_json::from_value::<GetParameters>(json!({"foo": "bar"})).is_err());
1067 }
1068
1069 #[test]
1070 fn test_json_get_parameters_with_unknown_field() {
1071 assert_de(
1072 &GetParameters {
1073 url: "foo.bar".into(),
1074 },
1075 json!({"url": "foo.bar", "foo": "bar"}),
1076 );
1077 }
1078
1079 #[test]
1080 fn test_json_get_named_cookie_parameters_with_value() {
1081 assert_de(
1082 &GetNamedCookieParameters {
1083 name: Some("foo".into()),
1084 },
1085 json!({"name": "foo"}),
1086 );
1087 }
1088
1089 #[test]
1090 fn test_json_get_named_cookie_parameters_with_optional_null_field() {
1091 assert_de(
1092 &GetNamedCookieParameters { name: None },
1093 json!({ "name": null }),
1094 );
1095 }
1096
1097 #[test]
1098 fn test_json_get_named_cookie_parameters_without_optional_null_field() {
1099 assert_de(&GetNamedCookieParameters { name: None }, json!({}));
1100 }
1101
1102 #[test]
1103 fn test_json_get_named_cookie_parameters_with_invalid_name_field() {
1104 assert!(serde_json::from_value::<GetNamedCookieParameters>(json!({"name": 3})).is_err());
1105 }
1106
1107 #[test]
1108 fn test_json_get_named_cookie_parameters_with_unknown_field() {
1109 assert_de(
1110 &GetNamedCookieParameters {
1111 name: Some("foo".into()),
1112 },
1113 json!({"name": "foo", "foo": "bar"}),
1114 );
1115 }
1116
1117 #[test]
1118 fn test_json_javascript_command_parameters_with_values() {
1119 let json = json!({
1120 "script": "foo",
1121 "args": ["1", 2],
1122 });
1123 let execute_script = JavascriptCommandParameters {
1124 script: "foo".into(),
1125 args: Some(vec!["1".into(), 2.into()]),
1126 };
1127
1128 assert_de(&execute_script, json);
1129 }
1130
1131 #[test]
1132 fn test_json_javascript_command_parameters_with_optional_null_field() {
1133 let json = json!({
1134 "script": "foo",
1135 "args": null,
1136 });
1137 let execute_script = JavascriptCommandParameters {
1138 script: "foo".into(),
1139 args: None,
1140 };
1141
1142 assert_de(&execute_script, json);
1143 }
1144
1145 #[test]
1146 fn test_json_javascript_command_parameters_without_optional_null_field() {
1147 let execute_script = JavascriptCommandParameters {
1148 script: "foo".into(),
1149 args: None,
1150 };
1151 assert_de(&execute_script, json!({"script": "foo"}));
1152 }
1153
1154 #[test]
1155 fn test_json_javascript_command_parameters_invalid_script_field() {
1156 let json = json!({ "script": null });
1157 assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
1158 }
1159
1160 #[test]
1161 fn test_json_javascript_command_parameters_invalid_args_field() {
1162 let json = json!({
1163 "script": null,
1164 "args": "1",
1165 });
1166 assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
1167 }
1168
1169 #[test]
1170 fn test_json_javascript_command_parameters_missing_script_field() {
1171 let json = json!({ "args": null });
1172 assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
1173 }
1174
1175 #[test]
1176 fn test_json_javascript_command_parameters_with_unknown_field() {
1177 let json = json!({
1178 "script": "foo",
1179 "foo": "bar",
1180 });
1181 let execute_script = JavascriptCommandParameters {
1182 script: "foo".into(),
1183 args: None,
1184 };
1185
1186 assert_de(&execute_script, json);
1187 }
1188
1189 #[test]
1190 fn test_json_locator_parameters_with_values() {
1191 let json = json!({
1192 "using": "xpath",
1193 "value": "bar",
1194 });
1195 let locator = LocatorParameters {
1196 using: LocatorStrategy::XPath,
1197 value: "bar".into(),
1198 };
1199
1200 assert_de(&locator, json);
1201 }
1202
1203 #[test]
1204 fn test_json_locator_parameters_invalid_using_field() {
1205 let json = json!({
1206 "using": "foo",
1207 "value": "bar",
1208 });
1209 assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
1210 }
1211
1212 #[test]
1213 fn test_json_locator_parameters_invalid_value_field() {
1214 let json = json!({
1215 "using": "xpath",
1216 "value": 3,
1217 });
1218 assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
1219 }
1220
1221 #[test]
1222 fn test_json_locator_parameters_missing_using_field() {
1223 assert!(serde_json::from_value::<LocatorParameters>(json!({"value": "bar"})).is_err());
1224 }
1225
1226 #[test]
1227 fn test_json_locator_parameters_missing_value_field() {
1228 assert!(serde_json::from_value::<LocatorParameters>(json!({"using": "xpath"})).is_err());
1229 }
1230
1231 #[test]
1232 fn test_json_locator_parameters_with_unknown_field() {
1233 let json = json!({
1234 "using": "xpath",
1235 "value": "bar",
1236 "foo": "bar",
1237 });
1238 let locator = LocatorParameters {
1239 using: LocatorStrategy::XPath,
1240 value: "bar".into(),
1241 };
1242
1243 assert_de(&locator, json);
1244 }
1245
1246 #[test]
1247 fn test_json_new_session_parameters_spec() {
1248 let json = json!({"capabilities": {
1249 "alwaysMatch": {},
1250 "firstMatch": [{}],
1251 }});
1252 let caps = NewSessionParameters {
1253 capabilities: SpecNewSessionParameters {
1254 alwaysMatch: Capabilities::new(),
1255 firstMatch: vec![Capabilities::new()],
1256 },
1257 };
1258
1259 assert_de(&caps, json);
1260 }
1261
1262 #[test]
1263 fn test_json_new_session_parameters_capabilities_null() {
1264 let json = json!({ "capabilities": null });
1265 assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
1266 }
1267
1268 #[test]
1269 fn test_json_new_session_parameters_capabilities_empty_list() {
1270 let json = json!({ "capabilities": []});
1271 assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
1272 }
1273
1274 #[test]
1275 fn test_json_new_session_parameters_legacy() {
1276 let json = json!({
1277 "desiredCapabilities": {},
1278 "requiredCapabilities": {},
1279 });
1280 assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
1281 }
1282
1283 #[test]
1284 fn test_json_new_session_parameters_spec_and_legacy() {
1285 let json = json!({
1286 "capabilities": {
1287 "alwaysMatch": {},
1288 "firstMatch": [{}],
1289 },
1290 "desiredCapabilities": {},
1291 "requiredCapabilities": {},
1292 });
1293 let caps = NewSessionParameters {
1294 capabilities: SpecNewSessionParameters {
1295 alwaysMatch: Capabilities::new(),
1296 firstMatch: vec![Capabilities::new()],
1297 },
1298 };
1299
1300 assert_de(&caps, json);
1301 }
1302
1303 #[test]
1304 fn test_json_new_session_parameters_with_unknown_field() {
1305 let json = json!({
1306 "capabilities": {
1307 "alwaysMatch": {},
1308 "firstMatch": [{}]
1309 },
1310 "foo": "bar",
1311 });
1312 let caps = NewSessionParameters {
1313 capabilities: SpecNewSessionParameters {
1314 alwaysMatch: Capabilities::new(),
1315 firstMatch: vec![Capabilities::new()],
1316 },
1317 };
1318
1319 assert_de(&caps, json);
1320 }
1321
1322 #[test]
1323 fn test_json_new_window_parameters_without_type() {
1324 assert_de(&NewWindowParameters { type_hint: None }, json!({}));
1325 }
1326
1327 #[test]
1328 fn test_json_new_window_parameters_with_optional_null_type() {
1329 assert_de(
1330 &NewWindowParameters { type_hint: None },
1331 json!({ "type": null }),
1332 );
1333 }
1334
1335 #[test]
1336 fn test_json_new_window_parameters_with_supported_type() {
1337 assert_de(
1338 &NewWindowParameters {
1339 type_hint: Some("tab".into()),
1340 },
1341 json!({"type": "tab"}),
1342 );
1343 }
1344
1345 #[test]
1346 fn test_json_new_window_parameters_with_unknown_type() {
1347 assert_de(
1348 &NewWindowParameters {
1349 type_hint: Some("foo".into()),
1350 },
1351 json!({"type": "foo"}),
1352 );
1353 }
1354
1355 #[test]
1356 fn test_json_new_window_parameters_with_invalid_type() {
1357 assert!(serde_json::from_value::<NewWindowParameters>(json!({"type": 3})).is_err());
1358 }
1359
1360 #[test]
1361 fn test_json_new_window_parameters_with_unknown_field() {
1362 let json = json!({
1363 "type": "tab",
1364 "foo": "bar",
1365 });
1366 let new_window = NewWindowParameters {
1367 type_hint: Some("tab".into()),
1368 };
1369
1370 assert_de(&new_window, json);
1371 }
1372
1373 #[test]
1374 fn test_json_print_defaults() {
1375 let params = PrintParameters::default();
1376 assert_de(¶ms, json!({}));
1377 }
1378
1379 #[test]
1380 fn test_json_print() {
1381 let params = PrintParameters {
1382 orientation: PrintOrientation::Landscape,
1383 page: PrintPage {
1384 width: 10.0,
1385 ..Default::default()
1386 },
1387 margin: PrintMargins {
1388 top: 10.0,
1389 ..Default::default()
1390 },
1391 scale: 1.5,
1392 ..Default::default()
1393 };
1394 assert_de(
1395 ¶ms,
1396 json!({"orientation": "landscape", "page": {"width": 10}, "margin": {"top": 10}, "scale": 1.5}),
1397 );
1398 }
1399
1400 #[test]
1401 fn test_json_scale_invalid() {
1402 assert!(serde_json::from_value::<PrintParameters>(json!({"scale": 3})).is_err());
1403 }
1404
1405 #[test]
1406 fn test_json_permission() {
1407 let params: SetPermissionParameters = SetPermissionParameters {
1408 descriptor: SetPermissionDescriptor {
1409 name: "push".into(),
1410 },
1411 state: SetPermissionState::Granted,
1412 };
1413 assert_de(
1414 ¶ms,
1415 json!({"descriptor": {"name": "push"}, "state": "granted"}),
1416 );
1417 }
1418
1419 #[test]
1420 fn test_json_permission_parameters_invalid() {
1421 assert!(serde_json::from_value::<SetPermissionParameters>(json!({"test": 3})).is_err());
1422 }
1423
1424 #[test]
1425 fn test_json_permission_descriptor_invalid_type() {
1426 assert!(serde_json::from_value::<SetPermissionParameters>(
1427 json!({"descriptor": "test", "state": "granted"})
1428 )
1429 .is_err());
1430 }
1431
1432 #[test]
1433 fn test_json_permission_state_invalid_type() {
1434 assert!(serde_json::from_value::<SetPermissionParameters>(
1435 json!({"descriptor": {"name": "push"}, "state": 3})
1436 )
1437 .is_err());
1438 }
1439
1440 #[test]
1441 fn test_json_permission_state_invalid_value() {
1442 assert!(serde_json::from_value::<SetPermissionParameters>(
1443 json!({"descriptor": {"name": "push"}, "state": "invalid"})
1444 )
1445 .is_err());
1446 }
1447
1448 #[test]
1449 fn test_json_authenticator() {
1450 let params = AuthenticatorParameters {
1451 protocol: WebAuthnProtocol::Ctap1U2f,
1452 transport: AuthenticatorTransport::Usb,
1453 has_resident_key: false,
1454 has_user_verification: false,
1455 is_user_consenting: false,
1456 is_user_verified: false,
1457 };
1458 assert_de(
1459 ¶ms,
1460 json!({"protocol": "ctap1/u2f", "transport": "usb", "hasResidentKey": false, "hasUserVerification": false, "isUserConsenting": false, "isUserVerified": false}),
1461 );
1462 }
1463
1464 #[test]
1465 fn test_json_credential() {
1466 use base64::{engine::general_purpose::URL_SAFE, Engine};
1467
1468 let encoded_string = URL_SAFE.encode(b"hello internet~");
1469 let params = CredentialParameters {
1470 credential_id: r"c3VwZXIgcmVhZGVy".to_string(),
1471 is_resident_credential: true,
1472 rp_id: "valid.rpid".to_string(),
1473 private_key: encoded_string.clone(),
1474 user_handle: encoded_string.clone(),
1475 sign_count: 0,
1476 };
1477 assert_de(
1478 ¶ms,
1479 json!({"credentialId": r"c3VwZXIgcmVhZGVy", "isResidentCredential": true, "rpId": "valid.rpid", "privateKey": encoded_string, "userHandle": encoded_string, "signCount": 0}),
1480 );
1481 }
1482
1483 #[test]
1484 fn test_json_user_verification() {
1485 let params = UserVerificationParameters {
1486 is_user_verified: false,
1487 };
1488 assert_de(¶ms, json!({"isUserVerified": false}));
1489 }
1490
1491 #[test]
1492 fn test_json_send_keys_parameters_with_value() {
1493 assert_de(
1494 &SendKeysParameters { text: "foo".into() },
1495 json!({"text": "foo"}),
1496 );
1497 }
1498
1499 #[test]
1500 fn test_json_send_keys_parameters_invalid_text_field() {
1501 assert!(serde_json::from_value::<SendKeysParameters>(json!({"text": 3})).is_err());
1502 }
1503
1504 #[test]
1505 fn test_json_send_keys_parameters_missing_text_field() {
1506 assert!(serde_json::from_value::<SendKeysParameters>(json!({})).is_err());
1507 }
1508
1509 #[test]
1510 fn test_json_send_keys_parameters_with_unknown_field() {
1511 let json = json!({
1512 "text": "foo",
1513 "foo": "bar",
1514 });
1515 let send_keys = SendKeysParameters { text: "foo".into() };
1516
1517 assert_de(&send_keys, json);
1518 }
1519
1520 #[test]
1521 fn test_json_switch_to_frame_parameters_with_number() {
1522 assert_de(
1523 &SwitchToFrameParameters {
1524 id: FrameId::Short(3),
1525 },
1526 json!({"id": 3}),
1527 );
1528 }
1529
1530 #[test]
1531 fn test_json_switch_to_frame_parameters_with_null() {
1532 assert_de(
1533 &SwitchToFrameParameters { id: FrameId::Top },
1534 json!({"id": null}),
1535 );
1536 }
1537
1538 #[test]
1539 fn test_json_switch_to_frame_parameters_with_web_element() {
1540 assert_de(
1541 &SwitchToFrameParameters {
1542 id: FrameId::Element(WebElement("foo".to_string())),
1543 },
1544 json!({"id": {"element-6066-11e4-a52e-4f735466cecf": "foo"}}),
1545 );
1546 }
1547
1548 #[test]
1549 fn test_json_switch_to_frame_parameters_with_missing_id() {
1550 assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({})).is_err())
1551 }
1552
1553 #[test]
1554 fn test_json_switch_to_frame_parameters_with_invalid_id_field() {
1555 assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({"id": "3"})).is_err());
1556 }
1557
1558 #[test]
1559 fn test_json_switch_to_frame_parameters_with_unknown_field() {
1560 let json = json!({
1561 "id":3,
1562 "foo": "bar",
1563 });
1564 let switch_to_frame = SwitchToFrameParameters {
1565 id: FrameId::Short(3),
1566 };
1567
1568 assert_de(&switch_to_frame, json);
1569 }
1570
1571 #[test]
1572 fn test_json_switch_to_window_parameters_with_value() {
1573 assert_de(
1574 &SwitchToWindowParameters {
1575 handle: "foo".into(),
1576 },
1577 json!({"handle": "foo"}),
1578 );
1579 }
1580
1581 #[test]
1582 fn test_json_switch_to_window_parameters_invalid_handle_field() {
1583 assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({"handle": 3})).is_err());
1584 }
1585
1586 #[test]
1587 fn test_json_switch_to_window_parameters_missing_handle_field() {
1588 assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({})).is_err());
1589 }
1590
1591 #[test]
1592 fn test_json_switch_to_window_parameters_with_unknown_field() {
1593 let json = json!({
1594 "handle": "foo",
1595 "foo": "bar",
1596 });
1597 let switch_to_window = SwitchToWindowParameters {
1598 handle: "foo".into(),
1599 };
1600
1601 assert_de(&switch_to_window, json);
1602 }
1603
1604 #[test]
1605 fn test_json_take_screenshot_parameters_with_element() {
1606 assert_de(
1607 &TakeScreenshotParameters {
1608 element: Some(WebElement("elem".into())),
1609 },
1610 json!({"element": {ELEMENT_KEY: "elem"}}),
1611 );
1612 }
1613
1614 #[test]
1615 fn test_json_take_screenshot_parameters_with_optional_null_field() {
1616 assert_de(
1617 &TakeScreenshotParameters { element: None },
1618 json!({ "element": null }),
1619 );
1620 }
1621
1622 #[test]
1623 fn test_json_take_screenshot_parameters_without_optional_null_field() {
1624 assert_de(&TakeScreenshotParameters { element: None }, json!({}));
1625 }
1626
1627 #[test]
1628 fn test_json_take_screenshot_parameters_with_invalid_element_field() {
1629 assert!(
1630 serde_json::from_value::<TakeScreenshotParameters>(json!({"element": "foo"})).is_err()
1631 );
1632 }
1633
1634 #[test]
1635 fn test_json_take_screenshot_parameters_with_unknown_field() {
1636 let json = json!({
1637 "element": {ELEMENT_KEY: "elem"},
1638 "foo": "bar",
1639 });
1640 let take_screenshot = TakeScreenshotParameters {
1641 element: Some(WebElement("elem".into())),
1642 };
1643
1644 assert_de(&take_screenshot, json);
1645 }
1646
1647 #[test]
1648 fn test_json_timeout_parameters_with_only_null_script_timeout() {
1649 let timeouts = TimeoutsParameters {
1650 implicit: None,
1651 page_load: None,
1652 script: Some(None),
1653 };
1654 assert_de(&timeouts, json!({ "script": null }));
1655 }
1656
1657 #[test]
1658 fn test_json_timeout_parameters_with_only_null_implicit_timeout() {
1659 assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "implicit": null })).is_err());
1660 }
1661
1662 #[test]
1663 fn test_json_timeout_parameters_with_only_null_pageload_timeout() {
1664 assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "pageLoad": null })).is_err());
1665 }
1666
1667 #[test]
1668 fn test_json_timeout_parameters_without_optional_null_field() {
1669 let timeouts = TimeoutsParameters {
1670 implicit: None,
1671 page_load: None,
1672 script: None,
1673 };
1674 assert_de(&timeouts, json!({}));
1675 }
1676
1677 #[test]
1678 fn test_json_timeout_parameters_with_unknown_field() {
1679 let json = json!({
1680 "script": 60000,
1681 "foo": "bar",
1682 });
1683 let timeouts = TimeoutsParameters {
1684 implicit: None,
1685 page_load: None,
1686 script: Some(Some(60000)),
1687 };
1688
1689 assert_de(&timeouts, json);
1690 }
1691
1692 #[test]
1693 fn test_json_window_rect_parameters_with_values() {
1694 let json = json!({
1695 "x": 0,
1696 "y": 1,
1697 "width": 2,
1698 "height": 3,
1699 });
1700 let rect = WindowRectParameters {
1701 x: Some(0i32),
1702 y: Some(1i32),
1703 width: Some(2i32),
1704 height: Some(3i32),
1705 };
1706
1707 assert_de(&rect, json);
1708 }
1709
1710 #[test]
1711 fn test_json_window_rect_parameters_with_optional_null_fields() {
1712 let json = json!({
1713 "x": null,
1714 "y": null,
1715 "width": null,
1716 "height": null,
1717 });
1718 let rect = WindowRectParameters {
1719 x: None,
1720 y: None,
1721 width: None,
1722 height: None,
1723 };
1724
1725 assert_de(&rect, json);
1726 }
1727
1728 #[test]
1729 fn test_json_window_rect_parameters_without_optional_fields() {
1730 let rect = WindowRectParameters {
1731 x: None,
1732 y: None,
1733 width: None,
1734 height: None,
1735 };
1736 assert_de(&rect, json!({}));
1737 }
1738
1739 #[test]
1740 fn test_json_window_rect_parameters_invalid_values_float() {
1741 let json = json!({
1742 "x": 1.1,
1743 "y": 2.2,
1744 "width": 3.3,
1745 "height": 4.4,
1746 });
1747 let rect = WindowRectParameters {
1748 x: Some(1),
1749 y: Some(2),
1750 width: Some(3),
1751 height: Some(4),
1752 };
1753
1754 assert_de(&rect, json);
1755 }
1756
1757 #[test]
1758 fn test_json_window_rect_parameters_with_unknown_field() {
1759 let json = json!({
1760 "x": 1.1,
1761 "y": 2.2,
1762 "foo": "bar",
1763 });
1764 let rect = WindowRectParameters {
1765 x: Some(1),
1766 y: Some(2),
1767 width: None,
1768 height: None,
1769 };
1770
1771 assert_de(&rect, json);
1772 }
1773}