Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  command.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::actions::ActionSequence;
use crate::capabilities::{
    BrowserCapabilities, Capabilities, CapabilitiesMatching, SpecNewSessionParameters,
};
use crate::common::{
    CredentialParameters, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, MAX_SAFE_INTEGER,
};
use crate::error::{ErrorStatus, WebDriverError, WebDriverResult};
use crate::httpapi::{Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute};
use crate::Parameters;
use serde::de::{self, Deserialize, Deserializer};
use serde_json::Value;

#[derive(Debug, PartialEq)]
pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
    NewSession(NewSessionParameters),
    DeleteSession,
    Get(GetParameters),
    GetCurrentUrl,
    GoBack,
    GoForward,
    Refresh,
    GetTitle,
    GetPageSource,
    GetWindowHandle,
    GetWindowHandles,
    NewWindow(NewWindowParameters),
    CloseWindow,
    GetWindowRect,
    SetWindowRect(WindowRectParameters),
    MinimizeWindow,
    MaximizeWindow,
    FullscreenWindow,
    SwitchToWindow(SwitchToWindowParameters),
    SwitchToFrame(SwitchToFrameParameters),
    SwitchToParentFrame,
    FindElement(LocatorParameters),
    FindElements(LocatorParameters),
    FindElementElement(WebElement, LocatorParameters),
    FindElementElements(WebElement, LocatorParameters),
    FindShadowRootElement(ShadowRoot, LocatorParameters),
    FindShadowRootElements(ShadowRoot, LocatorParameters),
    GetActiveElement,
    GetComputedLabel(WebElement),
    GetComputedRole(WebElement),
    GetShadowRoot(WebElement),
    IsDisplayed(WebElement),
    IsSelected(WebElement),
    GetElementAttribute(WebElement, String),
    GetElementProperty(WebElement, String),
    GetCSSValue(WebElement, String),
    GetElementText(WebElement),
    GetElementTagName(WebElement),
    GetElementRect(WebElement),
    IsEnabled(WebElement),
    ExecuteScript(JavascriptCommandParameters),
    ExecuteAsyncScript(JavascriptCommandParameters),
    GetCookies,
    GetNamedCookie(String),
    AddCookie(AddCookieParameters),
    DeleteCookies,
    DeleteCookie(String),
    GetTimeouts,
    SetTimeouts(TimeoutsParameters),
    ElementClick(WebElement),
    ElementClear(WebElement),
    ElementSendKeys(WebElement, SendKeysParameters),
    PerformActions(ActionsParameters),
    ReleaseActions,
    DismissAlert,
    AcceptAlert,
    GetAlertText,
    SendAlertText(SendKeysParameters),
    TakeScreenshot,
    TakeElementScreenshot(WebElement),
    Print(PrintParameters),
    SetPermission(SetPermissionParameters),
    Status,
    Extension(T),
    WebAuthnAddVirtualAuthenticator(AuthenticatorParameters),
    WebAuthnRemoveVirtualAuthenticator,
    WebAuthnAddCredential(CredentialParameters),
    WebAuthnGetCredentials,
    WebAuthnRemoveCredential,
    WebAuthnRemoveAllCredentials,
    WebAuthnSetUserVerified(UserVerificationParameters),
}

pub trait WebDriverExtensionCommand: Clone + Send {
    fn parameters_json(&self) -> Option<Value>;
}

#[derive(Clone, Debug)]
pub struct VoidWebDriverExtensionCommand;

impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
    fn parameters_json(&self) -> Option<Value> {
        panic!("No extensions implemented");
    }
}

#[derive(Debug, PartialEq)]
pub struct WebDriverMessage<U: WebDriverExtensionRoute = VoidWebDriverExtensionRoute> {
    pub session_id: Option<String>,
    pub command: WebDriverCommand<U::Command>,
}

impl<U: WebDriverExtensionRoute> WebDriverMessage<U> {
    pub fn new(
        session_id: Option<String>,
        command: WebDriverCommand<U::Command>,
    ) -> WebDriverMessage<U> {
        WebDriverMessage {
            session_id,
            command,
        }
    }

