diff options
Diffstat (limited to '')
-rw-r--r-- | testing/geckodriver/src/command.rs | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/testing/geckodriver/src/command.rs b/testing/geckodriver/src/command.rs new file mode 100644 index 0000000000..c92eabf6f3 --- /dev/null +++ b/testing/geckodriver/src/command.rs @@ -0,0 +1,343 @@ +/* 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::logging; +use base64::prelude::BASE64_STANDARD; +use base64::Engine; +use hyper::Method; +use serde::de::{self, Deserialize, Deserializer}; +use serde_json::{self, Value}; +use std::env; +use std::fs::File; +use std::io::prelude::*; +use uuid::Uuid; +use webdriver::command::{WebDriverCommand, WebDriverExtensionCommand}; +use webdriver::error::WebDriverResult; +use webdriver::httpapi::WebDriverExtensionRoute; +use webdriver::Parameters; + +pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> { + vec![ + ( + Method::GET, + "/session/{sessionId}/moz/context", + GeckoExtensionRoute::GetContext, + ), + ( + Method::POST, + "/session/{sessionId}/moz/context", + GeckoExtensionRoute::SetContext, + ), + ( + Method::POST, + "/session/{sessionId}/moz/addon/install", + GeckoExtensionRoute::InstallAddon, + ), + ( + Method::POST, + "/session/{sessionId}/moz/addon/uninstall", + GeckoExtensionRoute::UninstallAddon, + ), + ( + Method::GET, + "/session/{sessionId}/moz/screenshot/full", + GeckoExtensionRoute::TakeFullScreenshot, + ), + ] +} + +#[derive(Clone, PartialEq, Eq)] +pub enum GeckoExtensionRoute { + GetContext, + SetContext, + InstallAddon, + UninstallAddon, + TakeFullScreenshot, +} + +impl WebDriverExtensionRoute for GeckoExtensionRoute { + type Command = GeckoExtensionCommand; + + fn command( + &self, + _params: &Parameters, + body_data: &Value, + ) -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> { + use self::GeckoExtensionRoute::*; + + let command = match *self { + GetContext => GeckoExtensionCommand::GetContext, + SetContext => { + GeckoExtensionCommand::SetContext(serde_json::from_value(body_data.clone())?) + } + InstallAddon => { + GeckoExtensionCommand::InstallAddon(serde_json::from_value(body_data.clone())?) + } + UninstallAddon => { + GeckoExtensionCommand::UninstallAddon(serde_json::from_value(body_data.clone())?) + } + TakeFullScreenshot => GeckoExtensionCommand::TakeFullScreenshot, + }; + + Ok(WebDriverCommand::Extension(command)) + } +} + +#[derive(Clone)] +pub enum GeckoExtensionCommand { + GetContext, + SetContext(GeckoContextParameters), + InstallAddon(AddonInstallParameters), + UninstallAddon(AddonUninstallParameters), + TakeFullScreenshot, +} + +impl WebDriverExtensionCommand for GeckoExtensionCommand { + fn parameters_json(&self) -> Option<Value> { + use self::GeckoExtensionCommand::*; + match self { + GetContext => None, + InstallAddon(x) => Some(serde_json::to_value(x).unwrap()), + SetContext(x) => Some(serde_json::to_value(x).unwrap()), + UninstallAddon(x) => Some(serde_json::to_value(x).unwrap()), + TakeFullScreenshot => None, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +pub struct AddonInstallParameters { + pub path: String, + pub temporary: Option<bool>, +} + +impl<'de> Deserialize<'de> for AddonInstallParameters { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Debug, Deserialize)] + #[serde(deny_unknown_fields)] + struct Base64 { + addon: String, + temporary: Option<bool>, + } + + #[derive(Debug, Deserialize)] + #[serde(deny_unknown_fields)] + struct Path { + path: String, + temporary: Option<bool>, + } + + #[derive(Debug, Deserialize)] + #[serde(untagged)] + enum Helper { + Base64(Base64), + Path(Path), + } + + let params = match Helper::deserialize(deserializer)? { + Helper::Path(ref mut data) => AddonInstallParameters { + path: data.path.clone(), + temporary: data.temporary, + }, + Helper::Base64(ref mut data) => { + let content = BASE64_STANDARD + .decode(&data.addon) + .map_err(de::Error::custom)?; + + let path = env::temp_dir() + .as_path() + .join(format!("addon-{}.xpi", Uuid::new_v4())); + let mut xpi_file = File::create(&path).map_err(de::Error::custom)?; + xpi_file + .write(content.as_slice()) + .map_err(de::Error::custom)?; + + let path = match path.to_str() { + Some(path) => path.to_string(), + None => return Err(de::Error::custom("could not write addon to file")), + }; + + AddonInstallParameters { + path, + temporary: data.temporary, + } + } + }; + + Ok(params) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AddonUninstallParameters { + pub id: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum GeckoContext { + Content, + Chrome, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct GeckoContextParameters { + pub context: GeckoContext, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct XblLocatorParameters { + pub name: String, + pub value: String, +} + +#[derive(Default, Debug, PartialEq, Eq)] +pub struct LogOptions { + pub level: Option<logging::Level>, +} + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::*; + use crate::test::assert_de; + + #[test] + fn test_json_addon_install_parameters_invalid() { + assert!(serde_json::from_str::<AddonInstallParameters>("").is_err()); + assert!(serde_json::from_value::<AddonInstallParameters>(json!(null)).is_err()); + assert!(serde_json::from_value::<AddonInstallParameters>(json!({})).is_err()); + } + + #[test] + fn test_json_addon_install_parameters_with_path_and_temporary() { + let params = AddonInstallParameters { + path: "/path/to.xpi".to_string(), + temporary: Some(true), + }; + assert_de(¶ms, json!({"path": "/path/to.xpi", "temporary": true})); + } + + #[test] + fn test_json_addon_install_parameters_with_path() { + let params = AddonInstallParameters { + path: "/path/to.xpi".to_string(), + temporary: None, + }; + assert_de(¶ms, json!({"path": "/path/to.xpi"})); + } + + #[test] + fn test_json_addon_install_parameters_with_path_invalid_type() { + let json = json!({"path": true, "temporary": true}); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_addon_install_parameters_with_path_and_temporary_invalid_type() { + let json = json!({"path": "/path/to.xpi", "temporary": "foo"}); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_addon_install_parameters_with_addon() { + let json = json!({"addon": "aGVsbG8=", "temporary": true}); + let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap(); + + assert_eq!(data.temporary, Some(true)); + let mut file = File::open(data.path).unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + assert_eq!(contents, "hello"); + } + + #[test] + fn test_json_addon_install_parameters_with_addon_only() { + let json = json!({"addon": "aGVsbG8="}); + let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap(); + + assert_eq!(data.temporary, None); + let mut file = File::open(data.path).unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + assert_eq!(contents, "hello"); + } + + #[test] + fn test_json_addon_install_parameters_with_addon_invalid_type() { + let json = json!({"addon": true, "temporary": true}); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_addon_install_parameters_with_addon_and_temporary_invalid_type() { + let json = json!({"addon": "aGVsbG8=", "temporary": "foo"}); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_install_parameters_with_temporary_only() { + let json = json!({"temporary": true}); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_addon_install_parameters_with_both_path_and_addon() { + let json = json!({ + "path": "/path/to.xpi", + "addon": "aGVsbG8=", + "temporary": true, + }); + assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); + } + + #[test] + fn test_json_addon_uninstall_parameters_invalid() { + assert!(serde_json::from_str::<AddonUninstallParameters>("").is_err()); + assert!(serde_json::from_value::<AddonUninstallParameters>(json!(null)).is_err()); + assert!(serde_json::from_value::<AddonUninstallParameters>(json!({})).is_err()); + } + + #[test] + fn test_json_addon_uninstall_parameters() { + let params = AddonUninstallParameters { + id: "foo".to_string(), + }; + assert_de(¶ms, json!({"id": "foo"})); + } + + #[test] + fn test_json_addon_uninstall_parameters_id_invalid_type() { + let json = json!({"id": true}); + assert!(serde_json::from_value::<AddonUninstallParameters>(json).is_err()); + } + + #[test] + fn test_json_gecko_context_parameters_content() { + let params = GeckoContextParameters { + context: GeckoContext::Content, + }; + assert_de(¶ms, json!({"context": "content"})); + } + + #[test] + fn test_json_gecko_context_parameters_chrome() { + let params = GeckoContextParameters { + context: GeckoContext::Chrome, + }; + assert_de(¶ms, json!({"context": "chrome"})); + } + + #[test] + fn test_json_gecko_context_parameters_context_invalid() { + type P = GeckoContextParameters; + assert!(serde_json::from_value::<P>(json!({})).is_err()); + assert!(serde_json::from_value::<P>(json!({ "context": null })).is_err()); + assert!(serde_json::from_value::<P>(json!({"context": "foo"})).is_err()); + } +} |