webdriver.rs (17503B)
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 serde::{Deserialize, Serialize}; 6 use serde_json::Value; 7 8 use crate::common::{from_cookie, from_name, to_cookie, to_name, Cookie, Frame, Timeouts, Window}; 9 10 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 11 pub struct Url { 12 pub url: String, 13 } 14 15 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 16 pub struct Locator { 17 #[serde(skip_serializing_if = "Option::is_none")] 18 pub element: Option<String>, 19 pub using: Selector, 20 pub value: String, 21 } 22 23 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 24 pub enum Selector { 25 #[serde(rename = "css selector")] 26 Css, 27 #[serde(rename = "link text")] 28 LinkText, 29 #[serde(rename = "partial link text")] 30 PartialLinkText, 31 #[serde(rename = "tag name")] 32 TagName, 33 #[serde(rename = "xpath")] 34 XPath, 35 } 36 37 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 38 pub struct NewWindow { 39 #[serde(rename = "type", skip_serializing_if = "Option::is_none")] 40 pub type_hint: Option<String>, 41 } 42 43 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 44 pub struct WindowRect { 45 #[serde(default, skip_serializing_if = "Option::is_none")] 46 pub x: Option<i32>, 47 #[serde(default, skip_serializing_if = "Option::is_none")] 48 pub y: Option<i32>, 49 #[serde(default, skip_serializing_if = "Option::is_none")] 50 pub width: Option<i32>, 51 #[serde(default, skip_serializing_if = "Option::is_none")] 52 pub height: Option<i32>, 53 } 54 55 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 56 pub struct Keys { 57 pub text: String, 58 pub value: Vec<String>, 59 } 60 61 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 62 #[serde(untagged)] 63 pub enum PrintPageRange { 64 Integer(u64), 65 Range(String), 66 } 67 68 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 69 #[serde(default, rename_all = "camelCase")] 70 pub struct PrintParameters { 71 pub orientation: PrintOrientation, 72 pub scale: f64, 73 pub background: bool, 74 pub page: PrintPage, 75 pub margin: PrintMargins, 76 pub page_ranges: Vec<PrintPageRange>, 77 pub shrink_to_fit: bool, 78 } 79 80 impl Default for PrintParameters { 81 fn default() -> Self { 82 PrintParameters { 83 orientation: PrintOrientation::default(), 84 scale: 1.0, 85 background: false, 86 page: PrintPage::default(), 87 margin: PrintMargins::default(), 88 page_ranges: Vec::new(), 89 shrink_to_fit: true, 90 } 91 } 92 } 93 94 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] 95 #[serde(rename_all = "lowercase")] 96 pub enum PrintOrientation { 97 Landscape, 98 #[default] 99 Portrait, 100 } 101 102 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 103 pub struct PrintPage { 104 pub width: f64, 105 pub height: f64, 106 } 107 108 impl Default for PrintPage { 109 fn default() -> Self { 110 PrintPage { 111 width: 21.59, 112 height: 27.94, 113 } 114 } 115 } 116 117 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 118 pub struct PrintMargins { 119 pub top: f64, 120 pub bottom: f64, 121 pub left: f64, 122 pub right: f64, 123 } 124 125 impl Default for PrintMargins { 126 fn default() -> Self { 127 PrintMargins { 128 top: 1.0, 129 bottom: 1.0, 130 left: 1.0, 131 right: 1.0, 132 } 133 } 134 } 135 136 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 137 pub struct SetPermissionParameters { 138 pub descriptor: SetPermissionDescriptor, 139 pub state: SetPermissionState, 140 } 141 142 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 143 pub struct SetPermissionDescriptor { 144 pub name: String, 145 } 146 147 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 148 #[serde(rename_all = "lowercase")] 149 pub enum SetPermissionState { 150 Denied, 151 Granted, 152 Prompt, 153 } 154 155 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 156 pub enum WebAuthnProtocol { 157 #[serde(rename = "ctap1/u2f")] 158 Ctap1U2f, 159 #[serde(rename = "ctap2")] 160 Ctap2, 161 #[serde(rename = "ctap2_1")] 162 Ctap2_1, 163 } 164 165 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 166 #[serde(rename_all = "kebab-case")] 167 pub enum AuthenticatorTransport { 168 Usb, 169 Nfc, 170 Ble, 171 SmartCard, 172 Hybrid, 173 Internal, 174 } 175 176 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 177 #[serde(rename_all = "camelCase")] 178 pub struct AuthenticatorParameters { 179 pub protocol: WebAuthnProtocol, 180 pub transport: AuthenticatorTransport, 181 pub has_resident_key: bool, 182 pub has_user_verification: bool, 183 pub is_user_consenting: bool, 184 pub is_user_verified: bool, 185 } 186 187 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 188 #[serde(rename_all = "camelCase")] 189 pub struct CredentialParameters { 190 pub credential_id: String, 191 pub is_resident_credential: bool, 192 pub rp_id: String, 193 pub private_key: String, 194 pub user_handle: String, 195 pub sign_count: u64, 196 } 197 198 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 199 #[serde(rename_all = "camelCase")] 200 pub struct UserVerificationParameters { 201 pub is_user_verified: bool, 202 } 203 204 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 205 #[serde(rename_all = "camelCase")] 206 pub struct GlobalPrivacyControlParameters { 207 pub gpc: bool, 208 } 209 210 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 211 pub struct ScreenshotOptions { 212 pub id: Option<String>, 213 pub highlights: Vec<Option<String>>, 214 pub full: bool, 215 } 216 217 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 218 pub struct Script { 219 pub script: String, 220 pub args: Option<Vec<Value>>, 221 } 222 223 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 224 pub enum Command { 225 #[serde(rename = "WebDriver:AcceptAlert")] 226 AcceptAlert, 227 #[serde( 228 rename = "WebDriver:AddCookie", 229 serialize_with = "to_cookie", 230 deserialize_with = "from_cookie" 231 )] 232 AddCookie(Cookie), 233 #[serde(rename = "WebDriver:CloseWindow")] 234 CloseWindow, 235 #[serde( 236 rename = "WebDriver:DeleteCookie", 237 serialize_with = "to_name", 238 deserialize_with = "from_name" 239 )] 240 DeleteCookie(String), 241 #[serde(rename = "WebDriver:DeleteAllCookies")] 242 DeleteCookies, 243 #[serde(rename = "WebDriver:DeleteSession")] 244 DeleteSession, 245 #[serde(rename = "WebDriver:DismissAlert")] 246 DismissAlert, 247 #[serde(rename = "WebDriver:ElementClear")] 248 ElementClear { id: String }, 249 #[serde(rename = "WebDriver:ElementClick")] 250 ElementClick { id: String }, 251 #[serde(rename = "WebDriver:ElementSendKeys")] 252 ElementSendKeys { 253 id: String, 254 text: String, 255 value: Vec<String>, 256 }, 257 #[serde(rename = "WebDriver:ExecuteAsyncScript")] 258 ExecuteAsyncScript(Script), 259 #[serde(rename = "WebDriver:ExecuteScript")] 260 ExecuteScript(Script), 261 #[serde(rename = "WebDriver:FindElement")] 262 FindElement(Locator), 263 #[serde(rename = "WebDriver:FindElements")] 264 FindElements(Locator), 265 #[serde(rename = "WebDriver:FindElementFromShadowRoot")] 266 FindShadowRootElement { 267 #[serde(rename = "shadowRoot")] 268 shadow_root: String, 269 using: Selector, 270 value: String, 271 }, 272 #[serde(rename = "WebDriver:FindElementsFromShadowRoot")] 273 FindShadowRootElements { 274 #[serde(rename = "shadowRoot")] 275 shadow_root: String, 276 using: Selector, 277 value: String, 278 }, 279 #[serde(rename = "WebDriver:FullscreenWindow")] 280 FullscreenWindow, 281 #[serde(rename = "WebDriver:Navigate")] 282 Get(Url), 283 #[serde(rename = "WebDriver:GetActiveElement")] 284 GetActiveElement, 285 #[serde(rename = "WebDriver:GetAlertText")] 286 GetAlertText, 287 #[serde(rename = "WebDriver:GetComputedLabel")] 288 GetComputedLabel { id: String }, 289 #[serde(rename = "WebDriver:GetComputedRole")] 290 GetComputedRole { id: String }, 291 #[serde(rename = "WebDriver:GetCookies")] 292 GetCookies, 293 #[serde(rename = "WebDriver:GetElementCSSValue")] 294 GetCSSValue { 295 id: String, 296 #[serde(rename = "propertyName")] 297 property: String, 298 }, 299 #[serde(rename = "WebDriver:GetCurrentURL")] 300 GetCurrentUrl, 301 #[serde(rename = "WebDriver:GetElementAttribute")] 302 GetElementAttribute { id: String, name: String }, 303 #[serde(rename = "WebDriver:GetElementProperty")] 304 GetElementProperty { id: String, name: String }, 305 #[serde(rename = "WebDriver:GetElementRect")] 306 GetElementRect { id: String }, 307 #[serde(rename = "WebDriver:GetElementTagName")] 308 GetElementTagName { id: String }, 309 #[serde(rename = "WebDriver:GetElementText")] 310 GetElementText { id: String }, 311 #[serde(rename = "WebDriver:GetPageSource")] 312 GetPageSource, 313 #[serde(rename = "WebDriver:GetShadowRoot")] 314 GetShadowRoot { id: String }, 315 #[serde(rename = "WebDriver:GetTimeouts")] 316 GetTimeouts, 317 #[serde(rename = "WebDriver:GetTitle")] 318 GetTitle, 319 #[serde(rename = "WebDriver:GetWindowHandle")] 320 GetWindowHandle, 321 #[serde(rename = "WebDriver:GetWindowHandles")] 322 GetWindowHandles, 323 #[serde(rename = "WebDriver:GetWindowRect")] 324 GetWindowRect, 325 #[serde(rename = "WebDriver:Back")] 326 GoBack, 327 #[serde(rename = "WebDriver:Forward")] 328 GoForward, 329 #[serde(rename = "WebDriver:IsElementDisplayed")] 330 IsDisplayed { id: String }, 331 #[serde(rename = "WebDriver:IsElementEnabled")] 332 IsEnabled { id: String }, 333 #[serde(rename = "WebDriver:IsElementSelected")] 334 IsSelected { id: String }, 335 #[serde(rename = "WebDriver:MaximizeWindow")] 336 MaximizeWindow, 337 #[serde(rename = "WebDriver:MinimizeWindow")] 338 MinimizeWindow, 339 #[serde(rename = "WebDriver:NewWindow")] 340 NewWindow(NewWindow), 341 #[serde(rename = "WebDriver:Print")] 342 Print(PrintParameters), 343 #[serde(rename = "WebDriver:Refresh")] 344 Refresh, 345 #[serde(rename = "WebDriver:ReleaseActions")] 346 ReleaseActions, 347 #[serde(rename = "WebDriver:SendAlertText")] 348 SendAlertText(Keys), 349 #[serde(rename = "WebDriver:SetPermission")] 350 SetPermission(SetPermissionParameters), 351 #[serde(rename = "WebDriver:SetTimeouts")] 352 SetTimeouts(Timeouts), 353 #[serde(rename = "WebDriver:SetWindowRect")] 354 SetWindowRect(WindowRect), 355 #[serde(rename = "WebDriver:SwitchToFrame")] 356 SwitchToFrame(Frame), 357 #[serde(rename = "WebDriver:SwitchToParentFrame")] 358 SwitchToParentFrame, 359 #[serde(rename = "WebDriver:SwitchToWindow")] 360 SwitchToWindow(Window), 361 #[serde(rename = "WebDriver:TakeScreenshot")] 362 TakeScreenshot(ScreenshotOptions), 363 #[serde(rename = "GPC:GetGlobalPrivacyControl")] 364 GPCGetGlobalPrivacyControl, 365 #[serde(rename = "GPC:SetGlobalPrivacyControl")] 366 GPCSetGlobalPrivacyControl(GlobalPrivacyControlParameters), 367 #[serde(rename = "WebAuthn:AddVirtualAuthenticator")] 368 WebAuthnAddVirtualAuthenticator(AuthenticatorParameters), 369 #[serde(rename = "WebAuthn:RemoveVirtualAuthenticator")] 370 WebAuthnRemoveVirtualAuthenticator, 371 #[serde(rename = "WebAuthn:AddCredential")] 372 WebAuthnAddCredential(CredentialParameters), 373 #[serde(rename = "WebAuthn:GetCredentials")] 374 WebAuthnGetCredentials, 375 #[serde(rename = "WebAuthn:RemoveCredential")] 376 WebAuthnRemoveCredential, 377 #[serde(rename = "WebAuthn:RemoveAllCredentials")] 378 WebAuthnRemoveAllCredentials, 379 #[serde(rename = "WebAuthn:SetUserVerified")] 380 WebAuthnSetUserVerified(UserVerificationParameters), 381 } 382 383 #[cfg(test)] 384 mod tests { 385 use super::*; 386 use crate::common::Date; 387 use crate::test::{assert_ser, assert_ser_de}; 388 use serde_json::json; 389 390 #[test] 391 fn test_json_screenshot() { 392 let data = ScreenshotOptions { 393 id: None, 394 highlights: vec![], 395 full: false, 396 }; 397 let json = json!({"full":false,"highlights":[],"id":null}); 398 assert_ser_de(&data, json); 399 } 400 401 #[test] 402 fn test_json_selector_css() { 403 assert_ser_de(&Selector::Css, json!("css selector")); 404 } 405 406 #[test] 407 fn test_json_selector_link_text() { 408 assert_ser_de(&Selector::LinkText, json!("link text")); 409 } 410 411 #[test] 412 fn test_json_selector_partial_link_text() { 413 assert_ser_de(&Selector::PartialLinkText, json!("partial link text")); 414 } 415 416 #[test] 417 fn test_json_selector_tag_name() { 418 assert_ser_de(&Selector::TagName, json!("tag name")); 419 } 420 421 #[test] 422 fn test_json_selector_xpath() { 423 assert_ser_de(&Selector::XPath, json!("xpath")); 424 } 425 426 #[test] 427 fn test_json_selector_invalid() { 428 assert!(serde_json::from_value::<Selector>(json!("foo")).is_err()); 429 } 430 431 #[test] 432 fn test_json_locator() { 433 let json = json!({ 434 "using": "partial link text", 435 "value": "link text", 436 }); 437 let data = Locator { 438 element: None, 439 using: Selector::PartialLinkText, 440 value: "link text".into(), 441 }; 442 443 assert_ser_de(&data, json); 444 } 445 446 #[test] 447 fn test_json_keys() { 448 let data = Keys { 449 text: "Foo".into(), 450 value: vec!["F".into(), "o".into(), "o".into()], 451 }; 452 let json = json!({"text": "Foo", "value": ["F", "o", "o"]}); 453 assert_ser_de(&data, json); 454 } 455 456 #[test] 457 fn test_json_new_window() { 458 let data = NewWindow { 459 type_hint: Some("foo".into()), 460 }; 461 assert_ser_de(&data, json!({ "type": "foo" })); 462 } 463 464 #[test] 465 fn test_json_window_rect() { 466 let data = WindowRect { 467 x: Some(123), 468 y: None, 469 width: None, 470 height: None, 471 }; 472 assert_ser_de(&data, json!({"x": 123})); 473 } 474 475 #[test] 476 fn test_command_with_params() { 477 let locator = Locator { 478 element: None, 479 using: Selector::Css, 480 value: "value".into(), 481 }; 482 let json = json!({"WebDriver:FindElement": {"using": "css selector", "value": "value"}}); 483 assert_ser_de(&Command::FindElement(locator), json); 484 } 485 486 #[test] 487 fn test_command_with_wrapper_params() { 488 let cookie = Cookie { 489 name: "hello".into(), 490 value: "world".into(), 491 path: None, 492 domain: None, 493 secure: false, 494 http_only: false, 495 expiry: Some(Date(1564488092)), 496 same_site: None, 497 }; 498 let json = json!({"WebDriver:AddCookie": {"cookie": {"name": "hello", "value": "world", "secure": false, "httpOnly": false, "expiry": 1564488092}}}); 499 assert_ser_de(&Command::AddCookie(cookie), json); 500 } 501 502 #[test] 503 fn test_empty_commands() { 504 assert_ser_de(&Command::GetTimeouts, json!("WebDriver:GetTimeouts")); 505 } 506 507 #[test] 508 fn test_json_command_invalid() { 509 assert!(serde_json::from_value::<Command>(json!("foo")).is_err()); 510 } 511 512 #[test] 513 fn test_json_delete_cookie_command() { 514 let json = json!({"WebDriver:DeleteCookie": {"name": "foo"}}); 515 assert_ser_de(&Command::DeleteCookie("foo".into()), json); 516 } 517 518 #[test] 519 fn test_json_new_window_command() { 520 let data = NewWindow { 521 type_hint: Some("foo".into()), 522 }; 523 let json = json!({"WebDriver:NewWindow": {"type": "foo"}}); 524 assert_ser_de(&Command::NewWindow(data), json); 525 } 526 527 #[test] 528 fn test_json_new_window_command_with_none_value() { 529 let data = NewWindow { type_hint: None }; 530 let json = json!({"WebDriver:NewWindow": {}}); 531 assert_ser_de(&Command::NewWindow(data), json); 532 } 533 534 #[test] 535 fn test_json_command_as_struct() { 536 assert_ser( 537 &Command::FindElement(Locator { 538 element: Some("foo".into()), 539 using: Selector::XPath, 540 value: "bar".into(), 541 }), 542 json!({"WebDriver:FindElement": {"element": "foo", "using": "xpath", "value": "bar" }}), 543 ); 544 } 545 546 #[test] 547 fn test_json_get_computed_label_command() { 548 assert_ser_de( 549 &Command::GetComputedLabel { id: "foo".into() }, 550 json!({"WebDriver:GetComputedLabel": {"id": "foo"}}), 551 ); 552 } 553 554 #[test] 555 fn test_json_get_computed_role_command() { 556 assert_ser_de( 557 &Command::GetComputedRole { id: "foo".into() }, 558 json!({"WebDriver:GetComputedRole": {"id": "foo"}}), 559 ); 560 } 561 562 #[test] 563 fn test_json_get_css_value() { 564 assert_ser_de( 565 &Command::GetCSSValue { 566 id: "foo".into(), 567 property: "bar".into(), 568 }, 569 json!({"WebDriver:GetElementCSSValue": {"id": "foo", "propertyName": "bar"}}), 570 ); 571 } 572 573 #[test] 574 fn test_json_find_shadow_root_element() { 575 assert_ser_de( 576 &Command::FindShadowRootElement { 577 shadow_root: "foo".into(), 578 using: Selector::Css, 579 value: "bar".into(), 580 }, 581 json!({"WebDriver:FindElementFromShadowRoot": {"shadowRoot": "foo", "using": "css selector", "value": "bar"}}), 582 ); 583 } 584 585 #[test] 586 fn test_json_find_shadow_root_elements() { 587 assert_ser_de( 588 &Command::FindShadowRootElements { 589 shadow_root: "foo".into(), 590 using: Selector::Css, 591 value: "bar".into(), 592 }, 593 json!({"WebDriver:FindElementsFromShadowRoot": {"shadowRoot": "foo", "using": "css selector", "value": "bar"}}), 594 ); 595 } 596 }