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