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 LegacyWebElement { 17 pub id: String, 18 } 19 20 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 21 pub struct Locator { 22 pub using: Selector, 23 pub value: String, 24 } 25 26 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 27 pub enum Selector { 28 #[serde(rename = "css selector")] 29 Css, 30 #[serde(rename = "link text")] 31 LinkText, 32 #[serde(rename = "partial link text")] 33 PartialLinkText, 34 #[serde(rename = "tag name")] 35 TagName, 36 #[serde(rename = "xpath")] 37 XPath, 38 } 39 40 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 41 pub struct NewWindow { 42 #[serde(rename = "type", skip_serializing_if = "Option::is_none")] 43 pub type_hint: Option<String>, 44 } 45 46 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 47 pub struct WindowRect { 48 #[serde(default, skip_serializing_if = "Option::is_none")] 49 pub x: Option<i32>, 50 #[serde(default, skip_serializing_if = "Option::is_none")] 51 pub y: Option<i32>, 52 #[serde(default, skip_serializing_if = "Option::is_none")] 53 pub width: Option<i32>, 54 #[serde(default, skip_serializing_if = "Option::is_none")] 55 pub height: Option<i32>, 56 } 57 58 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 59 pub struct Keys { 60 pub text: String, 61 pub value: Vec<String>, 62 } 63 64 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 65 #[serde(default, rename_all = "camelCase")] 66 pub struct PrintParameters { 67 pub orientation: PrintOrientation, 68 pub scale: f64, 69 pub background: bool, 70 pub page: PrintPage, 71 pub margin: PrintMargins, 72 pub page_ranges: Vec<String>, 73 pub shrink_to_fit: bool, 74 } 75 76 impl Default for PrintParameters { default() -> Self77 fn default() -> Self { 78 PrintParameters { 79 orientation: PrintOrientation::default(), 80 scale: 1.0, 81 background: false, 82 page: PrintPage::default(), 83 margin: PrintMargins::default(), 84 page_ranges: Vec::new(), 85 shrink_to_fit: true, 86 } 87 } 88 } 89 90 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 91 #[serde(rename_all = "lowercase")] 92 pub enum PrintOrientation { 93 Landscape, 94 Portrait, 95 } 96 97 impl Default for PrintOrientation { default() -> Self98 fn default() -> Self { 99 PrintOrientation::Portrait 100 } 101 } 102 103 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 104 pub struct PrintPage { 105 pub width: f64, 106 pub height: f64, 107 } 108 109 impl Default for PrintPage { default() -> Self110 fn default() -> Self { 111 PrintPage { 112 width: 21.59, 113 height: 27.94, 114 } 115 } 116 } 117 118 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 119 pub struct PrintMargins { 120 pub top: f64, 121 pub bottom: f64, 122 pub left: f64, 123 pub right: f64, 124 } 125 126 impl Default for PrintMargins { default() -> Self127 fn default() -> Self { 128 PrintMargins { 129 top: 1.0, 130 bottom: 1.0, 131 left: 1.0, 132 right: 1.0, 133 } 134 } 135 } 136 137 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 138 pub struct ScreenshotOptions { 139 pub id: Option<String>, 140 pub highlights: Vec<Option<String>>, 141 pub full: bool, 142 } 143 144 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 145 pub struct Script { 146 pub script: String, 147 pub args: Option<Vec<Value>>, 148 } 149 150 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 151 pub enum Command { 152 #[serde(rename = "WebDriver:AcceptAlert")] 153 AcceptAlert, 154 #[serde( 155 rename = "WebDriver:AddCookie", 156 serialize_with = "to_cookie", 157 deserialize_with = "from_cookie" 158 )] 159 AddCookie(Cookie), 160 #[serde(rename = "WebDriver:CloseWindow")] 161 CloseWindow, 162 #[serde( 163 rename = "WebDriver:DeleteCookie", 164 serialize_with = "to_name", 165 deserialize_with = "from_name" 166 )] 167 DeleteCookie(String), 168 #[serde(rename = "WebDriver:DeleteAllCookies")] 169 DeleteCookies, 170 #[serde(rename = "WebDriver:DeleteSession")] 171 DeleteSession, 172 #[serde(rename = "WebDriver:DismissAlert")] 173 DismissAlert, 174 #[serde(rename = "WebDriver:ElementClear")] 175 ElementClear(LegacyWebElement), 176 #[serde(rename = "WebDriver:ElementClick")] 177 ElementClick(LegacyWebElement), 178 #[serde(rename = "WebDriver:ElementSendKeys")] 179 ElementSendKeys { 180 id: String, 181 text: String, 182 value: Vec<String>, 183 }, 184 #[serde(rename = "WebDriver:ExecuteAsyncScript")] 185 ExecuteAsyncScript(Script), 186 #[serde(rename = "WebDriver:ExecuteScript")] 187 ExecuteScript(Script), 188 #[serde(rename = "WebDriver:FindElement")] 189 FindElement(Locator), 190 #[serde(rename = "WebDriver:FindElements")] 191 FindElements(Locator), 192 #[serde(rename = "WebDriver:FindElement")] 193 FindElementElement { 194 element: String, 195 using: Selector, 196 value: String, 197 }, 198 #[serde(rename = "WebDriver:FindElements")] 199 FindElementElements { 200 element: String, 201 using: Selector, 202 value: String, 203 }, 204 #[serde(rename = "WebDriver:FullscreenWindow")] 205 FullscreenWindow, 206 #[serde(rename = "WebDriver:Navigate")] 207 Get(Url), 208 #[serde(rename = "WebDriver:GetActiveElement")] 209 GetActiveElement, 210 #[serde(rename = "WebDriver:GetAlertText")] 211 GetAlertText, 212 #[serde(rename = "WebDriver:GetCookies")] 213 GetCookies, 214 #[serde(rename = "WebDriver:GetElementCSSValue")] 215 GetCSSValue { 216 id: String, 217 #[serde(rename = "propertyName")] 218 property: String, 219 }, 220 #[serde(rename = "WebDriver:GetCurrentURL")] 221 GetCurrentUrl, 222 #[serde(rename = "WebDriver:GetElementAttribute")] 223 GetElementAttribute { id: String, name: String }, 224 #[serde(rename = "WebDriver:GetElementProperty")] 225 GetElementProperty { id: String, name: String }, 226 #[serde(rename = "WebDriver:GetElementRect")] 227 GetElementRect(LegacyWebElement), 228 #[serde(rename = "WebDriver:GetElementTagName")] 229 GetElementTagName(LegacyWebElement), 230 #[serde(rename = "WebDriver:GetElementText")] 231 GetElementText(LegacyWebElement), 232 #[serde(rename = "WebDriver:GetPageSource")] 233 GetPageSource, 234 #[serde(rename = "WebDriver:GetTimeouts")] 235 GetTimeouts, 236 #[serde(rename = "WebDriver:GetTitle")] 237 GetTitle, 238 #[serde(rename = "WebDriver:GetWindowHandle")] 239 GetWindowHandle, 240 #[serde(rename = "WebDriver:GetWindowHandles")] 241 GetWindowHandles, 242 #[serde(rename = "WebDriver:GetWindowRect")] 243 GetWindowRect, 244 #[serde(rename = "WebDriver:Back")] 245 GoBack, 246 #[serde(rename = "WebDriver:Forward")] 247 GoForward, 248 #[serde(rename = "WebDriver:IsElementDisplayed")] 249 IsDisplayed(LegacyWebElement), 250 #[serde(rename = "WebDriver:IsElementEnabled")] 251 IsEnabled(LegacyWebElement), 252 #[serde(rename = "WebDriver:IsElementSelected")] 253 IsSelected(LegacyWebElement), 254 #[serde(rename = "WebDriver:MaximizeWindow")] 255 MaximizeWindow, 256 #[serde(rename = "WebDriver:MinimizeWindow")] 257 MinimizeWindow, 258 #[serde(rename = "WebDriver:NewWindow")] 259 NewWindow(NewWindow), 260 #[serde(rename = "WebDriver:Print")] 261 Print(PrintParameters), 262 #[serde(rename = "WebDriver:Refresh")] 263 Refresh, 264 #[serde(rename = "WebDriver:ReleaseActions")] 265 ReleaseActions, 266 #[serde(rename = "WebDriver:SendAlertText")] 267 SendAlertText(Keys), 268 #[serde(rename = "WebDriver:SetTimeouts")] 269 SetTimeouts(Timeouts), 270 #[serde(rename = "WebDriver:SetWindowRect")] 271 SetWindowRect(WindowRect), 272 #[serde(rename = "WebDriver:SwitchToFrame")] 273 SwitchToFrame(Frame), 274 #[serde(rename = "WebDriver:SwitchToParentFrame")] 275 SwitchToParentFrame, 276 #[serde(rename = "WebDriver:SwitchToWindow")] 277 SwitchToWindow(Window), 278 #[serde(rename = "WebDriver:TakeScreenshot")] 279 TakeElementScreenshot(ScreenshotOptions), 280 #[serde(rename = "WebDriver:TakeScreenshot")] 281 TakeFullScreenshot(ScreenshotOptions), 282 #[serde(rename = "WebDriver:TakeScreenshot")] 283 TakeScreenshot(ScreenshotOptions), 284 } 285 286 #[cfg(test)] 287 mod tests { 288 use super::*; 289 use crate::common::Date; 290 use crate::test::{assert_ser, assert_ser_de}; 291 use serde_json::json; 292 293 #[test] test_json_screenshot()294 fn test_json_screenshot() { 295 let data = ScreenshotOptions { 296 id: None, 297 highlights: vec![], 298 full: false, 299 }; 300 let json = json!({"full":false,"highlights":[],"id":null}); 301 assert_ser_de(&data, json); 302 } 303 304 #[test] test_json_selector_css()305 fn test_json_selector_css() { 306 assert_ser_de(&Selector::Css, json!("css selector")); 307 } 308 309 #[test] test_json_selector_link_text()310 fn test_json_selector_link_text() { 311 assert_ser_de(&Selector::LinkText, json!("link text")); 312 } 313 314 #[test] test_json_selector_partial_link_text()315 fn test_json_selector_partial_link_text() { 316 assert_ser_de(&Selector::PartialLinkText, json!("partial link text")); 317 } 318 319 #[test] test_json_selector_tag_name()320 fn test_json_selector_tag_name() { 321 assert_ser_de(&Selector::TagName, json!("tag name")); 322 } 323 324 #[test] test_json_selector_xpath()325 fn test_json_selector_xpath() { 326 assert_ser_de(&Selector::XPath, json!("xpath")); 327 } 328 329 #[test] test_json_selector_invalid()330 fn test_json_selector_invalid() { 331 assert!(serde_json::from_value::<Selector>(json!("foo")).is_err()); 332 } 333 334 #[test] test_json_locator()335 fn test_json_locator() { 336 let json = json!({ 337 "using": "partial link text", 338 "value": "link text", 339 }); 340 let data = Locator { 341 using: Selector::PartialLinkText, 342 value: "link text".into(), 343 }; 344 345 assert_ser_de(&data, json); 346 } 347 348 #[test] test_json_keys()349 fn test_json_keys() { 350 let data = Keys { 351 text: "Foo".into(), 352 value: vec!["F".into(), "o".into(), "o".into()], 353 }; 354 let json = json!({"text": "Foo", "value": ["F", "o", "o"]}); 355 assert_ser_de(&data, json); 356 } 357 358 #[test] test_json_new_window()359 fn test_json_new_window() { 360 let data = NewWindow { 361 type_hint: Some("foo".into()), 362 }; 363 assert_ser_de(&data, json!({ "type": "foo" })); 364 } 365 366 #[test] test_json_window_rect()367 fn test_json_window_rect() { 368 let data = WindowRect { 369 x: Some(123), 370 y: None, 371 width: None, 372 height: None, 373 }; 374 assert_ser_de(&data, json!({"x": 123})); 375 } 376 377 #[test] test_command_with_params()378 fn test_command_with_params() { 379 let locator = Locator { 380 using: Selector::Css, 381 value: "value".into(), 382 }; 383 let json = json!({"WebDriver:FindElement": {"using": "css selector", "value": "value"}}); 384 assert_ser_de(&Command::FindElement(locator), json); 385 } 386 387 #[test] test_command_with_wrapper_params()388 fn test_command_with_wrapper_params() { 389 let cookie = Cookie { 390 name: "hello".into(), 391 value: "world".into(), 392 path: None, 393 domain: None, 394 secure: false, 395 http_only: false, 396 expiry: Some(Date(1564488092)), 397 same_site: None, 398 }; 399 let json = json!({"WebDriver:AddCookie": {"cookie": {"name": "hello", "value": "world", "secure": false, "httpOnly": false, "expiry": 1564488092}}}); 400 assert_ser_de(&Command::AddCookie(cookie), json); 401 } 402 403 #[test] test_empty_commands()404 fn test_empty_commands() { 405 assert_ser_de(&Command::GetTimeouts, json!("WebDriver:GetTimeouts")); 406 } 407 408 #[test] test_json_command_invalid()409 fn test_json_command_invalid() { 410 assert!(serde_json::from_value::<Command>(json!("foo")).is_err()); 411 } 412 413 #[test] test_json_delete_cookie_command()414 fn test_json_delete_cookie_command() { 415 let json = json!({"WebDriver:DeleteCookie": {"name": "foo"}}); 416 assert_ser_de(&Command::DeleteCookie("foo".into()), json); 417 } 418 419 #[test] test_json_new_window_command()420 fn test_json_new_window_command() { 421 let data = NewWindow { 422 type_hint: Some("foo".into()), 423 }; 424 let json = json!({"WebDriver:NewWindow": {"type": "foo"}}); 425 assert_ser_de(&Command::NewWindow(data), json); 426 } 427 428 #[test] test_json_new_window_command_with_none_value()429 fn test_json_new_window_command_with_none_value() { 430 let data = NewWindow { type_hint: None }; 431 let json = json!({"WebDriver:NewWindow": {}}); 432 assert_ser_de(&Command::NewWindow(data), json); 433 } 434 435 #[test] test_json_command_as_struct()436 fn test_json_command_as_struct() { 437 assert_ser( 438 &Command::FindElementElement { 439 element: "foo".into(), 440 using: Selector::XPath, 441 value: "bar".into(), 442 }, 443 json!({"WebDriver:FindElement": {"element": "foo", "using": "xpath", "value": "bar" }}), 444 ); 445 } 446 447 #[test] test_json_get_css_value()448 fn test_json_get_css_value() { 449 assert_ser_de( 450 &Command::GetCSSValue { 451 id: "foo".into(), 452 property: "bar".into(), 453 }, 454 json!({"WebDriver:GetElementCSSValue": {"id": "foo", "propertyName": "bar"}}), 455 ); 456 } 457 } 458