Skip to main content

webdriver/
command.rs

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