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