    pub fn from_http(
        match_type: Route<U>,
        params: &Parameters,
        raw_body: &str,
        requires_body: bool,
    ) -> WebDriverResult<WebDriverMessage<U>> {
        let session_id = WebDriverMessage::<U>::get_session_id(params);
        let body_data = WebDriverMessage::<U>::decode_body(raw_body, requires_body)?;
        let command = match match_type {
            Route::NewSession => WebDriverCommand::NewSession(serde_json::from_str(raw_body)?),
            Route::DeleteSession => WebDriverCommand::DeleteSession,
            Route::Get => WebDriverCommand::Get(serde_json::from_str(raw_body)?),
            Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
            Route::GoBack => WebDriverCommand::GoBack,
            Route::GoForward => WebDriverCommand::GoForward,
            Route::Refresh => WebDriverCommand::Refresh,
            Route::GetTitle => WebDriverCommand::GetTitle,
            Route::GetPageSource => WebDriverCommand::GetPageSource,
            Route::GetWindowHandle => WebDriverCommand::GetWindowHandle,
            Route::GetWindowHandles => WebDriverCommand::GetWindowHandles,
            Route::NewWindow => WebDriverCommand::NewWindow(serde_json::from_str(raw_body)?),
            Route::CloseWindow => WebDriverCommand::CloseWindow,
            Route::GetTimeouts => WebDriverCommand::GetTimeouts,
            Route::SetTimeouts => WebDriverCommand::SetTimeouts(serde_json::from_str(raw_body)?),
            Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => {
                WebDriverCommand::GetWindowRect
            }
            Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
                WebDriverCommand::SetWindowRect(serde_json::from_str(raw_body)?)
            }
            Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
            Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
            Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
            Route::SwitchToWindow => {
                WebDriverCommand::SwitchToWindow(serde_json::from_str(raw_body)?)
            }
            Route::SwitchToFrame => {
                WebDriverCommand::SwitchToFrame(serde_json::from_str(raw_body)?)
            }
            Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
            Route::FindElement => WebDriverCommand::FindElement(serde_json::from_str(raw_body)?),
            Route::FindElements => WebDriverCommand::FindElements(serde_json::from_str(raw_body)?),
            Route::FindElementElement => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::FindElementElement(element, serde_json::from_str(raw_body)?)
            }
            Route::FindElementElements => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::FindElementElements(element, serde_json::from_str(raw_body)?)
            }
            Route::FindShadowRootElement => {
                let shadow_id = try_opt!(
                    params.get("shadowId"),
                    ErrorStatus::InvalidArgument,
                    "Missing shadowId parameter"
                );
                let shadow_root = ShadowRoot(shadow_id.as_str().into());
                WebDriverCommand::FindShadowRootElement(
                    shadow_root,
                    serde_json::from_str(raw_body)?,
                )
            }
            Route::FindShadowRootElements => {
                let shadow_id = try_opt!(
                    params.get("shadowId"),
                    ErrorStatus::InvalidArgument,
                    "Missing shadowId parameter"
                );
                let shadow_root = ShadowRoot(shadow_id.as_str().into());
                WebDriverCommand::FindShadowRootElements(
                    shadow_root,
                    serde_json::from_str(raw_body)?,
                )
            }
            Route::GetActiveElement => WebDriverCommand::GetActiveElement,
            Route::GetShadowRoot => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetShadowRoot(element)
            }
            Route::GetComputedLabel => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetComputedLabel(element)
            }
            Route::GetComputedRole => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetComputedRole(element)
            }
            Route::IsDisplayed => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::IsDisplayed(element)
            }
            Route::IsSelected => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::IsSelected(element)
            }
            Route::GetElementAttribute => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                let attr = try_opt!(
                    params.get("name"),
                    ErrorStatus::InvalidArgument,
                    "Missing name parameter"
                )
                .as_str();
                WebDriverCommand::GetElementAttribute(element, attr.into())
            }
            Route::GetElementProperty => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                let property = try_opt!(
                    params.get("name"),
                    ErrorStatus::InvalidArgument,
                    "Missing name parameter"
                )
                .as_str();
                WebDriverCommand::GetElementProperty(element, property.into())
            }
            Route::GetCSSValue => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                let property = try_opt!(
                    params.get("propertyName"),
                    ErrorStatus::InvalidArgument,
                    "Missing propertyName parameter"
                )
                .as_str();
                WebDriverCommand::GetCSSValue(element, property.into())
            }
            Route::GetElementText => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetElementText(element)
            }
            Route::GetElementTagName => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetElementTagName(element)
            }
            Route::GetElementRect => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::GetElementRect(element)
            }
            Route::IsEnabled => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::IsEnabled(element)
            }
            Route::ElementClick => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::ElementClick(element)
            }
            Route::ElementClear => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::ElementClear(element)
            }
            Route::ElementSendKeys => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::ElementSendKeys(element, serde_json::from_str(raw_body)?)
            }
            Route::ExecuteScript => {
                WebDriverCommand::ExecuteScript(serde_json::from_str(raw_body)?)
            }
            Route::ExecuteAsyncScript => {
                WebDriverCommand::ExecuteAsyncScript(serde_json::from_str(raw_body)?)
            }
            Route::GetCookies => WebDriverCommand::GetCookies,
            Route::GetNamedCookie => {
                let name = try_opt!(
                    params.get("name"),
                    ErrorStatus::InvalidArgument,
                    "Missing 'name' parameter"
                )
                .as_str()
                .into();
                WebDriverCommand::GetNamedCookie(name)
            }
            Route::AddCookie => WebDriverCommand::AddCookie(serde_json::from_str(raw_body)?),
            Route::DeleteCookies => WebDriverCommand::DeleteCookies,
            Route::DeleteCookie => {
                let name = try_opt!(
                    params.get("name"),
                    ErrorStatus::InvalidArgument,
                    "Missing name parameter"
                )
                .as_str()
                .into();
                WebDriverCommand::DeleteCookie(name)
            }
            Route::PerformActions => {
                WebDriverCommand::PerformActions(serde_json::from_str(raw_body)?)
            }
            Route::ReleaseActions => WebDriverCommand::ReleaseActions,
            Route::DismissAlert => WebDriverCommand::DismissAlert,
            Route::AcceptAlert => WebDriverCommand::AcceptAlert,
            Route::GetAlertText => WebDriverCommand::GetAlertText,
            Route::SendAlertText => {
                WebDriverCommand::SendAlertText(serde_json::from_str(raw_body)?)
            }
            Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
            Route::TakeElementScreenshot => {
                let element_id = try_opt!(
                    params.get("elementId"),
                    ErrorStatus::InvalidArgument,
                    "Missing elementId parameter"
                );
                let element = WebElement(element_id.as_str().into());
                WebDriverCommand::TakeElementScreenshot(element)
            }
            Route::Print => WebDriverCommand::Print(serde_json::from_str(raw_body)?),
            Route::SetPermission => {
                WebDriverCommand::SetPermission(serde_json::from_str(raw_body)?)
            }
            Route::Status => WebDriverCommand::Status,
            Route::Extension(ref extension) => extension.command(params, &body_data)?,
            Route::WebAuthnAddVirtualAuthenticator => {
                WebDriverCommand::WebAuthnAddVirtualAuthenticator(serde_json::from_str(raw_body)?)
            }
            Route::WebAuthnRemoveVirtualAuthenticator => {
                WebDriverCommand::WebAuthnRemoveVirtualAuthenticator
            }
            Route::WebAuthnAddCredential => {
                WebDriverCommand::WebAuthnAddCredential(serde_json::from_str(raw_body)?)
            }
            Route::WebAuthnGetCredentials => WebDriverCommand::WebAuthnGetCredentials,
            Route::WebAuthnRemoveCredential => WebDriverCommand::WebAuthnRemoveCredential,
            Route::WebAuthnRemoveAllCredentials => WebDriverCommand::WebAuthnRemoveAllCredentials,
            Route::WebAuthnSetUserVerified => {
                WebDriverCommand::WebAuthnSetUserVerified(serde_json::from_str(raw_body)?)
            }
        };
        Ok(WebDriverMessage::new(session_id, command))
    }

    fn get_session_id(params: &Parameters) -> Option<String> {
        params.get("sessionId").cloned()
    }

    fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Value> {
        if requires_body {
            match serde_json::from_str(body) {
                Ok(x @ Value::Object(_)) => Ok(x),
                Ok(_) => Err(WebDriverError::new(
                    ErrorStatus::InvalidArgument,
                    "Body was not a JSON Object",
                )),
                Err(e) => {
                    if e.is_io() {
                        Err(WebDriverError::new(
                            ErrorStatus::InvalidArgument,
                            format!("I/O error whilst decoding body: {}", e),
                        ))
                    } else {
                        let msg = format!("Failed to decode request as JSON: {}", body);
                        let stack = format!("Syntax error at :{}:{}", e.line(), e.column());
                        Err(WebDriverError::new_with_stack(
                            ErrorStatus::InvalidArgument,
                            msg,
                            stack,
                        ))
                    }
                }
            }
        } else {
            Ok(Value::Null)
        }
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ActionsParameters {
    pub actions: Vec<ActionSequence>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct AddCookieParameters {
    pub name: String,
    pub value: String,
    pub path: Option<String>,
    pub domain: Option<String>,
    #[serde(default)]
    pub secure: bool,
    #[serde(default)]
    pub httpOnly: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expiry: Option<Date>,
    pub sameSite: Option<String>,
}

impl<'de> Deserialize<'de> for AddCookieParameters {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct Wrapper {
            #[serde(with = "AddCookieParameters")]
            cookie: AddCookieParameters,
        }

        Wrapper::deserialize(deserializer).map(|wrapper| wrapper.cookie)
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct GetParameters {
    pub url: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct GetNamedCookieParameters {
    pub name: Option<String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct JavascriptCommandParameters {
    pub script: String,
    pub args: Option<Vec<Value>>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct LocatorParameters {
    pub using: LocatorStrategy,
    pub value: String,
}

#[derive(Debug, PartialEq, Serialize)]
pub struct NewSessionParameters {
    capabilities: SpecNewSessionParameters,
}

// Manual deserialize implementation to error if capabilities is not an object
// Without this the empty list test fails
impl<'de> Deserialize<'de> for NewSessionParameters {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = serde_json::Value::deserialize(deserializer)?;
        let caps = value
            .get("capabilities")
            .ok_or(de::Error::missing_field("capabilities"))?;
        if !caps.is_object() {
            return Err(de::Error::custom("capabilities must be objects"));
        }
        let capabilities =
            SpecNewSessionParameters::deserialize(caps).map_err(de::Error::custom)?;
        Ok(NewSessionParameters { capabilities })
    }
}

impl CapabilitiesMatching for NewSessionParameters {
    fn match_browser<T: BrowserCapabilities>(
        &self,
        browser_capabilities: &mut T,
    ) -> WebDriverResult<Option<Capabilities>> {
        self.capabilities.match_browser(browser_capabilities)
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct NewWindowParameters {
    #[serde(rename = "type")]
    pub type_hint: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PrintPageRange {
    Integer(u64),
    Range(String),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
pub struct PrintParameters {
    pub orientation: PrintOrientation,
    #[serde(deserialize_with = "deserialize_to_print_scale_f64")]
    pub scale: f64,
    pub background: bool,
    pub page: PrintPage,
    pub margin: PrintMargins,
    pub page_ranges: Vec<PrintPageRange>,
    pub shrink_to_fit: bool,
}

impl Default for PrintParameters {
    fn default() -> Self {
        PrintParameters {
            orientation: PrintOrientation::default(),
            scale: 1.0,
            background: false,
            page: PrintPage::default(),
            margin: PrintMargins::default(),
            page_ranges: Vec::new(),
            shrink_to_fit: true,
        }
    }
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PrintOrientation {
    Landscape,
    #[default]
    Portrait,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct PrintPage {
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub width: f64,
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub height: f64,
}

impl Default for PrintPage {
    fn default() -> Self {
        PrintPage {
            width: 21.59,
            height: 27.94,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct PrintMargins {
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub top: f64,
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub bottom: f64,
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub left: f64,
    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    pub right: f64,
}

impl Default for PrintMargins {
    fn default() -> Self {
        PrintMargins {
            top: 1.0,
            bottom: 1.0,
            left: 1.0,
            right: 1.0,
        }
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SetPermissionParameters {
    pub descriptor: SetPermissionDescriptor,
    pub state: SetPermissionState,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SetPermissionDescriptor {
    pub name: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SetPermissionState {
    Denied,
    Granted,
    Prompt,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum WebAuthnProtocol {
    #[serde(rename = "ctap1/u2f")]
    Ctap1U2f,
    #[serde(rename = "ctap2")]
    Ctap2,
    #[serde(rename = "ctap2_1")]
    Ctap2_1,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum AuthenticatorTransport {
    Usb,
    Nfc,
    Ble,
    SmartCard,
    Hybrid,
    Internal,
}

fn default_as_true() -> bool {
    true
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticatorParameters {
    pub protocol: WebAuthnProtocol,
    pub transport: AuthenticatorTransport,
    #[serde(default)]
    pub has_resident_key: bool,
    #[serde(default)]
    pub has_user_verification: bool,
    #[serde(default = "default_as_true")]
    pub is_user_consenting: bool,
    #[serde(default)]
    pub is_user_verified: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct UserVerificationParameters {
    #[serde(rename = "isUserVerified")]
    pub is_user_verified: bool,
}

fn deserialize_to_positive_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
    D: Deserializer<'de>,
{
    let val = f64::deserialize(deserializer)?;
    if val < 0.0 {
        return Err(de::Error::custom(format!("{} is negative", val)));
    };
    Ok(val)
}

fn deserialize_to_print_scale_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
    D: Deserializer<'de>,
{
    let val = f64::deserialize(deserializer)?;
    if !(0.1..=2.0).contains(&val) {
        return Err(de::Error::custom(format!("{} is outside range 0.1-2", val)));
    };
    Ok(val)
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SendKeysParameters {
    pub text: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SwitchToFrameParameters {
    pub id: FrameId,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SwitchToWindowParameters {
    pub handle: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct TakeScreenshotParameters {
    pub element: Option<WebElement>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct TimeoutsParameters {
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_u64"
    )]
    pub implicit: Option<u64>,
    #[serde(
        default,
        rename = "pageLoad",
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_u64"
    )]
    pub page_load: Option<u64>,
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_nullable_u64"
    )]
    #[allow(clippy::option_option)]
    pub script: Option<Option<u64>>,
}

#[allow(clippy::option_option)]
fn deserialize_to_nullable_u64<'de, D>(deserializer: D) -> Result<Option<Option<u64>>, D::Error>
where
    D: Deserializer<'de>,
{
    let opt: Option<f64> = Option::deserialize(deserializer)?;
    let value = match opt {
        Some(n) => {
            if n < 0.0 || n.fract() != 0.0 {
                return Err(de::Error::custom(format!(
                    "{} is not a positive Integer",
                    n
                )));
            }
            if (n as u64) > MAX_SAFE_INTEGER {
                return Err(de::Error::custom(format!(
                    "{} is greater than maximum safe integer",
                    n
                )));
            }
            Some(Some(n as u64))
        }
        None => Some(None),
    };

    Ok(value)
}

fn deserialize_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
    D: Deserializer<'de>,
{
    let opt: Option<f64> = Option::deserialize(deserializer)?;
    let value = match opt {
        Some(n) => {
            if n < 0.0 || n.fract() != 0.0 {
                return Err(de::Error::custom(format!(
                    "{} is not a positive Integer",
                    n
                )));
            }
            if (n as u64) > MAX_SAFE_INTEGER {
                return Err(de::Error::custom(format!(
                    "{} is greater than maximum safe integer",
                    n
                )));
            }
            Some(n as u64)
        }
        None => return Err(de::Error::custom("null is not a positive integer")),
    };

    Ok(value)
}

/// A top-level browsing context’s window rect is a dictionary of the
/// [`screenX`], [`screenY`], `width`, and `height` attributes of the
/// `WindowProxy`.
///
/// In some user agents the operating system’s window dimensions, including
/// decorations, are provided by the proprietary `window.outerWidth` and
/// `window.outerHeight` DOM properties.
///
/// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
/// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct WindowRectParameters {
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_i32"
    )]
    pub x: Option<i32>,
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_i32"
    )]
    pub y: Option<i32>,
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_positive_i32"
    )]
    pub width: Option<i32>,
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_to_positive_i32"
    )]
    pub height: Option<i32>,
}

fn deserialize_to_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
where
    D: Deserializer<'de>,
{
    let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
    let value = match opt {
        Some(n) => {
            if n < i64::from(i32::min_value()) || n > i64::from(i32::max_value()) {
                return Err(de::Error::custom(format!("'{}' is larger than i32", n)));
            }
            Some(n as i32)
        }
        None => None,
    };

    Ok(value)
}

fn deserialize_to_positive_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
where
    D: Deserializer<'de>,
{
    let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
    let value = match opt {
        Some(n) => {
            if n < 0 || n > i64::from(i32::max_value()) {
                return Err(de::Error::custom(format!("'{}' is outside of i32", n)));
            }
            Some(n as i32)
        }
        None => None,
    };

    Ok(value)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::capabilities::SpecNewSessionParameters;
    use crate::common::ELEMENT_KEY;
    use crate::test::assert_de;
    use serde_json::{self, json};

    #[test]
    fn test_json_actions_parameters_missing_actions_field() {
        assert!(serde_json::from_value::<ActionsParameters>(json!({})).is_err());
    }

    #[test]
    fn test_json_actions_parameters_invalid() {
        assert!(serde_json::from_value::<ActionsParameters>(json!({ "actions": null })).is_err());
    }

    #[test]
    fn test_json_action_parameters_empty_list() {
        assert_de(
            &ActionsParameters { actions: vec![] },
            json!({"actions": []}),
        );
    }

    #[test]
    fn test_json_action_parameters_with_unknown_field() {
        assert_de(
            &ActionsParameters { actions: vec![] },
            json!({"actions": [], "foo": "bar"}),
        );
    }

    #[test]
    fn test_json_add_cookie_parameters_with_values() {
        let json = json!({"cookie": {
            "name": "foo",
            "value": "bar",
            "path": "/",
            "domain": "foo.bar",
            "expiry": 123,
            "secure": true,
            "httpOnly": false,
            "sameSite": "Lax",
        }});
        let cookie = AddCookieParameters {
            name: "foo".into(),
            value: "bar".into(),
            path: Some("/".into()),
            domain: Some("foo.bar".into()),
            expiry: Some(Date(123)),
            secure: true,
            httpOnly: false,
            sameSite: Some("Lax".into()),
        };

        assert_de(&cookie, json);
    }

    #[test]
    fn test_json_add_cookie_parameters_with_optional_null_fields() {
        let json = json!({"cookie": {
            "name": "foo",
            "value": "bar",
            "path": null,
            "domain": null,
            "expiry": null,
            "secure": true,
            "httpOnly": false,
            "sameSite": null,
        }});
        let cookie = AddCookieParameters {
            name: "foo".into(),
            value: "bar".into(),
            path: None,
            domain: None,
            expiry: None,
            secure: true,
            httpOnly: false,
            sameSite: None,
        };

        assert_de(&cookie, json);
    }

    #[test]
    fn test_json_add_cookie_parameters_without_optional_fields() {
        let json = json!({"cookie": {
            "name": "foo",
            "value": "bar",
            "secure": true,
            "httpOnly": false,
        }});
        let cookie = AddCookieParameters {
            name: "foo".into(),
            value: "bar".into(),
            path: None,
            domain: None,
            expiry: None,
            secure: true,
            httpOnly: false,
            sameSite: None,
        };

        assert_de(&cookie, json);
    }

    #[test]
    fn test_json_add_cookie_parameters_with_invalid_cookie_field() {
        assert!(serde_json::from_value::<AddCookieParameters>(json!({"name": "foo"})).is_err());
    }

    #[test]
    fn test_json_add_cookie_parameters_with_unknown_field() {
        let json = json!({"cookie": {
            "name": "foo",
            "value": "bar",
            "secure": true,
            "httpOnly": false,
            "foo": "bar",
        }, "baz": "bah"});
        let cookie = AddCookieParameters {
            name: "foo".into(),
            value: "bar".into(),
            path: None,
            domain: None,
            expiry: None,
            secure: true,
            httpOnly: false,
            sameSite: None,
        };

        assert_de(&cookie, json);
    }

    #[test]
    fn test_json_get_parameters_with_url() {
        assert_de(
            &GetParameters {
                url: "foo.bar".into(),
            },
            json!({"url": "foo.bar"}),
        );
    }

    #[test]
    fn test_json_get_parameters_with_invalid_url_value() {
        assert!(serde_json::from_value::<GetParameters>(json!({"url": 3})).is_err());
    }

    #[test]
    fn test_json_get_parameters_with_invalid_url_field() {
        assert!(serde_json::from_value::<GetParameters>(json!({"foo": "bar"})).is_err());
    }

    #[test]
    fn test_json_get_parameters_with_unknown_field() {
        assert_de(
            &GetParameters {
                url: "foo.bar".into(),
            },
            json!({"url": "foo.bar", "foo": "bar"}),
        );
    }

    #[test]
    fn test_json_get_named_cookie_parameters_with_value() {
        assert_de(
            &GetNamedCookieParameters {
                name: Some("foo".into()),
            },
            json!({"name": "foo"}),
        );
    }

    #[test]
    fn test_json_get_named_cookie_parameters_with_optional_null_field() {
        assert_de(
            &GetNamedCookieParameters { name: None },
            json!({ "name": null }),
        );
    }

    #[test]
    fn test_json_get_named_cookie_parameters_without_optional_null_field() {
        assert_de(&GetNamedCookieParameters { name: None }, json!({}));
    }

    #[test]
    fn test_json_get_named_cookie_parameters_with_invalid_name_field() {
        assert!(serde_json::from_value::<GetNamedCookieParameters>(json!({"name": 3})).is_err());
    }

    #[test]
    fn test_json_get_named_cookie_parameters_with_unknown_field() {
        assert_de(
            &GetNamedCookieParameters {
                name: Some("foo".into()),
            },
            json!({"name": "foo", "foo": "bar"}),
        );
    }

    #[test]
    fn test_json_javascript_command_parameters_with_values() {
        let json = json!({
            "script": "foo",
            "args": ["1", 2],
        });
        let execute_script = JavascriptCommandParameters {
            script: "foo".into(),
            args: Some(vec!["1".into(), 2.into()]),
        };

        assert_de(&execute_script, json);
    }

    #[test]
    fn test_json_javascript_command_parameters_with_optional_null_field() {
        let json = json!({
            "script": "foo",
            "args": null,
        });
        let execute_script = JavascriptCommandParameters {
            script: "foo".into(),
            args: None,
        };

        assert_de(&execute_script, json);
    }

    #[test]
    fn test_json_javascript_command_parameters_without_optional_null_field() {
        let execute_script = JavascriptCommandParameters {
            script: "foo".into(),
            args: None,
        };
        assert_de(&execute_script, json!({"script": "foo"}));
    }

    #[test]
    fn test_json_javascript_command_parameters_invalid_script_field() {
        let json = json!({ "script": null });
        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
    }

    #[test]
    fn test_json_javascript_command_parameters_invalid_args_field() {
        let json = json!({
            "script": null,
            "args": "1",
        });
        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
    }

    #[test]
    fn test_json_javascript_command_parameters_missing_script_field() {
        let json = json!({ "args": null });
        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
    }

    #[test]
    fn test_json_javascript_command_parameters_with_unknown_field() {
        let json = json!({
            "script": "foo",
            "foo": "bar",
        });
        let execute_script = JavascriptCommandParameters {
            script: "foo".into(),
            args: None,
        };

        assert_de(&execute_script, json);
    }

    #[test]
    fn test_json_locator_parameters_with_values() {
        let json = json!({
            "using": "xpath",
            "value": "bar",
        });
        let locator = LocatorParameters {
            using: LocatorStrategy::XPath,
            value: "bar".into(),
        };

        assert_de(&locator, json);
    }

    #[test]
    fn test_json_locator_parameters_invalid_using_field() {
        let json = json!({
            "using": "foo",
            "value": "bar",
        });
        assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
    }

    #[test]
    fn test_json_locator_parameters_invalid_value_field() {
        let json = json!({
            "using": "xpath",
            "value": 3,
        });
        assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
    }

    #[test]
    fn test_json_locator_parameters_missing_using_field() {
        assert!(serde_json::from_value::<LocatorParameters>(json!({"value": "bar"})).is_err());
    }

    #[test]
    fn test_json_locator_parameters_missing_value_field() {
        assert!(serde_json::from_value::<LocatorParameters>(json!({"using": "xpath"})).is_err());
    }

    #[test]
    fn test_json_locator_parameters_with_unknown_field() {
        let json = json!({
            "using": "xpath",
            "value": "bar",
            "foo": "bar",
        });
        let locator = LocatorParameters {
            using: LocatorStrategy::XPath,
            value: "bar".into(),
        };

        assert_de(&locator, json);
    }

    #[test]
    fn test_json_new_session_parameters_spec() {
        let json = json!({"capabilities": {
            "alwaysMatch": {},
            "firstMatch": [{}],
        }});
        let caps = NewSessionParameters {
            capabilities: SpecNewSessionParameters {
                alwaysMatch: Capabilities::new(),
                firstMatch: vec![Capabilities::new()],
            },
        };

        assert_de(&caps, json);
    }

    #[test]
    fn test_json_new_session_parameters_capabilities_null() {
        let json = json!({ "capabilities": null });
        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
    }

    #[test]
    fn test_json_new_session_parameters_capabilities_empty_list() {
        let json = json!({ "capabilities": []});
        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
    }

    #[test]
    fn test_json_new_session_parameters_legacy() {
        let json = json!({
            "desiredCapabilities": {},
            "requiredCapabilities": {},
        });
        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
    }

    #[test]
    fn test_json_new_session_parameters_spec_and_legacy() {
        let json = json!({
            "capabilities": {
                "alwaysMatch": {},
                "firstMatch": [{}],
            },
            "desiredCapabilities": {},
            "requiredCapabilities": {},
        });
        let caps = NewSessionParameters {
            capabilities: SpecNewSessionParameters {
                alwaysMatch: Capabilities::new(),
                firstMatch: vec![Capabilities::new()],
            },
        };

        assert_de(&caps, json);
    }

    #[test]
    fn test_json_new_session_parameters_with_unknown_field() {
        let json = json!({
            "capabilities": {
                "alwaysMatch": {},
                "firstMatch": [{}]
            },
            "foo": "bar",
        });
        let caps = NewSessionParameters {
            capabilities: SpecNewSessionParameters {
                alwaysMatch: Capabilities::new(),
                firstMatch: vec![Capabilities::new()],
            },
        };

        assert_de(&caps, json);
    }

    #[test]
    fn test_json_new_window_parameters_without_type() {
        assert_de(&NewWindowParameters { type_hint: None }, json!({}));
    }

    #[test]
    fn test_json_new_window_parameters_with_optional_null_type() {
        assert_de(
            &NewWindowParameters { type_hint: None },
            json!({ "type": null }),
        );
    }

    #[test]
    fn test_json_new_window_parameters_with_supported_type() {
        assert_de(
            &NewWindowParameters {
                type_hint: Some("tab".into()),
            },
            json!({"type": "tab"}),
        );
    }

    #[test]
    fn test_json_new_window_parameters_with_unknown_type() {
        assert_de(
            &NewWindowParameters {
                type_hint: Some("foo".into()),
            },
            json!({"type": "foo"}),
        );
    }

    #[test]
    fn test_json_new_window_parameters_with_invalid_type() {
        assert!(serde_json::from_value::<NewWindowParameters>(json!({"type": 3})).is_err());
    }

    #[test]
    fn test_json_new_window_parameters_with_unknown_field() {
        let json = json!({
            "type": "tab",
            "foo": "bar",
        });
        let new_window = NewWindowParameters {
            type_hint: Some("tab".into()),
        };

        assert_de(&new_window, json);
    }

    #[test]
    fn test_json_print_defaults() {
        let params = PrintParameters::default();
        assert_de(¶ms, json!({}));
    }

    #[test]
    fn test_json_print() {
        let params = PrintParameters {
            orientation: PrintOrientation::Landscape,
            page: PrintPage {
                width: 10.0,
                ..Default::default()
            },
            margin: PrintMargins {
                top: 10.0,
                ..Default::default()
            },
            scale: 1.5,
            ..Default::default()
        };
        assert_de(
            ¶ms,
            json!({"orientation": "landscape", "page": {"width": 10}, "margin": {"top": 10}, "scale": 1.5}),
        );
    }

    #[test]
    fn test_json_scale_invalid() {
        assert!(serde_json::from_value::<PrintParameters>(json!({"scale": 3})).is_err());
    }

    #[test]
    fn test_json_permission() {
        let params: SetPermissionParameters = SetPermissionParameters {
            descriptor: SetPermissionDescriptor {
                name: "push".into(),
            },
            state: SetPermissionState::Granted,
        };
        assert_de(
            ¶ms,
            json!({"descriptor": {"name": "push"}, "state": "granted"}),
        );
    }

    #[test]
    fn test_json_permission_parameters_invalid() {
        assert!(serde_json::from_value::<SetPermissionParameters>(json!({"test": 3})).is_err());
    }

    #[test]
    fn test_json_permission_descriptor_invalid_type() {
        assert!(serde_json::from_value::<SetPermissionParameters>(
            json!({"descriptor": "test", "state": "granted"})
        )
        .is_err());
    }

    #[test]
    fn test_json_permission_state_invalid_type() {
        assert!(serde_json::from_value::<SetPermissionParameters>(
            json!({"descriptor": {"name": "push"}, "state": 3})
        )
        .is_err());
    }

    #[test]
    fn test_json_permission_state_invalid_value() {
        assert!(serde_json::from_value::<SetPermissionParameters>(
            json!({"descriptor": {"name": "push"}, "state": "invalid"})
        )
        .is_err());
    }

    #[test]
    fn test_json_authenticator() {
        let params = AuthenticatorParameters {
            protocol: WebAuthnProtocol::Ctap1U2f,
            transport: AuthenticatorTransport::Usb,
            has_resident_key: false,
            has_user_verification: false,
            is_user_consenting: false,
            is_user_verified: false,
        };
        assert_de(
            ¶ms,
            json!({"protocol": "ctap1/u2f", "transport": "usb", "hasResidentKey": false, "hasUserVerification": false, "isUserConsenting": false, "isUserVerified": false}),
        );
    }

    #[test]
    fn test_json_credential() {
        use base64::{engine::general_purpose::URL_SAFE, Engine};

        let encoded_string = URL_SAFE.encode(b"hello internet~");
        let params = CredentialParameters {
            credential_id: r"c3VwZXIgcmVhZGVy".to_string(),
            is_resident_credential: true,
            rp_id: "valid.rpid".to_string(),
            private_key: encoded_string.clone(),
            user_handle: encoded_string.clone(),
            sign_count: 0,
        };
        assert_de(
            ¶ms,
            json!({"credentialId": r"c3VwZXIgcmVhZGVy", "isResidentCredential": true, "rpId": "valid.rpid", "privateKey": encoded_string, "userHandle": encoded_string, "signCount": 0}),
        );
    }

    #[test]
    fn test_json_user_verification() {
        let params = UserVerificationParameters {
            is_user_verified: false,
        };
        assert_de(¶ms, json!({"isUserVerified": false}));
    }

    #[test]
    fn test_json_send_keys_parameters_with_value() {
        assert_de(
            &SendKeysParameters { text: "foo".into() },
            json!({"text": "foo"}),
        );
    }

    #[test]
    fn test_json_send_keys_parameters_invalid_text_field() {
        assert!(serde_json::from_value::<SendKeysParameters>(json!({"text": 3})).is_err());
    }

    #[test]
    fn test_json_send_keys_parameters_missing_text_field() {
        assert!(serde_json::from_value::<SendKeysParameters>(json!({})).is_err());
    }

    #[test]
    fn test_json_send_keys_parameters_with_unknown_field() {
        let json = json!({
            "text": "foo",
            "foo": "bar",
        });
        let send_keys = SendKeysParameters { text: "foo".into() };

        assert_de(&send_keys, json);
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_number() {
        assert_de(
            &SwitchToFrameParameters {
                id: FrameId::Short(3),
            },
            json!({"id": 3}),
        );
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_null() {
        assert_de(
            &SwitchToFrameParameters { id: FrameId::Top },
            json!({"id": null}),
        );
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_web_element() {
        assert_de(
            &SwitchToFrameParameters {
                id: FrameId::Element(WebElement("foo".to_string())),
            },
            json!({"id": {"element-6066-11e4-a52e-4f735466cecf": "foo"}}),
        );
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_missing_id() {
        assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({})).is_err())
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_invalid_id_field() {
        assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({"id": "3"})).is_err());
    }

    #[test]
    fn test_json_switch_to_frame_parameters_with_unknown_field() {
        let json = json!({
            "id":3,
            "foo": "bar",
        });
        let switch_to_frame = SwitchToFrameParameters {
            id: FrameId::Short(3),
        };

        assert_de(&switch_to_frame, json);
    }

    #[test]
    fn test_json_switch_to_window_parameters_with_value() {
        assert_de(
            &SwitchToWindowParameters {
                handle: "foo".into(),
            },
            json!({"handle": "foo"}),
        );
    }

    #[test]
    fn test_json_switch_to_window_parameters_invalid_handle_field() {
        assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({"handle": 3})).is_err());
    }

    #[test]
    fn test_json_switch_to_window_parameters_missing_handle_field() {
        assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({})).is_err());
    }

    #[test]
    fn test_json_switch_to_window_parameters_with_unknown_field() {
        let json = json!({
            "handle": "foo",
            "foo": "bar",
        });
        let switch_to_window = SwitchToWindowParameters {
            handle: "foo".into(),
        };

        assert_de(&switch_to_window, json);
    }

    #[test]
    fn test_json_take_screenshot_parameters_with_element() {
        assert_de(
            &TakeScreenshotParameters {
                element: Some(WebElement("elem".into())),
            },
            json!({"element": {ELEMENT_KEY: "elem"}}),
        );
    }

    #[test]
    fn test_json_take_screenshot_parameters_with_optional_null_field() {
        assert_de(
            &TakeScreenshotParameters { element: None },
            json!({ "element": null }),
        );
    }

    #[test]
    fn test_json_take_screenshot_parameters_without_optional_null_field() {
        assert_de(&TakeScreenshotParameters { element: None }, json!({}));
    }

    #[test]
    fn test_json_take_screenshot_parameters_with_invalid_element_field() {
        assert!(
            serde_json::from_value::<TakeScreenshotParameters>(json!({"element": "foo"})).is_err()
        );
    }

    #[test]
    fn test_json_take_screenshot_parameters_with_unknown_field() {
        let json = json!({
            "element": {ELEMENT_KEY: "elem"},
            "foo": "bar",
        });
        let take_screenshot = TakeScreenshotParameters {
            element: Some(WebElement("elem".into())),
        };

        assert_de(&take_screenshot, json);
    }

    #[test]
    fn test_json_timeout_parameters_with_only_null_script_timeout() {
        let timeouts = TimeoutsParameters {
            implicit: None,
            page_load: None,
            script: Some(None),
        };
        assert_de(&timeouts, json!({ "script": null }));
    }

    #[test]
    fn test_json_timeout_parameters_with_only_null_implicit_timeout() {
        assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "implicit": null })).is_err());
    }

    #[test]
    fn test_json_timeout_parameters_with_only_null_pageload_timeout() {
        assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "pageLoad": null })).is_err());
    }

    #[test]
    fn test_json_timeout_parameters_without_optional_null_field() {
        let timeouts = TimeoutsParameters {
            implicit: None,
            page_load: None,
            script: None,
        };
        assert_de(&timeouts, json!({}));
    }

    #[test]
    fn test_json_timeout_parameters_with_unknown_field() {
        let json = json!({
            "script": 60000,
            "foo": "bar",
        });
        let timeouts = TimeoutsParameters {
            implicit: None,
            page_load: None,
            script: Some(Some(60000)),
        };

        assert_de(&timeouts, json);
    }

    #[test]
    fn test_json_window_rect_parameters_with_values() {
        let json = json!({
            "x": 0,
            "y": 1,
            "width": 2,
            "height": 3,
        });
        let rect = WindowRectParameters {
            x: Some(0i32),
            y: Some(1i32),
            width: Some(2i32),
            height: Some(3i32),
        };

        assert_de(&rect, json);
    }

    #[test]
    fn test_json_window_rect_parameters_with_optional_null_fields() {
        let json = json!({
            "x": null,
            "y": null,
            "width": null,
            "height": null,
        });
        let rect = WindowRectParameters {
            x: None,
            y: None,
            width: None,
            height: None,
        };

        assert_de(&rect, json);
    }

    #[test]
    fn test_json_window_rect_parameters_without_optional_fields() {
        let rect = WindowRectParameters {
            x: None,
            y: None,
            width: None,
            height: None,
        };
        assert_de(&rect, json!({}));
    }

    #[test]
    fn test_json_window_rect_parameters_invalid_values_float() {
        let json = json!({
            "x": 1.1,
            "y": 2.2,
            "width": 3.3,
            "height": 4.4,
        });
        let rect = WindowRectParameters {
            x: Some(1),
            y: Some(2),
            width: Some(3),
            height: Some(4),
        };

        assert_de(&rect, json);
    }

    #[test]
    fn test_json_window_rect_parameters_with_unknown_field() {
        let json = json!({
            "x": 1.1,
            "y": 2.2,
            "foo": "bar",
        });
        let rect = WindowRectParameters {
            x: Some(1),
            y: Some(2),
            width: None,
            height: None,
        };

        assert_de(&rect, json);
    }
}

[Dauer der Verarbeitung: 0.35 Sekunden, vorverarbeitet 2026-04-25]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

letze Version der Autor Authoringsoftware

     letze Version des Demonstrationsprogramms Goedel
     letze Version des Bille Abgleichprogramms
     Bilder

Jenseits des Üblichen ....
    

Besucher

Besucher

Monitoring

Montastic status badge