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